From ac2bb3522e8e93efad2a49211f73e57ca85194b5 Mon Sep 17 00:00:00 2001 From: NagendraOpsmx <80510611+NagendraOpsmx@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:17:00 +0530 Subject: [PATCH] Latest Master changes synced 2.11.0 (#22) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: Update signed-release-assets.md (#16755) Missing \ in example Signed-off-by: mfreeman451 * Document restarting argocd after modifying argocd-cm (#12405) Signed-off-by: Lie Ryan Co-authored-by: Blake Pettersson * fix: support specifying username/password for redis holding manifests in argocd-server (#16786) Signed-off-by: Alexander Matyushentsev * fix: add list permission deployments (#16785) * add list permissions for deployments to application controller Signed-off-by: ishitasequeira * revert redis-ha chart changes Signed-off-by: ishitasequeira * revert redis-ha chart changes Signed-off-by: ishitasequeira --------- Signed-off-by: ishitasequeira * chore(manifests): add ClsuterRole/ClusterRoleBinding for applicationset controller. (#16699) Closes https://github.com/argoproj/argo-cd/issues/16698. Signed-off-by: mugioka * Added socks5 proxy support for ssh based git URL, upgraded go-git to 5.10.1 (#15864) Signed-off-by: Anand Francis Joseph * Added Openkruise workload integration health check scripts (#16238) Signed-off-by: Mahesh Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> * fix: allow to run codegen outside GOPATH (#16511) * fix: allow to run codegen outside GOPATH Signed-off-by: Alexandre Gaudreault * clientgen Signed-off-by: Alexandre Gaudreault * openapigen Signed-off-by: Alexandre Gaudreault * remove ensure-gopath Signed-off-by: Alexandre Gaudreault --------- Signed-off-by: Alexandre Gaudreault * fix: Use the cache for sharding (#15237) * feat(sharding): use a cache Signed-off-by: Alexandre Gaudreault * cluster cmd Signed-off-by: Alexandre Gaudreault * - Assign shard 0 to in-cluster cluster and nil check updates - Caching clusters while sharding: Fixing unit tests - Update generated docs - Debug e2e tests - Default the shardNumber to the number of replicas if it is calculated to a higher value - defered Unlock only when a lock is set - Disabling temporarly other versions of k3s to check if e2e passes - Do not fail if hostname format is not abc-n - Fix unit test and skip some e2e - Skip TestGitSubmoduleHTTPSSupport test - Remove breaking defer c.lock.Unlock() - Reverting testing all k3s version - Default sharding fix Signed-off-by: Akram Ben Aissi Signed-off-by: Akram Ben Aissi * fixes related to code review: renaming structure param, moving db initialisation Signed-off-by: Akram Ben Aissi * Code review Signed-off-by: Akram Ben Aissi * Set default shard to 0 Signed-off-by: Akram Ben Aissi * Set different default value for Sts and Deployment mode Signed-off-by: Akram Ben Aissi * Expose ClusterShardingCache Signed-off-by: Akram Ben Aissi * Removing use of argoDB.db for DistributionFunction Signed-off-by: Akram Ben Aissi * Update generated documentation Signed-off-by: Akram Ben Aissi * Fix comment about NoShardingDistributionFunction and NoShardingAlgorithm Signed-off-by: Akram Ben Aissi --------- Signed-off-by: Alexandre Gaudreault Signed-off-by: Akram Ben Aissi Co-authored-by: Alexandre Gaudreault * fix(manifests): applicationset-controller dir is not added to cluster-rbac/kustomization.yaml. (#16810) * fix(manifests): applicationset-controller dir is not added to cluster-rbac/kustomization.yaml. Related PR: https://github.com/argoproj/argo-cd/pull/16699. I missed adding a new folder(applicationset-controller) to kustomization.yaml. So, i addressed it. Signed-off-by: mugioka * chore: exec `make manifests`. Signed-off-by: mugioka * chore: exec `make manifests`. Signed-off-by: mugioka --------- Signed-off-by: mugioka * typo in comment (#16834) Signed-off-by: eddimull * feat: adding option to specify an aws profile to use by the argocd-server when adding a EKS cluster (#16767) useful for argocd-servers which are not running in AWS and want to add multiple EKS clusters using separate keys instead of assuming roles #16766 Signed-off-by: Isaac Gaskin Co-authored-by: Soumya Ghosh Dastidar <44349253+gdsoumya@users.noreply.github.com> * fix: enforce content type header for API requests (#16860) Signed-off-by: Alexander Matyushentsev * adding tests for githandlers (#16678) Signed-off-by: zhaque44 * fix: added logging if repo credentials collide (#16833) Signed-off-by: doxsch <28098153+doxsch@users.noreply.github.com> * fix(cli): add support for Application in any namespace for app wait (argoproj#16812) (#16816) Use fully qualified application names in ApplicationWaitCommand Closes: #16812 Signed-off-by: Sergiy Kulanov * docs: Add LinkedIn badge to README.md (#16889) Signed-off-by: Yuan Tang * update follow-redirects to 1.15.5 (#16899) Signed-off-by: Regina Scott * chore: allow @approvers-docs to approve readme.md (#16897) Signed-off-by: Blake Pettersson * Adding CNCF blog to readme file (#16893) Signed-off-by: Chetan Deshmukh * docs: Update Azure AD to Entra ID (#16869) * Update Azure AD to Entra ID https://learn.microsoft.com/en-us/entra/fundamentals/new-name Signed-off-by: Ryan Flynn * Add formerly known as azuread Signed-off-by: Ryan Flynn --------- Signed-off-by: Ryan Flynn * chore: Preventing runnings jobs when updating documentation (#16706) * Preventing runnings jobs when updating documentation Signed-off-by: Aymen Ben Tanfous * Empty line added to .md file Signed-off-by: Aymen Ben Tanfous --------- Signed-off-by: Aymen Ben Tanfous Co-authored-by: Aymen Ben Tanfous * chore(deps): bump github.com/go-git/go-git/v5 from 5.8.1 to 5.11.0 (#16711) Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.8.1 to 5.11.0. - [Release notes](https://github.com/go-git/go-git/releases) - [Commits](https://github.com/go-git/go-git/compare/v5.8.1...v5.11.0) --- updated-dependencies: - dependency-name: github.com/go-git/go-git/v5 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(cli): add support for Application in any namespace for `app delete` cmd (#16898) Use fully qualified application names when operate with Applications Closes: #16896 Signed-off-by: Sergiy Kulanov * docs: Fixed Slugify doc in GoTemplate.md (#16685) * docs: Fixed Slugify doc in GoTemplate.md Signed-off-by: Aymen Ben Tanfous * Update docs/operator-manual/applicationset/GoTemplate.md Co-authored-by: Blake Pettersson Signed-off-by: Aymen Ben Tanfous * Update docs/operator-manual/applicationset/GoTemplate.md Co-authored-by: Blake Pettersson Signed-off-by: Aymen Ben Tanfous --------- Signed-off-by: Aymen Ben Tanfous Co-authored-by: Blake Pettersson * Initialize & send forceHttpBasicAuth & enableOCI params correctly during repo update from UI (#16794) * feat(health): support for distribution aws.crossplane.io resource (#16827) Signed-off-by: nueavv * fix(ui): set content-type for certain UI requests (#16923) (#16930) Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * feat(controller): add sync jitter(#14241) (#16820) * feat(controller): add sync jitter Signed-off-by: Alexandre Gaudreault * convert to duration for simplicity Signed-off-by: Alexandre Gaudreault * docs Signed-off-by: Alexandre Gaudreault * add config to manifests Signed-off-by: Alexandre Gaudreault * fix tests Signed-off-by: Alexandre Gaudreault --------- Signed-off-by: Alexandre Gaudreault * fix(action): Add validation for Kustomize Build Options white space (#16704) Signed-off-by: Sergey Lanzman * Revert "chore: Preventing runnings jobs when updating documentation (#16706)" (#16943) This reverts commit 65869a3860c7555b3ea7a962db44cc9b05f7e333. Signed-off-by: Blake Pettersson * feat: Add PITS Globale Datenrettungsdienste to user list (#16765) * Add PITS Globale Datenrettungsdienste to user list Signed-off-by: Arnold <87698848+arnoldberlin@users.noreply.github.com> * Update USERS.md Signed-off-by: Arnold <87698848+arnoldberlin@users.noreply.github.com> --------- Signed-off-by: Arnold <87698848+arnoldberlin@users.noreply.github.com> Co-authored-by: Soumya Ghosh Dastidar <44349253+gdsoumya@users.noreply.github.com> * separate application controller roles into a separate manifests directory (#16884) Signed-off-by: ishitasequeira * fix(ui): fix display banner when not explicit set position (#16741) Signed-off-by: linghaoSu * fix(appcontroller): Uptake fix in gitops engine which fixes application sync with auto create ns and server side apply (#16942) * Uptake fix in gitops engine to fix auto create ns with server side apply Signed-off-by: anandf * Moved the new e2e test to different location Signed-off-by: anandf * Fix test name to be less than 63 char for creating ns Signed-off-by: anandf * update gitops-engine with latest master Signed-off-by: Leonardo Luz Almeida --------- Signed-off-by: anandf Signed-off-by: Leonardo Luz Almeida Co-authored-by: Leonardo Luz Almeida * docs: Update argocd-cm.yaml bannerposition description (#16961) (#16962) Signed-off-by: Keith Chong * docs: Added an example of downloading the latest stable version (#16968) * added an example of downloading the latest stable version Signed-off-by: Christian Hernandez * Update docs/cli_installation.md Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> Signed-off-by: Christian Hernandez --------- Signed-off-by: Christian Hernandez Signed-off-by: Christian Hernandez Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> * feat(health): support for resourcerecordsets aws.crossplane.io resource (#16823) Signed-off-by: nueavv * chore(deps): rm go-jose Cxb6dee8d5-b814 high vuln (#16947) Signed-off-by: fengshunli <1171313930@qq.com> * fix(server): allow disabling content-type check (#16959) * fix(server): allow disabling content-type check Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * fix spacing Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --------- Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * Clean up repeated package import (#16987) Signed-off-by: Zechun Chen * docs(helm): add example of public oci chart (#17000) There doesn't appear to be an example of using an OCI helm chart repository, so this adds a simple declarative example. This is a common question from the community. Signed-off-by: Nicholas Morey * Badge for apps in any namespace (#16739) Signed-off-by: sshenoy6 Co-authored-by: sshenoy6 * docs(helm): fix yaml formatting on code block (#17001) Signed-off-by: Nicholas Morey * fix(redis): go-redis v9 regression missing metrics and reconnect hook (#13415) (#15275) * fix(redis): go-redis v9 regression missing metrics and reconnect hook Signed-off-by: phanama * fix: golangci lint return values not checked in tests Signed-off-by: phanama * chore: move dnsError var locally into func Signed-off-by: phanama --------- Signed-off-by: phanama * Fix typo in documentation (#17022) Signed-off-by: saeedhosseini * feat: Prune resources in reverse order of syncwave during sync (#15074) (#16748) * Add e2e & doc for prune order during sync Signed-off-by: Siddhesh Ghadi * Point gitops-engine to fork with reverse prune changes Signed-off-by: Siddhesh Ghadi * Fix ci linting failures Signed-off-by: Siddhesh Ghadi * Update gitops-engine commit ref Signed-off-by: Siddhesh Ghadi --------- Signed-off-by: Siddhesh Ghadi * docs(applicationset): explain impact of empty spec in templatePatch (#17042) * docs: explain impact of empty spec in templatePatch Signed-off-by: Nicholas Morey * fix: not conditional helm values Signed-off-by: Nicholas Morey --------- Signed-off-by: Nicholas Morey * docs(kustomize): add components yaml example (#17043) Signed-off-by: Nicholas Morey * docs(argocd-cm): add timeout.reconciliation.jitter example (#17044) Signed-off-by: Nicholas Morey * docs(hooks): add postdelete to table (#17048) Add `PostDelete` to the hooks table, and clean up wording and old availability statement (all the way back to v1, probably irrelevant). Signed-off-by: Nicholas Morey * Corrected certificate managment for OCI helm charts (#16656) Signed-off-by: Andrew Block Co-authored-by: Soumya Ghosh Dastidar <44349253+gdsoumya@users.noreply.github.com> * chore: use kubernetes 1.29.0 in CI (#17050) Keeping 1.25 for now. Signed-off-by: Shyukri Shyukriev * chore(deps): bump Helm to 3.14.0 (#17031) (#17032) * bump helm to 3.14.0 Signed-off-by: Simon HEGE * Add a note about helm bump in upgrade instructions Signed-off-by: Simon HEGE --------- Signed-off-by: Simon HEGE * chore(deps): bump github.com/evanphx/json-patch (#17021) Bumps [github.com/evanphx/json-patch](https://github.com/evanphx/json-patch) from 5.6.0+incompatible to 5.9.0+incompatible. - [Release notes](https://github.com/evanphx/json-patch/releases) - [Commits](https://github.com/evanphx/json-patch/compare/v5.6.0...v5.9.0) --- updated-dependencies: - dependency-name: github.com/evanphx/json-patch dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): Upgrade aws-sdk-go to support eks pod identity (#17063) * chore: Upgrade aws-sdk-go to support eks pod identity --------- Signed-off-by: Carlos Santana Co-authored-by: Mathieu Bruneau * add cogen for notifications Signed-off-by: Carlos Santana --------- Signed-off-by: Carlos Santana Co-authored-by: Mathieu Bruneau * fix: removed pkce code challange check for WebUI (#16730) Signed-off-by: Patrick Kerwood * chore(ci): bump k3s versions to latest patches (#17060) Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * feat: add prometheus metrics around proxy extension requests (#17012) * feat: add prometheus metrics around proxy extension requests Signed-off-by: Leonardo Luz Almeida * update go.mod Signed-off-by: Leonardo Luz Almeida * fix metrics bugs Signed-off-by: Leonardo Luz Almeida * fix unit-test Signed-off-by: Leonardo Luz Almeida * Add unit suffix in the duration metric Signed-off-by: Leonardo Luz Almeida * update doc Signed-off-by: Leonardo Luz Almeida --------- Signed-off-by: Leonardo Luz Almeida * fix(ci): correct helm checksum path (#17081) Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * fix(controller): fix application controller deployment crashing (#16984) * fix application controller deployment crashing and update manifests Signed-off-by: ishitasequeira * remove environment variable ARGOCD_ENABLE_DYNAMIC_CLUSTER_DISTRIBUTION Signed-off-by: ishitasequeira * fix auto-generated docs Signed-off-by: ishitasequeira --------- Signed-off-by: ishitasequeira * chore: improve error logs (#10592) (#17089) Signed-off-by: Bardia Heydari * DOC: add Fly Security and Telavita in USERS.md (#17076) Signed-off-by: Gustavo Esser * fix(ui): prevent app name too long hide open icon (#16983) * fix(ui): prevent app name too long hide open icon Signed-off-by: linghaoSu * fix(ui): fix app resource list lint Signed-off-by: linghaoSu --------- Signed-off-by: linghaoSu * fix: log all token verification failures (#16625) * fix: log all token verification failures Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * better Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --------- Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * feat: add health-checks for eck elastic beat (#16563) * feat: add health-checks for eck elastic beat Signed-off-by: ebuildy * fix tests Signed-off-by: ebuildy --------- Signed-off-by: ebuildy * docs(proposal): decoupling app sync from control plane user w/ impersonation (#14255) * Proposal for decoupling application sync from control plane user using impersonation Signed-off-by: Anand Francis Joseph * Moved the proposal document to the right directory Signed-off-by: Anand Francis Joseph * Update docs/decouple-application-sync-user-using-impersonation Co-authored-by: Blake Pettersson Signed-off-by: Anand Francis Joseph * Update docs/decouple-application-sync-user-using-impersonation Co-authored-by: Blake Pettersson Signed-off-by: Anand Francis Joseph * Update docs/decouple-application-sync-user-using-impersonation Co-authored-by: Blake Pettersson Signed-off-by: Anand Francis Joseph * Modified the proposal to have control in AppProjects alone instead of Application and AppProject Signed-off-by: Anand Francis Joseph * Removed proposal placed in wrong directory and corrected examples Signed-off-by: Anand Francis Joseph * Update docs/proposals/decouple-application-sync-user-using-impersonation Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Signed-off-by: Anand Francis Joseph * Update docs/proposals/decouple-application-sync-user-using-impersonation Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Signed-off-by: Anand Francis Joseph * Addressed review comments Signed-off-by: Anand Francis Joseph * Additional corrections Signed-off-by: anandf * Fixed alternative proposals section to include only AppProject based approach Signed-off-by: anandf * Added information on impersonation and added related links Signed-off-by: anandf * Added examples for remote cluster destination with the required RBAC access Signed-off-by: anandf * Fixed clusterrole and clusterrolebinding creation commands Signed-off-by: anandf * Addressed review comments from Akram Signed-off-by: anandf * Corrected RBAC to include serviceaccounts that can be impersonated as swell Signed-off-by: anandf * Address few more review comments from Ishita, Akram Signed-off-by: anandf * Fixed a typo and updated the last updated date field Signed-off-by: anandf * Added information of the sync hook behaviour and also corrected the namespace to match that of destination Signed-off-by: Anand Francis Joseph * Changed proposal to meet the latest api design using destinationServiceAccounts Signed-off-by: Anand Francis Joseph * Fixed proposal document to use destinationServiceAccounts struct Signed-off-by: Anand Francis Joseph * Renamed proposal file to have .md extension Signed-off-by: anandf * Using glob pattern instead of regex, and corrected the order of precedence when multiple matches are available Signed-off-by: anandf --------- Signed-off-by: Anand Francis Joseph Signed-off-by: Anand Francis Joseph Signed-off-by: anandf Co-authored-by: Blake Pettersson Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * chore(ci): run ci checks conditionally (#16982) * chore(ci): run ci checks conditionally This should prevent docs changes from having the need to run e2e tests etc, and prevent backend changes from needing to run ui tests, and vice versa. This is similar to previous attempts (see #16706 and #13507), with the difference here that we add the if checks on each _step_ rather than each _job_ - the reason being that most of these jobs are required, and if we skip whole jobs any PR which does this will be left hanging indefinitely, so Github forces us to do this on a step level instead. Signed-off-by: Blake Pettersson * chore(ci): run ci checks conditionally Try conditional jobs, according to https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks Signed-off-by: Blake Pettersson * chore(ci): add composite test-e2e action This is a workaround for the e2e tests which do not run yet report `pending` when they are actually skipped. Signed-off-by: Blake Pettersson --------- Signed-off-by: Blake Pettersson Co-authored-by: Remington Breeze Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> * fix(ui): Change path to "root" when path is root directory (#14949) * change path to display root Signed-off-by: Eshwar Hebbur Shivakumar * Fix inequality typo Signed-off-by: Eshwar Hebbur Shivakumar * Fix lint issues Signed-off-by: Eshwar Hebbur Shivakumar --------- Signed-off-by: Eshwar Hebbur Shivakumar * typo `registires` -> `registries` (#17099) Signed-off-by: Adam Huganir * Updated otelgrpc to remediate CVE found by JFrog Xray (#17084) Signed-off-by: Tal Yitzhak Co-authored-by: Tal Yitzhak Co-authored-by: Blake Pettersson * docs(webhook): use real cm name instead of placeholder (#17002) The document says I should registed configMap named argocd-notifications-cm but then uses placeholder in examples. Signed-off-by: Petr Studeny * docs: Update Okta OIDC SSO docs (#13811) * Update the Okta SSO docs * fill out the OIDC section with step-by-step instructions on using Okta with custom authorization servers * adjust outdated docs about updating the docs Signed-off-by: Jonas Courteau * Add the Okta version that these docs are written against Signed-off-by: Jonas Courteau --------- Signed-off-by: Jonas Courteau Signed-off-by: Dan Garfield Co-authored-by: Dan Garfield * Add a description for using contour httpproxy CRD (#14614) Which allows you to reuse the same hostname. Co-authored-by: Boris Smidt Co-authored-by: pasha-codefresh * fix: ci failures (#17107) Signed-off-by: Soumya Ghosh Dastidar * chore(deps): bump library/golang from 1.21.3 to 1.22.0 in /test/remote (#17111) Bumps library/golang from 1.21.3 to 1.22.0. --- updated-dependencies: - dependency-name: library/golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump yarn from 1.22.10 to 1.22.13 in /ui-test (#17092) Bumps [yarn](https://github.com/yarnpkg/yarn) from 1.22.10 to 1.22.13. - [Release notes](https://github.com/yarnpkg/yarn/releases) - [Changelog](https://github.com/yarnpkg/yarn/blob/master/CHANGELOG.md) - [Commits](https://github.com/yarnpkg/yarn/compare/1.22.10...v1.22.13) --- updated-dependencies: - dependency-name: yarn dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * chore(deps): bump library/redis from 7.0.11 to 7.2.4 in /test/container (#16806) Bumps library/redis from 7.0.11 to 7.2.4. --- updated-dependencies: - dependency-name: library/redis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * chore(deps-dev): bump yarn from 1.22.10 to 1.22.21 in /ui (#17096) Bumps [yarn](https://github.com/yarnpkg/yarn) from 1.22.10 to 1.22.21. - [Release notes](https://github.com/yarnpkg/yarn/releases) - [Changelog](https://github.com/yarnpkg/yarn/blob/master/CHANGELOG.md) - [Commits](https://github.com/yarnpkg/yarn/compare/1.22.10...v1.22.21) --- updated-dependencies: - dependency-name: yarn dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Blake Pettersson Co-authored-by: pasha-codefresh * fix the typo (#17116) * chore(deps): bump library/node from 20.6.1 to 21.6.1 (#17053) Bumps library/node from 20.6.1 to 21.6.1. --- updated-dependencies: - dependency-name: library/node dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * chore(deps): bump library/node from 20.7.0 to 21.6.1 (#17065) Signed-off-by: fengshunli <1171313930@qq.com> Co-authored-by: pasha-codefresh * fix(kustomize): set build dir (#15057) #16229 #16652 (#16653) * use repo root, not app path Signed-off-by: Prune correct patch Signed-off-by: Prune * use Getwd to find the root path for diff commands Signed-off-by: Prune * set dot a default for argo app commands Signed-off-by: Prune * revert default values Signed-off-by: Prune * patch diff in TestNamespacedResourceDiffing Signed-off-by: Prune * patching some diff and sync Signed-off-by: Prune * patch remaining diff in error Signed-off-by: Prune --------- Signed-off-by: Prune * chore(ci): tweak backend filters (#17134) The existing backend filters get triggered even on frontend-only or docs-only changes, which should not be the case. The reason for this seems to be the fact that each filter line is ORed rather than ANDed. To remedy this, we put all the filters on the same line. I tried the filter out in a REPL (https://runkit.com/blakepettersson/65c3daba99653f0008c74eda). This is a filter using picomatch (the same library `dorny/paths-filter` uses). Signed-off-by: Blake Pettersson * docs: fix error in toolchain setup (#17154) Signed-off-by: Alexandre Gaudreault * chore(dex): 2.37.0 to 2.38.0 (#17157) Signed-off-by: asingh51 Co-authored-by: asingh51 * feat: Add support for passing Redis Sentinel username(ACL) and password (#17168) * Add support for passing Sentinel username and password Signed-off-by: ShlomiTubul * fix align with var naming Signed-off-by: ShlomiTubul * fix align with var naming Signed-off-by: ShlomiTubul --------- Signed-off-by: ShlomiTubul Co-authored-by: ShlomiTubul * fix: stop initializing deployment informer if dynamic sharding is disabled (#17097) * fix: stop initializing deployment informer if dynamic sharding is disabled Signed-off-by: Soumya Ghosh Dastidar * feat: updated sharding cache getter func Signed-off-by: Soumya Ghosh Dastidar --------- Signed-off-by: Soumya Ghosh Dastidar * feat: query escape function for notifications (#16343) Signed-off-by: Jan Schumann Co-authored-by: pasha-codefresh * Update triggers doc to fix typo (#17185) Signed-off-by: David Grizzanti * docs: fixes Template.md targetRevision typo (#17190) * Template.md targetRevision typo fixed Signed-off-by: Ajay Chidambaram <105060495+chidambaram27@users.noreply.github.com> * retrigger checks Signed-off-by: Ajay Chidambaram <105060495+chidambaram27@users.noreply.github.com> * sign off Signed-off-by: chidambaram27 Signed-off-by: Ajay Chidambaram <105060495+chidambaram27@users.noreply.github.com> * sign off Signed-off-by: Ajay Chidambaram <105060495+chidambaram27@users.noreply.github.com> --------- Signed-off-by: Ajay Chidambaram <105060495+chidambaram27@users.noreply.github.com> Signed-off-by: chidambaram27 * docs: Private-helm-repo section target added to helm.md (#16697) * helm-repo Signed-off-by: Surajyadav * Update docs/user-guide/helm.md Co-authored-by: Blake Pettersson Signed-off-by: Suraj yadav --------- Signed-off-by: Surajyadav Signed-off-by: Suraj yadav Co-authored-by: Blake Pettersson * fix: infer correct shard in statefulset setup (#17124, #17016) (#17167) * fix: infer correct shard in statefulset setup Signed-off-by: Lukas Wöhrl * fix the case if only a single replica Signed-off-by: Lukas Wöhrl * fix: resolving pointer on shard compare Signed-off-by: Lukas Wöhrl * fix: add readlock for cluster accessor Signed-off-by: Lukas Wöhrl * fix: use defer to protect access of 'shard' Signed-off-by: Lukas Wöhrl * fix: revert locking in getclusteraccessor Signed-off-by: Lukas Wöhrl * fix: handle nil shard case Signed-off-by: Lukas Wöhrl * fix: handle any nil shard value as false Signed-off-by: Lukas Wöhrl * fix: handle nil case and fix another missing pointer dereference Signed-off-by: Lukas Wöhrl * revert Signed-off-by: Lukas Wöhrl * fix: added tests and fixed some behaviour bugs Signed-off-by: Lukas Wöhrl * test: add test to validate that Shard value is not overriden Signed-off-by: Lukas Wöhrl * fix: added tests and fixe the case when server is changed inside a secret Signed-off-by: Lukas Wöhrl * tests: add test cases for infering the shard logic Signed-off-by: Lukas Wöhrl --------- Signed-off-by: Lukas Wöhrl * feat: wait until resources are deleted #6085 (#16733) * feat: wait until resources are deleted Signed-off-by: MichaelMorris * Added unit and e2e test Signed-off-by: MichaelMorris --------- Signed-off-by: MichaelMorris * fix(controller): add missing workqueue metrics (#16315) (#17013) * fix(controller): add missing kubernetes metrics Signed-off-by: Alexandre Gaudreault * validate workqueue metrics are present Signed-off-by: Alexandre Gaudreault * use newer metrics registry Signed-off-by: Alexandre Gaudreault * fix duplicated Signed-off-by: Alexandre Gaudreault * init runtime controller in test to have correct metrics Signed-off-by: Alexandre Gaudreault * fix lint error Signed-off-by: Alexandre Gaudreault * update controller-runtime to remove metrics with high cardinality Signed-off-by: Alexandre Gaudreault --------- Signed-off-by: Alexandre Gaudreault Signed-off-by: Alexandre Gaudreault * chore(deps): upgrade helm to 3.14.1 (#17213) * chore(deps): upgrade helm to 3.14.1 Signed-off-by: Alexandre Gaudreault * move files to folder...... Signed-off-by: Alexandre Gaudreault --------- Signed-off-by: Alexandre Gaudreault * docs: Add PayIt to USERS.md (#17215) * docs: Add PayIt to USERS.md Signed-off-by: Matt Menzenski * docs: Add PayIt to USERS.md Signed-off-by: Matt Menzenski --------- Signed-off-by: Matt Menzenski * docs: Add Nextbasket to USERS.md (#17228) Signed-off-by: NextBasket-Petyo <100193556+NextBasket-Petyo@users.noreply.github.com> * fix: Permission Denied error when calling GetAppDetails API (#17221) (#17229) Signed-off-by: Keith Chong * Count git checkout failures (#15657) Signed-off-by: Mikołaj Przybysz <1093404+mikolajprzybysz@users.noreply.github.com> Co-authored-by: Mikołaj Przybysz <1093404+mikolajprzybysz@users.noreply.github.com> Co-authored-by: Blake Pettersson * chore(deps): bump library/node from 21.6.1 to 21.6.2 in /ui-test (#17226) Bumps library/node from 21.6.1 to 21.6.2. --- updated-dependencies: - dependency-name: library/node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * chore(deps): bump library/node from 21.6.1 to 21.6.2 (#17223) Bumps library/node from 21.6.1 to 21.6.2. --- updated-dependencies: - dependency-name: library/node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * chore(deps): bump library/golang in /test/remote (#17138) Bumps library/golang from `094e47e` to `ef61a20`. --- updated-dependencies: - dependency-name: library/golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * feat(grafana-dashboard): Update example dashboard, add AppSet Telemetry (#17232) Signed-off-by: lukepatrick * chore(deps): bump library/node from `6fb1883` to `65998e3` in /ui-test (#17245) Bumps library/node from `6fb1883` to `65998e3`. --- updated-dependencies: - dependency-name: library/node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump library/golang in /test/remote (#17244) Bumps library/golang from `ef61a20` to `7b297d9`. --- updated-dependencies: - dependency-name: library/golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add `AppName` to the RepoServerAppDetailsQuery for notification-controller (#17233) Signed-off-by: Dong Wang Co-authored-by: pasha-codefresh * chore(deps): bump library/node from `50703e6` to `65998e3` (#17243) Bumps library/node from `50703e6` to `65998e3`. --- updated-dependencies: - dependency-name: library/node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * docs: Update USERS.md (#17248) * Update USERS.md Signed-off-by: itayvolo <72027444+itayvolo@users.noreply.github.com> * Update USERS.md Signed-off-by: itayvolo <72027444+itayvolo@users.noreply.github.com> * Update USERS.md Signed-off-by: itayvolo <72027444+itayvolo@users.noreply.github.com> --------- Signed-off-by: itayvolo <72027444+itayvolo@users.noreply.github.com> Co-authored-by: pasha-codefresh * docs(metrics): add release label to haproxy (#17264) Add missing `release` label to `argocd-redis-haproxy-metrics` ServiceMonitor example. Signed-off-by: Gaston Festari * docs: Fix typo in notifications example (#17250) * Fix typo in notifications example I'm not too sure what the example should look like, so I'm taking a guess here. Signed-off-by: Adrian Moisey * Update docs/operator-manual/notifications/troubleshooting.md Co-authored-by: Soumya Ghosh Dastidar <44349253+gdsoumya@users.noreply.github.com> Signed-off-by: Adrian Moisey --------- Signed-off-by: Adrian Moisey Co-authored-by: Soumya Ghosh Dastidar <44349253+gdsoumya@users.noreply.github.com> * Update contributors-quickstart.md (#17266) Signed-off-by: Dan Garfield * reduce unnecessary unmarshal (#17187) Signed-off-by: Wilson Wang * fix typo (#17272) Signed-off-by: Oscar Wieman * docs: Added examples for alternate EKS cluster authentication methods (#17270) * Added examples for alternate EKS cluster authentication methods Signed-off-by: Damon Edstrom * Update docs/operator-manual/declarative-setup.md Signed-off-by: Dan Garfield Signed-off-by: Dan Garfield --------- Signed-off-by: Dan Garfield Co-authored-by: Dan Garfield * docs for PR #9791 (#16021) Signed-off-by: mzain * chore(ci): use changed files action (#17180) `dorny/paths-filter` doesn't seem to handle (multiple) negations well. Therefore, this PR switches to `tj-actions/changed-files`, since it is already successfully used in argo-workflows. Signed-off-by: Blake Pettersson * fix(ui): Include application name in status badge (#17126) * Added application name to badge Signed-off-by: sshenoy6 * Rever svg change Signed-off-by: sshenoy6 * Doc for disabling application name Signed-off-by: sshenoy6 * Flag to not display application name Signed-off-by: sshenoy6 * Added tests Signed-off-by: sshenoy6 * Make no app name the default Signed-off-by: sshenoy6 * Have enable app name as a query parameter Signed-off-by: sshenoy6 * Have enable app name as a query parameter Signed-off-by: sshenoy6 * argocd to original Signed-off-by: sshenoy6 * Update docs/user-guide/status-badge.md Signed-off-by: Dan Garfield Signed-off-by: Dan Garfield --------- Signed-off-by: sshenoy6 Signed-off-by: Dan Garfield Co-authored-by: sshenoy6 Co-authored-by: Dan Garfield * fix: Update test image ssl/crypto libs (#17303) Signed-off-by: Carlos Santana * chore(deps-dev): bump postcss from 8.2.13 to 8.4.35 in /ui (#17140) Bumps [postcss](https://github.com/postcss/postcss) from 8.2.13 to 8.4.35. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.2.13...8.4.35) --- updated-dependencies: - dependency-name: postcss dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * chore(deps): bump library/redis in /test/container (#17137) Bumps library/redis from `cc8b0b8` to `11c3e41`. --- updated-dependencies: - dependency-name: library/redis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * chore(deps): bump react-helmet and @types/react-helmet in /ui (#11556) Bumps [react-helmet](https://github.com/nfl/react-helmet) and [@types/react-helmet](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-helmet). These dependencies needed to be updated together. Updates `react-helmet` from 5.2.1 to 6.1.0 - [Release notes](https://github.com/nfl/react-helmet/releases) - [Changelog](https://github.com/nfl/react-helmet/blob/master/CHANGELOG.md) - [Commits](https://github.com/nfl/react-helmet/commits/6.1.0) Updates `@types/react-helmet` from 5.0.19 to 6.1.6 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-helmet) --- updated-dependencies: - dependency-name: react-helmet dependency-type: direct:production update-type: version-update:semver-major - dependency-name: "@types/react-helmet" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * chore(deps): bump bitnami/kubectl in /test/container (#14220) Bumps bitnami/kubectl from `670fe3f` to `14ab746`. --- updated-dependencies: - dependency-name: bitnami/kubectl dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * chore: add Rocket.Chat to users (#17306) Signed-off-by: Debdut Chakraborty * chore(deps): bump library/ubuntu in /test/container (#13409) Bumps library/ubuntu from `9a0bdde` to `67211c1`. --- updated-dependencies: - dependency-name: library/ubuntu dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump library/redis in /test/container (#17309) Bumps library/redis from `11c3e41` to `e647cfe`. --- updated-dependencies: - dependency-name: library/redis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump library/registry in /test/container (#13050) Bumps library/registry from `41f413c` to `b209a41`. --- updated-dependencies: - dependency-name: library/registry dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * chore(deps): bump library/busybox in /test/e2e/multiarch-container (#14592) Bumps library/busybox from `2376a0c` to `3fbc632`. --- updated-dependencies: - dependency-name: library/busybox dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * fix: use simple python image to build mkdocs (#17313) * fix: use simple python image to build mkdocs Signed-off-by: Carlos Santana * use python 3.7 Signed-off-by: Carlos Santana --------- Signed-off-by: Carlos Santana * chore(deps): bump library/registry in /test/container (#17317) Bumps library/registry from `b209a41` to `f4e1b87`. --- updated-dependencies: - dependency-name: library/registry dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs: fixed name of environment variable and config map property enabling scm providers for applicationsets (#17326) Signed-off-by: Juliusz Co-authored-by: Juliusz * fix(ui): Added dashed line between collapsed sections in Compact-diff (#17173) * dashed-line-breaker Signed-off-by: Surajyadav * dark-mode Signed-off-by: Surajyadav * dark-mode-text-fix Signed-off-by: Surajyadav --------- Signed-off-by: Surajyadav * fix: multi-source app breaks application parameters UI (#16910) (#17033) Signed-off-by: Keith Chong * chore(deps): upgrade helm to 3.14.2 (#17330) * chore(deps): upgrade helm to 3.14.2 Signed-off-by: David Bunn * Signing commit Signed-off-by: David Bunn --------- Signed-off-by: David Bunn * chore(deps): bump library/node from 21.6.1 to 21.6.2 in /test/container (#17316) Bumps library/node from 21.6.1 to 21.6.2. --- updated-dependencies: - dependency-name: library/node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * fix: The argocd server api-content-type flag does not allow empty content-type header (#17331) Signed-off-by: Alexander Matyushentsev * fix(ui): The tiles in Applications List are too wide #17220 (#17340) * fix(ui): The tiles in Applications List are too wide Signed-off-by: Rafal Pelczar * change min width of app tiles Signed-off-by: Rafal Pelczar --------- Signed-off-by: Rafal Pelczar * chore(notifications): remove unneeded operations from templates (#17307) * chore(deps): bump library/ubuntu in /test/container (#13409) Bumps library/ubuntu from `9a0bdde` to `67211c1`. --- updated-dependencies: - dependency-name: library/ubuntu dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: Michael Firestone * rm unneeded operations from templates Signed-off-by: Michael Firestone Signed-off-by: Michael Firestone * rm more unneeded ops Signed-off-by: Michael Firestone --------- Signed-off-by: dependabot[bot] Signed-off-by: Michael Firestone Signed-off-by: Michael Firestone Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Firestone * feat: allow webhook settings to be referenced by external secret (#16262) Signed-off-by: Arthur Outhenin-Chalandre * feat: Add app data to sharding cache to allow sharding by apps (#17014) * Adding app list to sharding cache Signed-off-by: Andrew Lee * Add shard by apps test Signed-off-by: Andrew Lee * Fix lint Signed-off-by: Andrew Lee * Add coverage to test Signed-off-by: Andrew Lee * Fix lint Signed-off-by: Andrew Lee * Converted cluster/app accesors to private, add apps-in-any-namespace suport in shardingcache init, added read lock to GetAppDistribution Signed-off-by: Andrew Lee * Fix tests Signed-off-by: Andrew Lee --------- Signed-off-by: Andrew Lee * docs: Update USERS.md (#17371) Hi, I added IABAI as using officially ArgoCD Signed-off-by: Andrea Sannuto * feat: add cli commands to add/delete sourceNamespaces from AppProject (#17337) * Add cli to add/delete sourceNamespaces Signed-off-by: Raghavi Shirur * update command/comments Signed-off-by: Raghavi Shirur * update command/comments(1) Signed-off-by: Raghavi Shirur * update user-guide docs Signed-off-by: Raghavi Shirur * Retrigger CI pipeline Signed-off-by: Raghavi Shirur * add check for '*' & rename command to remove-source-namespace Signed-off-by: Raghavi Shirur * update command/comments(2) Signed-off-by: Raghavi Shirur * update command/comments(3) Signed-off-by: Raghavi Shirur * Retrigger CI pipeline Signed-off-by: Raghavi Shirur --------- Signed-off-by: Raghavi Shirur * feat: add ability to auto label clusters from k8s clusterinfo (#17289) * feat: add ability to auto label clusters This gives the ability to automatically label cluster secrets on a cluster-by-cluster basis. If `enableClusterInfoLabels` is set on a cluster secret, the controller will (eventually) label the cluster secret with the current k8s version detected by the cluster info. This needs documentation, e2e tests, as well as CLI/UI additions. Signed-off-by: Blake Pettersson * refactor: use labels instead of secret data This is easier to work with, especially in the context where we need this feature. Signed-off-by: Blake Pettersson * docs: add description on how to use dynamic labeling Signed-off-by: Blake Pettersson --------- Signed-off-by: Blake Pettersson * chore(ci): free up disk space for goreleaser (#17373) Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * fix(ui): Dark-mode-enhancements (#17241) * dark-mode-enhancements Signed-off-by: Surajyadav * monaco Signed-off-by: Surajyadav --------- Signed-off-by: Surajyadav Signed-off-by: Suraj yadav * feat: add cli commands to add/remove sources for multi-source applications (#17310) * Initial commit Signed-off-by: ishitasequeira * add cli commands to add/remove sources for multi-source app Signed-off-by: ishitasequeira * add checks Signed-off-by: ishitasequeira * add docs Signed-off-by: ishitasequeira * refactor code and update tests Signed-off-by: ishitasequeira * add removed additional switch case Signed-off-by: ishitasequeira * fix suggested nits Signed-off-by: ishitasequeira --------- Signed-off-by: ishitasequeira * feat: enable users to run commands related to Argo Applications in any namespace (#17360) * enable --app-namespace falg for application get command Signed-off-by: Mangaal * enable --app-namespace falg for application diff command Signed-off-by: Mangaal * enable --app-namespace falg for application wait command Signed-off-by: Mangaal * enable --app-namespace falg for application rollback command Signed-off-by: Mangaal * enable --app-namespace falg for application patch command Signed-off-by: Mangaal * enable --app-namespace falg for application edit command Signed-off-by: Mangaal * enable --app-namespace falg for application history command Signed-off-by: Mangaal * enable --app-namespace falg for application sync command Signed-off-by: Mangaal * enable --app-namespace falg for application delete command Signed-off-by: Mangaal * cli doc generated Signed-off-by: Mangaal --------- Signed-off-by: Mangaal Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> * fix: preserve escape codes in repo url of git webhook payload (#17376) Signed-off-by: Eric Bissonnette * fix: disable rate limiting sompletely by default (#17355) Signed-off-by: Soumya Ghosh Dastidar * docs: use service-name instead of service-id (#17389) * use service-name instead of service-id Signed-off-by: DongHo Jung * trigger CICD Signed-off-by: DongHo Jung --------- Signed-off-by: DongHo Jung * feat: Allow Kustomize common labels to not apply to selectors (#17329) * modify crds Signed-off-by: Collin Signed-off-by: lets-call-n-walk * cmd opts and test Signed-off-by: Collin Signed-off-by: lets-call-n-walk * kustomize build and test Signed-off-by: Collin Signed-off-by: lets-call-n-walk * fix option order and add ancestry to users Signed-off-by: lets-call-n-walk * fix users format Signed-off-by: lets-call-n-walk * generated files Signed-off-by: lets-call-n-walk * set flag value Signed-off-by: lets-call-n-walk * modify crds Signed-off-by: Collin Signed-off-by: lets-call-n-walk * chore(deps): bump library/registry in /test/container (#17317) Bumps library/registry from `b209a41` to `f4e1b87`. --- updated-dependencies: - dependency-name: library/registry dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: Collin Signed-off-by: lets-call-n-walk * generated files Signed-off-by: lets-call-n-walk * add docs Signed-off-by: lets-call-n-walk * fix doc Signed-off-by: lets-call-n-walk * remove debug prints Signed-off-by: lets-call-n-walk * fix autogen docs Signed-off-by: lets-call-n-walk --------- Signed-off-by: Collin Signed-off-by: lets-call-n-walk Signed-off-by: dependabot[bot] Signed-off-by: Collin Walker <10523817+lets-call-n-walk@users.noreply.github.com> Co-authored-by: Collin Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(ui): Highlight failing containers in the UI (#17143) * failing container icon added Signed-off-by: Surajyadav * lint-fix Signed-off-by: Surajyadav * .. Signed-off-by: Surajyadav * tried yarn lint-fix Signed-off-by: Surajyadav * margin Signed-off-by: Surajyadav * running Signed-off-by: Surajyadav --------- Signed-off-by: Surajyadav * fix(ui): application-summary-help-icon fix (#17385) * Update application-summary.tsx revision-history icon fix Signed-off-by: Suraj yadav * icons Signed-off-by: Surajyadav --------- Signed-off-by: Suraj yadav Signed-off-by: Surajyadav * chore: add more unit tests around useDiffCache function (#17404) * chore: add more unit tests around useDiffCache function Signed-off-by: Leonardo Luz Almeida * fix doc Signed-off-by: Leonardo Luz Almeida --------- Signed-off-by: Leonardo Luz Almeida * fix(grpcproxy): parse headers contain colon (#13274) (#14294) * fix(grpcproxy): parse headers contain colon Signed-off-by: bogay * test(apiclient): headers in wrong format Signed-off-by: bogay --------- Signed-off-by: bogay Co-authored-by: Blake Pettersson * docs: Fix some awkward phrasing in `core.md` (#17412) Signed-off-by: Nate Douglas * docs (aks cluster): update workloadidentity documentation (#17401) Signed-off-by: duncan485 * docs: remove repetitive words (#17430) Signed-off-by: hishope * feat(cli): enable --app-namespace flag for argocd app subcommand (#17437) * add --app-namespace flag for set/unset command Signed-off-by: Mangaal * add --app-namespace flag for add-source/remove-source command Signed-off-by: Mangaal * fix bug, handle array out of-bound when --source-index=len(source) Signed-off-by: Mangaal * add documentation Signed-off-by: Mangaal --------- Signed-off-by: Mangaal * docs: Clarify for `valueFiles` behaviour with `path` field (#17431) This commit adds clarification by explaining that `valueFiles` must be a relative path to the root of sources, even if the `path` field is set. Signed-off-by: Takahiro Suzuki Signed-off-by: tkasuz * docs: Small edits to `docs/user-guide/application-set.md` (#17434) * Update application-set.md Signed-off-by: Nate Douglas * Make example code consistent with examples elsewhere. Signed-off-by: Nathan Douglas --------- Signed-off-by: Nate Douglas Signed-off-by: Nathan Douglas * fix: add retry condition with kube-apiserver sent GOAWAY (#17422) Signed-off-by: penglongli * fix(ui): align resource nodegroup (#17427) Signed-off-by: Caio Paiva * docs: proposal to implement sync timeout and termination settings (#16630) Signed-off-by: Alexander Matyushentsev * feat: Allow mkdocs to livereload when using docker (#17383) * feat: Allow mkdocs to livereload when using docker Signed-off-by: Carlos Santana * update the docs Signed-off-by: Carlos Santana --------- Signed-off-by: Carlos Santana * chore(deps): bump github.com/go-jose/go-jose/v3 from 3.0.1 to 3.0.3 (#17442) Bumps [github.com/go-jose/go-jose/v3](https://github.com/go-jose/go-jose) from 3.0.1 to 3.0.3. - [Release notes](https://github.com/go-jose/go-jose/releases) - [Changelog](https://github.com/go-jose/go-jose/blob/v3.0.3/CHANGELOG.md) - [Commits](https://github.com/go-jose/go-jose/compare/v3.0.1...v3.0.3) --- updated-dependencies: - dependency-name: github.com/go-jose/go-jose/v3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: Fix Helm Installation Breaking on Mac (#17426) * fix helm installation to work with mac Signed-off-by: lets-call-n-walk * fix checksums Signed-off-by: lets-call-n-walk * change install filename and makefile Signed-off-by: lets-call-n-walk * change name to just helm - fix dockerfile Signed-off-by: lets-call-n-walk --------- Signed-off-by: lets-call-n-walk * docs: re-worded updated a link in release-process-and-cadance.md (#17438) Signed-off-by: AlbinB97 * Fixed `project` parameter docs for Gitlab pull request generator (#17429) Signed-off-by: Nguyen Thai * chore: update cosign and version (#17441) * chore: update cosign and version Signed-off-by: Justin Marquis * fix typo Signed-off-by: Justin Marquis --------- Signed-off-by: Justin Marquis * chore: update slsa3 generate (#17451) Signed-off-by: Justin Marquis * docs: dex google oidc: add note on dex connector type (#17453) Signed-off-by: Aiman Ismail * docs: Update site.md (#17454) erros -> errors Signed-off-by: Ikko Eltociear Ashimine * add Oncourse Home (#17457) * chore(deps): bump library/busybox in /test/e2e/multiarch-container (#17445) Bumps library/busybox from `3fbc632` to `650fd57`. --- updated-dependencies: - dependency-name: library/busybox dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump library/node from 21.6.2 to 21.7.0 in /ui-test (#17444) Bumps library/node from 21.6.2 to 21.7.0. --- updated-dependencies: - dependency-name: library/node dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * make CGO_ENABLED configurable (#17462) Signed-off-by: Mark McCormick * chore(deps): bump library/ubuntu in /test/container (#17414) Bumps library/ubuntu from `f9d633f` to `77906da`. --- updated-dependencies: - dependency-name: library/ubuntu dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: remove repetitive words (#17464) Signed-off-by: avoidalone Co-authored-by: pasha-codefresh * feat: add option `manual` to the `--sync-policy` flag of the `app create` command (#17459) * feat: add sync policy option 'manual' for app create command Signed-off-by: Anirudh Sudhir * chore: Update tests to reflect sync policy option changes Signed-off-by: Anirudh Sudhir --------- Signed-off-by: Anirudh Sudhir * [Bot] docs: Update Snyk reports (#17458) Signed-off-by: CI Co-authored-by: CI * docs: fix kustomize example resources url (#17468) Signed-off-by: Jonas Bakken * chore(deps): bump library/node from 21.6.2 to 21.7.1 in /test/container (#17475) Bumps library/node from 21.6.2 to 21.7.1. --- updated-dependencies: - dependency-name: library/node dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump library/golang in /test/container (#17415) Bumps library/golang from 1.21.3 to 1.22.1. --- updated-dependencies: - dependency-name: library/golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(notifications): Helm.GetParameterValueByName should take helm.parametes first (#17472) * fix: Helm.GetParameterValueByName should take helm.parametes first Signed-off-by: pashakostohrys * fix linters Signed-off-by: pashakostohrys --------- Signed-off-by: pashakostohrys * fix: registry argument to be only the host instead full URL (#17381) Signed-off-by: Pablo Aguilar * chore: Fix containerized toolchain (#17480) Signed-off-by: jannfis * chore(deps): bump docker/build-push-action from 5.1.0 to 5.2.0 (#17463) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.1.0 to 5.2.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/4a13e500e55cf31b7a5d59a38ab2040ab0f42f56...af5a7ed5ba88268d5278f7203fb52cd833f66d6e) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * docs: remove `configManagementPlugins` from argocd-cm.yaml (#17486) That field is no longer supported. Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * chore: Support running and testing locally using podman instead of docker (#17481) * chore: Support rootless podman for run/test Signed-off-by: jannfis * Note DOCKER env in docs Signed-off-by: jannfis --------- Signed-off-by: jannfis Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> * fix(appset): keep reconciling even when params error occurred (#17062) * fix(appset): keep reconcile even when params error occurred Signed-off-by: Or Koren * requeue on generator rendering error Signed-off-by: Or Koren * test ignoring partial rendering errors Signed-off-by: Or Koren * e2e test create app with param error Signed-off-by: Or Koren --------- Signed-off-by: Or Koren Co-authored-by: Blake Pettersson * docs(EKS): Fix ArgoCD management role AssumeRole policy for IRSA (#17455) Signed-off-by: Xavier Krantz * chore(deps): bump library/node from 21.7.0 to 21.7.1 in /ui-test (#17498) Bumps library/node from 21.7.0 to 21.7.1. --- updated-dependencies: - dependency-name: library/node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump library/golang in /test/container (#17495) Bumps library/golang from `34ce21a` to `0b55ab8`. --- updated-dependencies: - dependency-name: library/golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump library/redis in /test/container (#17496) Bumps library/redis from `e647cfe` to `7dd7070`. --- updated-dependencies: - dependency-name: library/redis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * chore(deps): bump library/node in /test/container (#17494) Bumps library/node from `f358dfc` to `b9ccc4a`. --- updated-dependencies: - dependency-name: library/node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * Make evergreen (#17507) This example uses an old revision `v1.0.1` instead it should just use stable. Signed-off-by: Dan Garfield * docs: added all available fields for applicationset.yaml #16095 (#16104) * adding all available filds for generators Signed-off-by: Harshvir Potpose * add remaining fields in applicationset.yml Signed-off-by: Harshvir Potpose * Update docs/operator-manual/applicationset.yaml Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Signed-off-by: Harshvir Potpose <122517264+akagami-harsh@users.noreply.github.com> * Update docs/operator-manual/applicationset.yaml Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Signed-off-by: Harshvir Potpose <122517264+akagami-harsh@users.noreply.github.com> * Update docs/operator-manual/applicationset.yaml Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Signed-off-by: Harshvir Potpose <122517264+akagami-harsh@users.noreply.github.com> * Update docs/operator-manual/applicationset.yaml Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Signed-off-by: Harshvir Potpose <122517264+akagami-harsh@users.noreply.github.com> * fix Signed-off-by: Harshvir Potpose --------- Signed-off-by: Harshvir Potpose Signed-off-by: Harshvir Potpose <122517264+akagami-harsh@users.noreply.github.com> Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * docs: Add note in installation step 1 with argocd cli (#8030) * Add note in installation stesps Added notes in installation step 1: kubectl config set-context --current --namespace=argocd * Updated doc * Update docs/getting_started.md Signed-off-by: Dan Garfield --------- Signed-off-by: Dan Garfield Co-authored-by: pasha-codefresh Co-authored-by: Dan Garfield * Add documentation for google transitive groups (#9487) Signed-off-by: Trung Co-authored-by: Michael Crenshaw Co-authored-by: pasha-codefresh * fix: elements should be optional (#17424) A bug was reported, where an applicationset with an empty elements array, when created with `argocd appset create .yaml` gets a `...list.elements: Required value` error. My hypothesis is that when calling the K8s API, golang JSON marshalling mangles the empty `elements` array to `nil`, rather than creating an empty array when submitting the `POST`. Still need to figure out why the same setup seemingly works fine when the same appset is in an app-of-apps. Signed-off-by: Blake Pettersson * Merge pull request from GHSA-jwv5-8mqv-g387 * fix: Validate external URLs for applicatins Signed-off-by: Ry0taK <49341894+Ry0taK@users.noreply.github.com> * fix(ui): remove invalid external-link Signed-off-by: Alexandre Gaudreault * linting Signed-off-by: Alexandre Gaudreault --------- Signed-off-by: Ry0taK <49341894+Ry0taK@users.noreply.github.com> Signed-off-by: Alexandre Gaudreault Co-authored-by: Alexandre Gaudreault * Merge pull request from GHSA-g623-jcgg-mhmm Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * feat: Add support to enable FullTimeStamp in logging (#15127) * Add support to enable FullTimeStamp in logging Signed-off-by: skalinov * fix: Fix go linter file exist issue Signed-off-by: skalinov * fix: Remove --skip-pkg-cache Signed-off-by: skalinov * Update util/log/logrus_test.go Use custom set env for prevent linter to be failed Signed-off-by: pasha-codefresh * Update common/common.go Signed-off-by: Dan Garfield * Update util/log/logrus_test.go Signed-off-by: pasha-codefresh * Update util/log/logrus_test.go remove os import Signed-off-by: pasha-codefresh * Update util/log/logrus_test.go sort dependencies Signed-off-by: pasha-codefresh * fix formatting Signed-off-by: pashakostohrys --------- Signed-off-by: pasha-codefresh Signed-off-by: Dan Garfield Signed-off-by: pashakostohrys Co-authored-by: skalinov Co-authored-by: pasha-codefresh Co-authored-by: Dan Garfield * docs: add attestation of SBOM to release assets in signed-release-assets.md (#17520) Closes https://github.com/argoproj/argo-cd/issues/17200. Signed-off-by: Andreas Hunkeler * fix: Argo CD unnecessary enforce sequential helm manifest generation for one chart (#17518) * fix: Argo CD unnecessary enforce sequential helm manifest generation for one chart Signed-off-by: Alexander Matyushentsev * Update docs/operator-manual/high_availability.md Signed-off-by: Dan Garfield --------- Signed-off-by: Alexander Matyushentsev Signed-off-by: Dan Garfield Co-authored-by: Dan Garfield * chore(deps): bump actions/checkout (#17493) Bumps [actions/checkout](https://github.com/actions/checkout) from 3df4ab11eba7bda6032a0b82a6bb43b11571feac to 8410ad0602e1e429cee44a835ae9f77f654a6694. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/3df4ab11eba7bda6032a0b82a6bb43b11571feac...8410ad0602e1e429cee44a835ae9f77f654a6694) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: Add error messages to return statements in BlockingDial function (#17521) * Add error messages to return statements in BlockingDial function Signed-off-by: danqixu * amend error wrapping from %s to %w Signed-off-by: danqixu --------- Signed-off-by: danqixu * chore(deps): upgrade helm to 3.14.3 (#17531) * chore(deps): upgrade helm to 3.14.3 Signed-off-by: David Bunn * chore(deps): upgrade helm to 3.14.3 Signed-off-by: David Bunn --------- Signed-off-by: David Bunn * chore(deps): bump docker/build-push-action from 5.2.0 to 5.3.0 (#17537) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.2.0 to 5.3.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/af5a7ed5ba88268d5278f7203fb52cd833f66d6e...2cdde995de11925a030ce8070c3d77a52ffcf1c0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(ui): add confirmation box in resource summary delete action (#17485) Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> * docs: Update the status of the feature, app in any namespace, from beta to stable (#17529) * Update the status of the feature, app in any namespace, from beta to stable Signed-off-by: Mangaal * adding warning and removing **Current feature state** Signed-off-by: Mangaal --------- Signed-off-by: Mangaal * Merge pull request from GHSA-2vgg-9h6w-m454 * feat: pick random user and exclude admin user and current user from deletion candidates Signed-off-by: pashakostohrys * feat: increase default max cache size Signed-off-by: pashakostohrys * add nil protection Signed-off-by: pashakostohrys * Update util/session/sessionmanager.go Signed-off-by: Dan Garfield Signed-off-by: Dan Garfield * chore: fix linter issues Signed-off-by: pashakostohrys --------- Signed-off-by: pashakostohrys Signed-off-by: Dan Garfield Co-authored-by: Dan Garfield * Merge pull request from GHSA-6v85-wr92-q4p7 * fix: Fix concurrency issue in session manager Signed-off-by: jannfis * Add note that modification to the map must be done in a thread safe manner * chore: fix linter issues Signed-off-by: pashakostohrys --------- Signed-off-by: jannfis Signed-off-by: pashakostohrys Co-authored-by: Dan Garfield Co-authored-by: pashakostohrys * chore(deps-dev): bump postcss from 8.4.35 to 8.4.36 in /ui (#17555) Bumps [postcss](https://github.com/postcss/postcss) from 8.4.35 to 8.4.36. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.4.35...8.4.36) --- updated-dependencies: - dependency-name: postcss dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump library/registry in /test/container (#17554) Bumps library/registry from `f4e1b87` to `fb9c9ae`. --- updated-dependencies: - dependency-name: library/registry dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs: mention that the argocd diff command does not include secrets (#15950) * doc(cli): update argocd_app_diff Adding the fact that secrets are ignored by the diff (not really stated in the doc so far) Signed-off-by: ario0 <118843430+ario0@users.noreply.github.com> * doc: remove whitespace Signed-off-by: ario0 <118843430+ario0@users.noreply.github.com> --------- Signed-off-by: ario0 <118843430+ario0@users.noreply.github.com> Signed-off-by: Alexis Renard Co-authored-by: pasha-codefresh Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> * feat: add cli commands to create/set/unset/edit sources for multi-source application (#17425) * "feat:add cli commands to create/set/unset/edit sources for multi-source app" Signed-off-by: ishitasequeira * fixed the ci failure Signed-off-by: ishitasequeira * update commands Signed-off-by: ishitasequeira * error out if source-index not specified for multi-source applications Signed-off-by: ishitasequeira * fixed the ci failure Signed-off-by: ishitasequeira * fix tests Signed-off-by: ishitasequeira * set 0 as default source index for app create Signed-off-by: ishitasequeira * add index to ParameterOverrides function Signed-off-by: ishitasequeira * do not allow overrides for applications with multiple sources Signed-off-by: ishitasequeira * update tests Signed-off-by: ishitasequeira * remove create with override example Signed-off-by: ishitasequeira * address comments Signed-off-by: ishitasequeira * update tests Signed-off-by: ishitasequeira * update examples in docs Signed-off-by: ishitasequeira * update logs Signed-off-by: ishitasequeira * Add test and update docs Signed-off-by: ishitasequeira --------- Signed-off-by: ishitasequeira * feat(UI): Added link for docs to add clusters (#17395) * cluster-add-link Signed-off-by: Surajyadav * docs Signed-off-by: Surajyadav * docs-panel Signed-off-by: Surajyadav * added Signed-off-by: Surajyadav * fixed Signed-off-by: Surajyadav --------- Signed-off-by: Surajyadav * chore(deps-dev): bump postcss from 8.4.36 to 8.4.37 in /ui (#17573) Bumps [postcss](https://github.com/postcss/postcss) from 8.4.36 to 8.4.37. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.4.36...8.4.37) --- updated-dependencies: - dependency-name: postcss dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: Fix linter issue with session manager (#17597) Signed-off-by: jannfis * chore(deps): bump slsa-framework/slsa-github-generator (#17593) Bumps [slsa-framework/slsa-github-generator](https://github.com/slsa-framework/slsa-github-generator) from 1.9.0 to 1.10.0. - [Release notes](https://github.com/slsa-framework/slsa-github-generator/releases) - [Changelog](https://github.com/slsa-framework/slsa-github-generator/blob/main/CHANGELOG.md) - [Commits](https://github.com/slsa-framework/slsa-github-generator/compare/v1.9.0...v1.10.0) --- updated-dependencies: - dependency-name: slsa-framework/slsa-github-generator dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump webpack-dev-middleware from 5.3.1 to 5.3.4 in /ui (#17598) Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.1 to 5.3.4. - [Release notes](https://github.com/webpack/webpack-dev-middleware/releases) - [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.1...v5.3.4) --- updated-dependencies: - dependency-name: webpack-dev-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump postcss from 8.4.37 to 8.4.38 in /ui (#17582) Bumps [postcss](https://github.com/postcss/postcss) from 8.4.37 to 8.4.38. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.4.37...8.4.38) --- updated-dependencies: - dependency-name: postcss dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump follow-redirects from 1.15.5 to 1.15.6 in /ui (#17542) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.5 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.5...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump docker/login-action from 2.2.0 to 3.1.0 (#17524) Bumps [docker/login-action](https://github.com/docker/login-action) from 2.2.0 to 3.1.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/465a07811f14bebb1938fbed4728c6a1ff8901fc...e92390c5fb421da1463c202d546fed0ec5c39f20) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump ip from 1.1.5 to 1.1.9 in /ui (#17256) Bumps [ip](https://github.com/indutny/node-ip) from 1.1.5 to 1.1.9. - [Commits](https://github.com/indutny/node-ip/compare/v1.1.5...v1.1.9) --- updated-dependencies: - dependency-name: ip dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pasha-codefresh * fix: invalid badge validation (#15507) (#17580) * fix: invalid badge validation Signed-off-by: Alexandre Gaudreault * use util methods Signed-off-by: Alexandre Gaudreault * rfc accept both lower and upper Signed-off-by: Alexandre Gaudreault * fix unit test affecting each other with var modification Signed-off-by: Alexandre Gaudreault --------- Signed-off-by: Alexandre Gaudreault Co-authored-by: Jann Fischer * Adds count of resource on resource tile (#14904) Signed-off-by: Vipin M S Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> * fix(repo-server): excess git requests, add shared cache lock on revisions (Issue #14725) (#17109) * fix(repo-server): excess git requests, cache lock on revisions Signed-off-by: nromriell * fix: pr feedback, simplify, add configurable variable Signed-off-by: nromriell * fix: codegen, lint Signed-off-by: nromriell * fix: test print, no opts set, var type nit Signed-off-by: nromriell * chore: add additional logging for unexpected cache error Signed-off-by: nromriell --------- Signed-off-by: nromriell Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> * feat: Update command argocd app history to support multiple sources (#17530) * update argocd app history command to print app history group by thier sources along with all the REVISIONS Signed-off-by: Mangaal * upadte unit test to ahve both Source and Sources and update function to overlooked source if sources is persent Signed-off-by: Mangaal * remove magic no 7 and introduc a variable MAX_ALLOWED_REVISIONS Signed-off-by: Mangaal * remove extra unit test Signed-off-by: Mangaal * remove extra unit test TestPrintApplicationHistoryTableForWhenBothSourcesAndSourceFiledsExist() Signed-off-by: Mangaal --------- Signed-off-by: Mangaal Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> * fix: flaky test - app history command not printing source in consistent order (#17615) * instead if ranging over the map varHistory to print the history, inroduced a string array varHistoryKeys Signed-off-by: Mangaal * update unit test expectation, remove new line in the beginning Signed-off-by: Mangaal --------- Signed-off-by: Mangaal * chore: Bump Golang to 1.21.8 (#17616) Signed-off-by: jannfis * chore: update and fix scorecard (#17617) Signed-off-by: Justin Marquis * fix: Appcontroller respects sync windows (#16492) * fix: Appcontroller keeps op running when denied by sync window Signed-off-by: Charles Coupal-Jetté * fix: Update test name Signed-off-by: Charles Coupal-Jetté --------- Signed-off-by: Charles Coupal-Jetté Co-authored-by: Blake Pettersson * docs(goTemplate): Fix bullet list (#17611) Signed-off-by: Wout Scheepers * fix(cmp): pass env to plugin discovery (#13947) Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Co-authored-by: Blake Pettersson Co-authored-by: Jann Fischer * wrap error for SyncKeyRingFromDirectory (#17633) Signed-off-by: danqixu * docs(user-guide): fix a typo (#17642) Signed-off-by: treble-snake * [Bot] docs: Update Snyk reports (#17601) Signed-off-by: CI Co-authored-by: CI * chore(deps): bump follow-redirects from 1.15.5 to 1.15.6 in /ui-test (#17541) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.5 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.5...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update USERS.md (#17651) Add arturia as users Signed-off-by: olivier beyler * chore(deps): bump express from 4.17.3 to 4.19.2 in /ui (#17648) Bumps [express](https://github.com/expressjs/express) from 4.17.3 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.17.3...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge pull request from GHSA-jhwx-mhww-rgc3 * sec: limit helm index max size Signed-off-by: pashakostohrys * sec: limit helm index max size Signed-off-by: pashakostohrys * feat: fix tests and linter Signed-off-by: pashakostohrys --------- Signed-off-by: pashakostohrys * docs: fix contrib meeting time description (#17655) Signed-off-by: Leonardo Luz Almeida * docs: 2 link fixes + hint (#17657) * Update security.md fix RBAC link Signed-off-by: Deniz Erdogan <91744937+deer-wmde@users.noreply.github.com> * Update security.md Signed-off-by: Deniz Erdogan <91744937+deer-wmde@users.noreply.github.com> * Update security.md fix link to application-controller role Signed-off-by: Deniz Erdogan <91744937+deer-wmde@users.noreply.github.com> * Update security.md Signed-off-by: Deniz Erdogan <91744937+deer-wmde@users.noreply.github.com> --------- Signed-off-by: Deniz Erdogan <91744937+deer-wmde@users.noreply.github.com> * docs: added warning for multiple sources (#17670) * docs: added warning for multiple sources Signed-off-by: Kostis (Codefresh) <39800303+kostis-codefresh@users.noreply.github.com> * docs: minor spelling Signed-off-by: Kostis (Codefresh) <39800303+kostis-codefresh@users.noreply.github.com> --------- Signed-off-by: Kostis (Codefresh) <39800303+kostis-codefresh@users.noreply.github.com> * feat: Enhance ArgoCD CLI: Dynamic Repo Server Retrieval with --core and --refresh Flags (#17613) * add const key value for ComponentRepoServer Signed-off-by: Mangaal * update NewRepoServerClient() to look for service with ComponentRepoServer labels , if the label exist construct label selector PortForward Signed-off-by: Mangaal * add comment for the new constants Signed-off-by: Mangaal * instead of passing nil which leads to nil ptr referance error, pass empty ClusterSharding{} Signed-off-by: Mangaal * check for operator install repo server name Signed-off-by: Mangaal * handle empty nil ptr dereference error Signed-off-by: Mangaal * handle nil prt dereference Signed-off-by: Mangaal * typo correction Signed-off-by: Mangaal * run clidocsgen Signed-off-by: Mangaal --------- Signed-off-by: Mangaal * feat(ui): metadata.annotations: too long message Improved (#17452) * metadata.annotations: too long Signed-off-by: Surajyadav * added as a default case Signed-off-by: Surajyadav --------- Signed-off-by: Surajyadav * chore: Update USERS.md (#17683) Add Shield.com as one of the users in the USER.md file Signed-off-by: suhas-chikkanna <162577490+suhas-chikkanna@users.noreply.github.com> --------- Signed-off-by: mfreeman451 Signed-off-by: Lie Ryan Signed-off-by: Alexander Matyushentsev Signed-off-by: ishitasequeira Signed-off-by: mugioka Signed-off-by: Anand Francis Joseph Signed-off-by: Mahesh Signed-off-by: Alexandre Gaudreault Signed-off-by: Akram Ben Aissi Signed-off-by: eddimull Signed-off-by: Isaac Gaskin Signed-off-by: zhaque44 Signed-off-by: doxsch <28098153+doxsch@users.noreply.github.com> Signed-off-by: Sergiy Kulanov Signed-off-by: Yuan Tang Signed-off-by: Regina Scott Signed-off-by: Blake Pettersson Signed-off-by: Chetan Deshmukh Signed-off-by: Ryan Flynn Signed-off-by: Aymen Ben Tanfous Signed-off-by: dependabot[bot] Signed-off-by: Aymen Ben Tanfous Signed-off-by: nueavv Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Signed-off-by: Sergey Lanzman Signed-off-by: Arnold <87698848+arnoldberlin@users.noreply.github.com> Signed-off-by: linghaoSu Signed-off-by: anandf Signed-off-by: Leonardo Luz Almeida Signed-off-by: Keith Chong Signed-off-by: Christian Hernandez Signed-off-by: Christian Hernandez Signed-off-by: fengshunli <1171313930@qq.com> Signed-off-by: Zechun Chen Signed-off-by: Nicholas Morey Signed-off-by: sshenoy6 Signed-off-by: phanama Signed-off-by: saeedhosseini Signed-off-by: Siddhesh Ghadi Signed-off-by: Andrew Block Signed-off-by: Shyukri Shyukriev Signed-off-by: Simon HEGE Signed-off-by: Carlos Santana Signed-off-by: Patrick Kerwood Signed-off-by: Bardia Heydari Signed-off-by: Gustavo Esser Signed-off-by: ebuildy Signed-off-by: Anand Francis Joseph Signed-off-by: Eshwar Hebbur Shivakumar Signed-off-by: Adam Huganir Signed-off-by: Tal Yitzhak Signed-off-by: Petr Studeny Signed-off-by: Jonas Courteau Signed-off-by: Dan Garfield Signed-off-by: Soumya Ghosh Dastidar Signed-off-by: Prune Signed-off-by: Alexandre Gaudreault Signed-off-by: asingh51 Signed-off-by: ShlomiTubul Signed-off-by: Jan Schumann Signed-off-by: David Grizzanti Signed-off-by: Ajay Chidambaram <105060495+chidambaram27@users.noreply.github.com> Signed-off-by: chidambaram27 Signed-off-by: Surajyadav Signed-off-by: Suraj yadav Signed-off-by: Lukas Wöhrl Signed-off-by: MichaelMorris Signed-off-by: Matt Menzenski Signed-off-by: NextBasket-Petyo <100193556+NextBasket-Petyo@users.noreply.github.com> Signed-off-by: Mikołaj Przybysz <1093404+mikolajprzybysz@users.noreply.github.com> Signed-off-by: lukepatrick Signed-off-by: Dong Wang Signed-off-by: itayvolo <72027444+itayvolo@users.noreply.github.com> Signed-off-by: Gaston Festari Signed-off-by: Adrian Moisey Signed-off-by: Wilson Wang Signed-off-by: Oscar Wieman Signed-off-by: mzain Signed-off-by: Carlos Santana Signed-off-by: Debdut Chakraborty Signed-off-by: Juliusz Signed-off-by: David Bunn Signed-off-by: Rafal Pelczar Signed-off-by: Michael Firestone Signed-off-by: Michael Firestone Signed-off-by: Arthur Outhenin-Chalandre Signed-off-by: Andrew Lee Signed-off-by: Andrea Sannuto Signed-off-by: Raghavi Shirur Signed-off-by: Mangaal Signed-off-by: Eric Bissonnette Signed-off-by: DongHo Jung Signed-off-by: Collin Signed-off-by: lets-call-n-walk Signed-off-by: Collin Walker <10523817+lets-call-n-walk@users.noreply.github.com> Signed-off-by: bogay Signed-off-by: Nate Douglas Signed-off-by: duncan485 Signed-off-by: hishope Signed-off-by: Takahiro Suzuki Signed-off-by: tkasuz Signed-off-by: Nathan Douglas Signed-off-by: penglongli Signed-off-by: Caio Paiva Signed-off-by: AlbinB97 Signed-off-by: Nguyen Thai Signed-off-by: Justin Marquis Signed-off-by: Aiman Ismail Signed-off-by: Ikko Eltociear Ashimine Signed-off-by: Mark McCormick Signed-off-by: avoidalone Signed-off-by: Anirudh Sudhir Signed-off-by: CI Signed-off-by: Jonas Bakken Signed-off-by: pashakostohrys Signed-off-by: Pablo Aguilar Signed-off-by: jannfis Signed-off-by: Or Koren Signed-off-by: Xavier Krantz Signed-off-by: Harshvir Potpose Signed-off-by: Harshvir Potpose <122517264+akagami-harsh@users.noreply.github.com> Signed-off-by: Trung Signed-off-by: Ry0taK <49341894+Ry0taK@users.noreply.github.com> Signed-off-by: pasha-codefresh Signed-off-by: Andreas Hunkeler Signed-off-by: danqixu Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> Signed-off-by: ario0 <118843430+ario0@users.noreply.github.com> Signed-off-by: Alexis Renard Signed-off-by: Vipin M S Signed-off-by: nromriell Signed-off-by: Charles Coupal-Jetté Signed-off-by: Wout Scheepers Signed-off-by: treble-snake Signed-off-by: olivier beyler Signed-off-by: Leonardo Luz Almeida Signed-off-by: Deniz Erdogan <91744937+deer-wmde@users.noreply.github.com> Signed-off-by: Kostis (Codefresh) <39800303+kostis-codefresh@users.noreply.github.com> Signed-off-by: suhas-chikkanna <162577490+suhas-chikkanna@users.noreply.github.com> Co-authored-by: mfreeman451 Co-authored-by: Lie Ryan Co-authored-by: Blake Pettersson Co-authored-by: Alexander Matyushentsev Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> Co-authored-by: mugi <62197019+mugioka@users.noreply.github.com> Co-authored-by: Anand Francis Joseph Co-authored-by: Mahesh Kasbe <60398112+maheshkasabe@users.noreply.github.com> Co-authored-by: Alexandre Gaudreault Co-authored-by: Akram Ben Aissi Co-authored-by: eddimull Co-authored-by: Isaac Gaskin Co-authored-by: Soumya Ghosh Dastidar <44349253+gdsoumya@users.noreply.github.com> Co-authored-by: Zubair Haque Co-authored-by: doxsch <28098153+doxsch@users.noreply.github.com> Co-authored-by: Sergiy Kulanov Co-authored-by: Yuan Tang Co-authored-by: Regina Scott <50851526+reginapizza@users.noreply.github.com> Co-authored-by: Chetan Deshmukh <60215917+chetanpdeshmukh@users.noreply.github.com> Co-authored-by: Ryan Flynn Co-authored-by: Aymen Ben Tanfous Co-authored-by: Aymen Ben Tanfous Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sergiy Kulanov Co-authored-by: Siddhesh Ghadi <61187612+svghadi@users.noreply.github.com> Co-authored-by: 1102 <90682513+nueavv@users.noreply.github.com> Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Co-authored-by: Sergey Lanzman Co-authored-by: Arnold <87698848+arnoldberlin@users.noreply.github.com> Co-authored-by: Linghao Su Co-authored-by: Leonardo Luz Almeida Co-authored-by: Keith Chong Co-authored-by: Christian Hernandez Co-authored-by: fsl <1171313930@qq.com> Co-authored-by: Fish-pro Co-authored-by: Nicholas Morey Co-authored-by: Sonam <49382298+sonamkshenoy@users.noreply.github.com> Co-authored-by: sshenoy6 Co-authored-by: Yudi A Phanama <11147376+phanama@users.noreply.github.com> Co-authored-by: saeedhosseini Co-authored-by: Andrew Block Co-authored-by: Shyukri Shyukriev Co-authored-by: Simon HEGE Co-authored-by: Carlos Santana Co-authored-by: Mathieu Bruneau Co-authored-by: Kerwood Co-authored-by: Leonardo Luz Almeida Co-authored-by: Bardia Heydari Co-authored-by: Gustavo Esser Co-authored-by: Linghao Su Co-authored-by: Thomas Decaux Co-authored-by: Remington Breeze Co-authored-by: Eshwar Hebbur Shivakumar Co-authored-by: Adam Huganir Co-authored-by: Tal Yitzhak Co-authored-by: Tal Yitzhak Co-authored-by: Petr Studeny Co-authored-by: jcourteau Co-authored-by: Dan Garfield Co-authored-by: borisssmidtCET <134265736+borisssmidtCET@users.noreply.github.com> Co-authored-by: Boris Smidt Co-authored-by: pasha-codefresh Co-authored-by: Sorav Kumar Sharma Co-authored-by: Prune Sebastien THOMAS Co-authored-by: Alexandre Gaudreault Co-authored-by: AS <11219262+ashutosh16@users.noreply.github.com> Co-authored-by: asingh51 Co-authored-by: shlomi tubul <33376277+shlomitubul@users.noreply.github.com> Co-authored-by: ShlomiTubul Co-authored-by: Jan Schumann Co-authored-by: David Grizzanti Co-authored-by: Ajay Chidambaram <105060495+chidambaram27@users.noreply.github.com> Co-authored-by: Suraj yadav Co-authored-by: Lukas Wöhrl Co-authored-by: Michael Morris <105736419+MichaelMorrisEst@users.noreply.github.com> Co-authored-by: Matt Menzenski Co-authored-by: NextBasket-Petyo <100193556+NextBasket-Petyo@users.noreply.github.com> Co-authored-by: Mikołaj Przybysz Co-authored-by: Mikołaj Przybysz <1093404+mikolajprzybysz@users.noreply.github.com> Co-authored-by: Luke Co-authored-by: Dong Wang Co-authored-by: itayvolo <72027444+itayvolo@users.noreply.github.com> Co-authored-by: Gaston Festari Co-authored-by: Adrian Moisey Co-authored-by: Wilson Wang <3913185+wilsonwang371@users.noreply.github.com> Co-authored-by: Oscar Wieman Co-authored-by: Damon Edstrom <43018444+dcedstrom@users.noreply.github.com> Co-authored-by: Muhammad Zain ul abidin Co-authored-by: Carlos Santana Co-authored-by: Debdut Chakraborty Co-authored-by: Juliusz Jaksa <161451850+juliuszjaksa@users.noreply.github.com> Co-authored-by: Juliusz Co-authored-by: David Bunn Co-authored-by: Rafal Co-authored-by: Michael Firestone Co-authored-by: Michael Firestone Co-authored-by: Arthur Outhenin-Chalandre Co-authored-by: Enclavet Co-authored-by: Andrea Sannuto Co-authored-by: Raghavi Co-authored-by: Mangaal <44372157+Mangaal@users.noreply.github.com> Co-authored-by: Eric Bissonnette Co-authored-by: DongHo Jung Co-authored-by: Collin Walker <10523817+lets-call-n-walk@users.noreply.github.com> Co-authored-by: Collin Co-authored-by: Bogay Co-authored-by: Nate Douglas Co-authored-by: Duncan <62943186+duncan485@users.noreply.github.com> Co-authored-by: John <153272819+hishope@users.noreply.github.com> Co-authored-by: Takahiro Suzuki <63289889+tkasuz@users.noreply.github.com> Co-authored-by: Pelen Co-authored-by: Caio Paiva Co-authored-by: Albin Björk <91016401+AlbinB97@users.noreply.github.com> Co-authored-by: Nguyen Thai <39090621+tk-nguyen@users.noreply.github.com> Co-authored-by: Justin Marquis <76892343+34fathombelow@users.noreply.github.com> Co-authored-by: Aiman Ismail Co-authored-by: Ikko Eltociear Ashimine Co-authored-by: Joe Wingard Co-authored-by: mamccorm Co-authored-by: avoidalone <151622490+avoidalone@users.noreply.github.com> Co-authored-by: Anirudh Sudhir Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: CI Co-authored-by: Jonas Bakken Co-authored-by: Pablo Aguilar Co-authored-by: jannfis Co-authored-by: similark <85114352+similark@users.noreply.github.com> Co-authored-by: Xavier Krantz Co-authored-by: Harshvir Potpose <122517264+akagami-harsh@users.noreply.github.com> Co-authored-by: Mario Adrián Domínguez González de Eiris Co-authored-by: Hoang Quoc Trung Co-authored-by: Michael Crenshaw Co-authored-by: RyotaK <49341894+Ry0taK@users.noreply.github.com> Co-authored-by: Savely Kalinov <111243561+SavelyKalinov@users.noreply.github.com> Co-authored-by: skalinov Co-authored-by: Andreas Hunkeler Co-authored-by: danqixu <156804971+danqixu@users.noreply.github.com> Co-authored-by: David Bunn Co-authored-by: ario0 <118843430+ario0@users.noreply.github.com> Co-authored-by: Vipin M S <40431065+vipinachar@users.noreply.github.com> Co-authored-by: Nathan Romriell Co-authored-by: Charles Coupal-Jetté <83649150+ccjette-logmein@users.noreply.github.com> Co-authored-by: Wout Scheepers Co-authored-by: treble-snake Co-authored-by: olivier beyler Co-authored-by: Deniz Erdogan <91744937+deer-wmde@users.noreply.github.com> Co-authored-by: Kostis (Codefresh) <39800303+kostis-codefresh@users.noreply.github.com> Co-authored-by: suhas-chikkanna <162577490+suhas-chikkanna@users.noreply.github.com> --- .dockerignore | 16 + .github/ISSUE_TEMPLATE/release.md | 6 - .github/cherry-pick-bot.yml | 3 + .github/dependabot.yml | 25 + .github/pr-title-checker-config.json | 15 + .github/pull_request_template.md | 13 +- .github/workflows/README.md | 38 + .github/workflows/ci-build.yaml | 207 +- .github/workflows/codeql.yml | 10 +- .github/workflows/image-reuse.yaml | 171 + .github/workflows/image.yaml | 142 +- .github/workflows/init-release.yaml | 77 + .github/workflows/pr-title-check.yml | 32 +- .github/workflows/release.yaml | 492 +- .github/workflows/scorecard.yaml | 67 + .github/workflows/update-snyk.yaml | 2 +- .gitignore | 1 + .gitpod.Dockerfile | 4 +- .goreleaser.yaml | 121 + .readthedocs.yml | 6 +- CODEOWNERS | 12 + CONTRIBUTING.md | 1 + Dockerfile | 28 +- Makefile | 190 +- OWNERS | 2 + Procfile | 17 +- README.md | 8 +- SECURITY-INSIGHTS.yml | 128 + SECURITY.md | 33 +- SECURITY_CONTACTS | 2 +- USERS.md | 91 +- VERSION | 2 +- .../controllers/applicationset_controller.go | 638 +- .../applicationset_controller_test.go | 4675 ++++--- .../controllers/clustereventhandler.go | 83 +- .../controllers/clustereventhandler_test.go | 409 +- .../controllers/requeue_after_test.go | 153 + applicationset/controllers/templatePatch.go | 46 + .../controllers/templatePatch_test.go | 249 + .../create-only.yaml | 35 + .../create-update.yaml | 35 + .../guestbook-ui-deployment.yaml | 20 + .../engineering-dev/guestbook-ui-svc.yaml | 10 + .../guestbook-ui-deployment.yaml | 20 + .../engineering-prod/guestbook-ui-svc.yaml | 10 + .../examples/cluster/cluster-example.yaml | 1 + .../ducktype-example.yaml | 1 + .../examples/design-doc/applicationset.yaml | 1 + .../design-doc/git-directory-discovery.yaml | 1 + .../design-doc/git-files-discovery.yaml | 1 + .../design-doc/git-files-literal.yaml | 1 + applicationset/examples/design-doc/list.yaml | 1 + .../design-doc/template-override.yaml | 1 + .../git-directories-exclude-example.yaml | 1 + .../git-directories-example.yaml | 1 + .../git-generator-files.yaml | 1 + .../list-elementsYaml-example.yaml | 14 + .../examples/list-generator/list-example.yaml | 1 + .../examples/matrix/cluster-and-git.yaml | 1 + .../examples/matrix/list-and-git.yaml | 1 + .../examples/matrix/list-and-list.yaml | 1 + .../matrix/matrix-and-union-in-matrix.yaml | 1 + .../merge/merge-clusters-and-list.yaml | 1 + .../examples/merge/merge-two-matrixes.yaml | 1 + .../pull-request-example.yaml | 1 + ...-provider-example-fasttemplate-gitlab.yaml | 26 + .../scm-provider-example.yaml | 1 + .../template-overrides-example.yaml | 1 + applicationset/generators/cluster.go | 49 +- applicationset/generators/duck_type.go | 4 +- applicationset/generators/duck_type_test.go | 3 +- .../generators/generator_spec_processor.go | 85 +- .../generator_spec_processor_test.go | 351 +- applicationset/generators/git.go | 66 +- applicationset/generators/git_test.go | 397 +- applicationset/generators/list.go | 13 + applicationset/generators/matrix.go | 78 +- applicationset/generators/matrix_test.go | 310 +- applicationset/generators/merge.go | 82 +- applicationset/generators/plugin.go | 211 + applicationset/generators/plugin_test.go | 705 + applicationset/generators/pull_request.go | 71 +- .../generators/pull_request_test.go | 215 +- applicationset/generators/scm_provider.go | 98 +- .../generators/scm_provider_test.go | 256 +- applicationset/generators/scm_utils.go | 5 + .../generators/value_interpolation.go | 43 + .../generators/value_interpolation_test.go | 125 + .../services/internal/http/client.go | 161 + .../services/internal/http/client_options.go | 22 + .../services/internal/http/client_test.go | 163 + applicationset/services/mocks/Repos.go | 81 + applicationset/services/mocks/RepositoryDB.go | 57 + .../services/plugin/plugin_service.go | 73 + .../services/plugin/plugin_service_test.go | 52 + applicationset/services/plugin/utils.go | 21 + applicationset/services/plugin/utils_test.go | 17 + .../services/pull_request/azure_devops.go | 145 + .../pull_request/azure_devops_test.go | 221 + .../services/pull_request/bitbucket_cloud.go | 138 + .../pull_request/bitbucket_cloud_test.go | 410 + .../services/pull_request/bitbucket_server.go | 9 +- .../pull_request/bitbucket_server_test.go | 92 +- applicationset/services/pull_request/gitea.go | 19 +- .../services/pull_request/gitea_test.go | 7 +- .../services/pull_request/github.go | 9 +- .../services/pull_request/github_test.go | 24 +- .../services/pull_request/gitlab.go | 22 +- .../services/pull_request/gitlab_test.go | 11 +- .../services/pull_request/interface.go | 5 +- applicationset/services/pull_request/utils.go | 9 + .../services/pull_request/utils_test.go | 167 +- applicationset/services/repo_service.go | 147 +- applicationset/services/repo_service_test.go | 344 +- .../services/scm_provider/aws_codecommit.go | 376 + .../mocks/AWSCodeCommitClient.go | 321 + .../aws_codecommit/mocks/AWSTaggingClient.go | 110 + .../scm_provider/aws_codecommit_test.go | 483 + .../services/scm_provider/bitbucket_cloud.go | 2 +- .../scm_provider/bitbucket_cloud_test.go | 5 +- .../services/scm_provider/bitbucket_server.go | 6 +- .../scm_provider/bitbucket_server_test.go | 38 +- applicationset/services/scm_provider/gitea.go | 12 +- .../services/scm_provider/gitea_test.go | 2 +- .../services/scm_provider/github.go | 3 +- .../services/scm_provider/github_test.go | 2 +- .../services/scm_provider/gitlab.go | 56 +- .../services/scm_provider/gitlab_test.go | 858 +- applicationset/utils/clusterUtils.go | 10 +- applicationset/utils/clusterUtils_test.go | 13 +- applicationset/utils/createOrUpdate.go | 113 +- applicationset/utils/createOrUpdate_test.go | 234 + applicationset/utils/policy.go | 67 +- applicationset/utils/selector.go | 261 + applicationset/utils/template_functions.go | 71 + applicationset/utils/utils.go | 228 +- applicationset/utils/utils_test.go | 472 +- .../testdata/azuredevops-pull-request.json | 85 + .../webhook/testdata/azuredevops-push.json | 76 + .../github-pull-request-labeled-event.json | 473 + applicationset/webhook/webhook.go | 130 +- applicationset/webhook/webhook_test.go | 261 +- assets/badge.svg | 2 + assets/embed.go | 1 + assets/swagger.json | 829 +- .../commands/argocd_application_controller.go | 116 +- .../commands/applicationset_controller.go | 160 +- .../commands/argocd_cmp_server.go | 18 +- cmd/argocd-dex/commands/argocd_dex.go | 2 +- .../commands/argocd_k8s_auth.go | 1 + cmd/argocd-k8s-auth/commands/aws.go | 16 +- cmd/argocd-k8s-auth/commands/aws_test.go | 8 +- cmd/argocd-k8s-auth/commands/azure.go | 43 + .../commands/controller.go | 54 +- .../commands/argocd_repo_server.go | 93 +- cmd/argocd-server/commands/argocd_server.go | 57 +- cmd/argocd/commands/account.go | 29 +- cmd/argocd/commands/admin/admin.go | 104 +- cmd/argocd/commands/admin/app.go | 62 +- cmd/argocd/commands/admin/app_test.go | 1 + cmd/argocd/commands/admin/backup.go | 28 +- cmd/argocd/commands/admin/cluster.go | 157 +- cmd/argocd/commands/admin/dashboard.go | 31 +- .../commands/admin/generatespec_utils.go | 2 +- cmd/argocd/commands/admin/notifications.go | 7 +- cmd/argocd/commands/admin/project.go | 12 + .../commands/admin/project_allowlist.go | 8 +- cmd/argocd/commands/admin/settings.go | 120 +- cmd/argocd/commands/admin/settings_rbac.go | 122 +- .../commands/admin/settings_rbac_test.go | 91 +- cmd/argocd/commands/admin/settings_test.go | 111 +- .../admin/testdata/rbac/argocd-rbac-cm.yaml | 2 + .../commands/admin/testdata/rbac/policy.csv | 2 + cmd/argocd/commands/app.go | 982 +- cmd/argocd/commands/app_actions.go | 33 +- cmd/argocd/commands/app_resource_test.go | 82 +- cmd/argocd/commands/app_resources.go | 129 +- cmd/argocd/commands/app_test.go | 519 +- cmd/argocd/commands/applicationset.go | 77 +- cmd/argocd/commands/applicationset_test.go | 199 +- cmd/argocd/commands/bcrypt.go | 6 +- cmd/argocd/commands/bcrypt_test.go | 2 +- cmd/argocd/commands/cert.go | 10 +- cmd/argocd/commands/cluster.go | 40 +- cmd/argocd/commands/common.go | 2 +- cmd/argocd/commands/completion.go | 16 +- cmd/argocd/commands/context.go | 8 + cmd/argocd/commands/gpg.go | 28 + cmd/argocd/commands/headless/headless.go | 115 +- cmd/argocd/commands/initialize/cmd.go | 3 +- cmd/argocd/commands/login.go | 11 +- cmd/argocd/commands/login_test.go | 2 - cmd/argocd/commands/logout.go | 4 + cmd/argocd/commands/project.go | 228 +- cmd/argocd/commands/project_role.go | 129 +- cmd/argocd/commands/projectwindows.go | 71 +- cmd/argocd/commands/relogin.go | 16 +- cmd/argocd/commands/relogin_test.go | 64 + cmd/argocd/commands/repo.go | 37 +- cmd/argocd/commands/repocreds.go | 29 + cmd/argocd/commands/root.go | 11 +- cmd/argocd/commands/tree.go | 168 + cmd/argocd/commands/tree_test.go | 216 + cmd/argocd/commands/version.go | 6 + cmd/argocd/commands/version_test.go | 4 +- cmd/util/app.go | 279 +- cmd/util/app_test.go | 95 +- cmd/util/applicationset.go | 4 +- cmd/util/applicationset_test.go | 8 +- cmd/util/cluster.go | 47 + cmd/util/cluster_test.go | 112 + cmd/util/project.go | 9 +- cmd/util/repo.go | 2 + cmpserver/apiclient/clientset.go | 5 +- cmpserver/apiclient/plugin.pb.go | 114 +- cmpserver/plugin/config.go | 11 +- cmpserver/plugin/config_test.go | 215 + cmpserver/plugin/plugin.go | 115 +- cmpserver/plugin/plugin.proto | 1 + cmpserver/plugin/plugin_test.go | 121 +- cmpserver/plugin/plugin_unix.go | 4 + cmpserver/plugin/plugin_windows.go | 4 + .../testdata/kustomize/config/plugin.yaml | 19 +- cmpserver/server.go | 2 +- common/common.go | 168 +- common/common_test.go | 46 + common/version.go | 4 + controller/appcontroller.go | 757 +- controller/appcontroller_test.go | 1091 +- controller/cache/cache.go | 159 +- controller/cache/cache_test.go | 267 +- controller/cache/info.go | 69 +- controller/cache/info_test.go | 97 +- controller/clusterinfoupdater.go | 62 +- controller/clusterinfoupdater_test.go | 92 +- controller/health.go | 12 +- controller/health_test.go | 4 +- controller/hook.go | 158 + controller/metrics/metrics.go | 19 +- controller/metrics/metrics_test.go | 111 +- controller/metrics/workqueue.go | 101 - controller/sharding/cache.go | 264 + controller/sharding/cache_test.go | 511 + controller/sharding/sharding.go | 415 +- controller/sharding/sharding_test.go | 949 +- controller/sharding/shuffle_test.go | 86 + controller/state.go | 361 +- controller/state_test.go | 758 +- controller/sync.go | 72 +- controller/sync_test.go | 121 +- controller/testdata/data.go | 3 + controller/testdata/diff-cache.yaml | 498 + docs/SUPPORT.md | 9 +- docs/assets/api-management.png | Bin 14376 -> 0 bytes docs/assets/argocd-arch-authn-authz.jpg | Bin 0 -> 275921 bytes docs/assets/argocd-components.png | Bin 0 -> 178109 bytes docs/assets/argocd-core-components.png | Bin 0 -> 160491 bytes docs/assets/azure-devops-webhook-config.png | Bin 0 -> 40779 bytes docs/assets/extra_info-1.png | Bin 0 -> 32441 bytes docs/assets/extra_info-2.png | Bin 0 -> 8481 bytes docs/assets/extra_info.png | Bin 0 -> 134747 bytes docs/assets/groups-claim.png | Bin 82650 -> 0 bytes docs/assets/groups-scope.png | Bin 59680 -> 0 bytes docs/assets/identity-center-1.png | Bin 0 -> 108361 bytes docs/assets/identity-center-2.png | Bin 0 -> 179317 bytes docs/assets/identity-center-3.png | Bin 0 -> 70839 bytes docs/assets/identity-center-4.png | Bin 0 -> 20636 bytes docs/assets/identity-center-5.png | Bin 0 -> 52918 bytes docs/assets/identity-center-6.png | Bin 0 -> 56086 bytes docs/assets/keycloak-add-client.png | Bin 157269 -> 62625 bytes docs/assets/keycloak-add-client_2.png | Bin 0 -> 73222 bytes docs/assets/keycloak-add-scope.png | Bin 172601 -> 58784 bytes .../assets/keycloak-client-scope-selected.png | Bin 95405 -> 0 bytes docs/assets/keycloak-client-scope.png | Bin 247556 -> 82176 bytes docs/assets/keycloak-client-secret.png | Bin 123340 -> 67653 bytes docs/assets/keycloak-configure-client.png | Bin 198765 -> 82472 bytes docs/assets/keycloak-groups-mapper.png | Bin 200602 -> 67678 bytes docs/assets/keycloak-user-group.png | Bin 101393 -> 62255 bytes docs/assets/okta-app.png | Bin 0 -> 260259 bytes docs/assets/okta-auth-policy.png | Bin 0 -> 85431 bytes docs/assets/okta-auth-rule.png | Bin 0 -> 229782 bytes docs/assets/okta-create-oidc-app.png | Bin 0 -> 360829 bytes docs/assets/okta-groups-claim.png | Bin 0 -> 144958 bytes docs/assets/okta-groups-scope.png | Bin 0 -> 187202 bytes docs/assets/release-action.png | Bin 0 -> 42174 bytes docs/assets/versions.js | 10 - docs/assets/zitadel-actions.png | Bin 0 -> 57176 bytes docs/assets/zitadel-application-1.png | Bin 0 -> 129320 bytes docs/assets/zitadel-application-2.png | Bin 0 -> 116260 bytes docs/assets/zitadel-application-3.png | Bin 0 -> 50472 bytes docs/assets/zitadel-application-4.png | Bin 0 -> 48643 bytes docs/assets/zitadel-application-secrets.png | Bin 0 -> 75511 bytes docs/assets/zitadel-application-settings.png | Bin 0 -> 70144 bytes docs/assets/zitadel-argocd-login.png | Bin 0 -> 7236 bytes docs/assets/zitadel-argocd-user-info.png | Bin 0 -> 33256 bytes .../assets/zitadel-project-authorizations.png | Bin 0 -> 48347 bytes docs/assets/zitadel-project-roles.png | Bin 0 -> 56677 bytes docs/assets/zitadel-project-settings.png | Bin 0 -> 17052 bytes docs/assets/zitadel-project.png | Bin 0 -> 68999 bytes docs/cli_installation.md | 16 + docs/developer-guide/api-docs.md | 16 +- .../architecture/authz-authn.md | 109 + .../architecture/components.md | 118 + docs/developer-guide/ci.md | 6 +- docs/developer-guide/code-contributions.md | 6 +- .../contributors-quickstart.md | 32 +- .../debugging-remote-environment.md | 4 +- .../extensions/proxy-extensions.md | 335 + .../extensions/ui-extensions.md | 160 + docs/developer-guide/faq.md | 13 +- .../release-process-and-cadence.md | 48 +- docs/developer-guide/releasing.md | 240 +- docs/developer-guide/running-locally.md | 4 +- docs/developer-guide/site.md | 17 +- docs/developer-guide/test-e2e.md | 2 +- docs/developer-guide/toolchain-guide.md | 25 +- docs/developer-guide/ui-extensions.md | 99 +- docs/developer-guide/use-gitpod.md | 2 +- docs/faq.md | 67 +- docs/getting_started.md | 16 +- docs/index.md | 4 +- docs/operator-manual/app-any-namespace.md | 17 +- docs/operator-manual/application.yaml | 55 +- docs/operator-manual/applicationset.yaml | 305 +- .../applicationset/Appset-Any-Namespace.md | 231 + .../Controlling-Resource-Modification.md | 231 +- .../Generators-Cluster-Decision-Resource.md | 8 +- .../applicationset/Generators-Cluster.md | 67 +- .../Generators-Git-File-Globbing.md | 85 + .../applicationset/Generators-Git.md | 156 +- .../applicationset/Generators-List.md | 93 +- .../applicationset/Generators-Matrix.md | 129 +- .../applicationset/Generators-Merge.md | 98 +- .../applicationset/Generators-Plugin.md | 344 + .../Generators-Post-Selector.md | 61 + .../applicationset/Generators-Pull-Request.md | 150 +- .../applicationset/Generators-SCM-Provider.md | 158 +- .../applicationset/Generators.md | 7 +- .../applicationset/GoTemplate.md | 63 +- ...ssive-Rollouts.md => Progressive-Syncs.md} | 34 +- .../applicationset/Template.md | 72 +- .../applicationset/Use-Cases.md | 22 +- .../applicationset-specification.md | 7 + docs/operator-manual/applicationset/index.md | 8 +- docs/operator-manual/argocd-cm-yaml.md | 7 + docs/operator-manual/argocd-cm.yaml | 93 +- .../argocd-cmd-params-cm-yaml.md | 7 + .../operator-manual/argocd-cmd-params-cm.yaml | 77 +- docs/operator-manual/argocd-rbac-cm-yaml.md | 7 + docs/operator-manual/argocd-rbac-cm.yaml | 9 + .../operator-manual/argocd-repo-creds-yaml.md | 7 + .../argocd-repositories-yaml.md | 7 + docs/operator-manual/argocd-repositories.yaml | 19 +- docs/operator-manual/argocd-secret-yaml.md | 7 + .../argocd-ssh-known-hosts-cm-yaml.md | 7 + .../argocd-ssh-known-hosts-cm.yaml | 14 +- .../argocd-tls-certs-cm-yaml.md | 7 + docs/operator-manual/cluster-management.md | 23 + .../config-management-plugins.md | 311 +- docs/operator-manual/core.md | 99 + docs/operator-manual/custom-styles.md | 6 +- docs/operator-manual/declarative-setup.md | 493 +- docs/operator-manual/deep_links.md | 61 +- docs/operator-manual/disaster_recovery.md | 4 +- .../dynamic-cluster-distribution.md | 53 + docs/operator-manual/health.md | 50 +- docs/operator-manual/high_availability.md | 191 +- docs/operator-manual/ingress.md | 235 +- docs/operator-manual/installation.md | 40 +- docs/operator-manual/metrics.md | 71 +- docs/operator-manual/notifications/catalog.md | 36 +- .../notifications/functions.md | 10 + docs/operator-manual/notifications/index.md | 115 +- .../notifications/monitoring.md | 10 +- .../notifications/services/alertmanager.md | 8 +- .../notifications/services/awssqs.md | 119 + .../notifications/services/email.md | 8 +- .../notifications/services/github.md | 28 +- .../notifications/services/googlechat.md | 23 +- .../notifications/services/grafana.md | 8 +- .../notifications/services/mattermost.md | 2 +- .../notifications/services/newrelic.md | 2 +- .../notifications/services/opsgenie.md | 7 +- .../notifications/services/overview.md | 1 + .../notifications/services/pagerduty.md | 18 +- .../notifications/services/pagerduty_v2.md | 78 + .../notifications/services/pushover.md | 4 +- .../notifications/services/rocketchat.md | 4 +- .../notifications/services/slack.md | 111 +- .../notifications/services/teams.md | 8 +- .../notifications/services/telegram.md | 4 +- .../notifications/services/webex.md | 2 +- .../notifications/services/webhook.md | 29 +- .../notifications/subscriptions.md | 8 +- .../notifications/templates.md | 80 +- .../operator-manual/notifications/triggers.md | 17 +- .../notifications/troubleshooting-commands.md | 4 + .../notifications/troubleshooting-errors.md | 2 +- .../notifications/troubleshooting.md | 45 +- docs/operator-manual/project-specification.md | 7 + docs/operator-manual/project.yaml | 10 + docs/operator-manual/rbac.md | 173 +- docs/operator-manual/reconcile.md | 113 + docs/operator-manual/resource_actions.md | 136 +- docs/operator-manual/secret-management.md | 3 +- docs/operator-manual/security.md | 10 +- .../argocd-application-controller.md | 119 +- .../server-commands/argocd-dex.md | 2 + .../server-commands/argocd-dex_gendexcfg.md | 3 + .../server-commands/argocd-dex_rundex.md | 3 + .../server-commands/argocd-repo-server.md | 13 +- .../server-commands/argocd-server.md | 155 +- .../server-commands/argocd-server_version.md | 3 + docs/operator-manual/signed-release-assets.md | 161 +- .../tested-kubernetes-versions.md | 6 + docs/operator-manual/tls.md | 5 +- docs/operator-manual/troubleshooting.md | 2 +- docs/operator-manual/ui-customization.md | 9 + docs/operator-manual/upgrading/1.8-2.0.md | 2 +- docs/operator-manual/upgrading/2.10-2.11.md | 5 + docs/operator-manual/upgrading/2.3-2.4.md | 21 + docs/operator-manual/upgrading/2.4-2.5.md | 78 +- docs/operator-manual/upgrading/2.5-2.6.md | 86 +- docs/operator-manual/upgrading/2.6-2.7.md | 108 + docs/operator-manual/upgrading/2.7-2.8.md | 73 + docs/operator-manual/upgrading/2.8-2.9.md | 5 + docs/operator-manual/upgrading/2.9-2.10.md | 16 + docs/operator-manual/upgrading/overview.md | 5 + .../operator-manual/user-management/google.md | 27 +- .../user-management/identity-center.md | 79 + docs/operator-manual/user-management/index.md | 71 +- .../user-management/keycloak.md | 35 +- .../user-management/microsoft.md | 51 +- docs/operator-manual/user-management/okta.md | 85 +- .../user-management/openunison.md | 2 +- .../user-management/zitadel.md | 210 + docs/operator-manual/webhook.md | 39 +- docs/proposals/002-ui-extensions.md | 2 +- .../proposals/004-scalability-benchmarking.md | 122 + .../applicationset-plugin-generator.md | 216 + docs/proposals/config-management-plugin-v2.md | 2 +- ...plication-sync-user-using-impersonation.md | 592 + docs/proposals/feature-bounties.md | 50 + .../feature-bounties/hide-annotations.md | 23 + docs/proposals/native-oci-support.md | 135 + ...parameterized-config-management-plugins.md | 36 +- docs/proposals/project-repos-and-clusters.md | 2 +- ...cing-clusters-across-shards-dynamically.md | 142 + .../respect-rbac-for-resource-exclusions.md | 74 + docs/proposals/sync-timeout.md | 126 + docs/requirements.txt | 9 +- docs/snyk/index.md | 80 +- docs/snyk/master/argocd-iac-install.html | 774 +- .../master/argocd-iac-namespace-install.html | 730 +- docs/snyk/master/argocd-test.html | 3305 ++++- .../master/ghcr.io_dexidp_dex_v2.35.3.html | 492 - .../master/ghcr.io_dexidp_dex_v2.38.0.html | 2561 ++++ docs/snyk/master/haproxy_2.6.14-alpine.html | 1376 ++ docs/snyk/master/haproxy_2.6.2-alpine.html | 492 - .../quay.io_argoproj_argocd_latest.html | 2840 +++- docs/snyk/master/redis_7.0.14-alpine.html | 993 ++ docs/snyk/master/redis_7.0.7-alpine.html | 492 - docs/snyk/v2.3.13/argocd-iac-install.html | 3251 ----- .../v2.3.13/argocd-iac-namespace-install.html | 3251 ----- .../v2.3.13/ghcr.io_dexidp_dex_v2.35.3.html | 492 - docs/snyk/v2.3.13/haproxy_2.0.29-alpine.html | 492 - .../quay.io_argoproj_argocd_v2.3.13.html | 2649 ---- docs/snyk/v2.3.13/redis_6.2.8-alpine.html | 492 - .../v2.4.19/ghcr.io_dexidp_dex_v2.35.3.html | 492 - docs/snyk/v2.4.19/haproxy_2.0.29-alpine.html | 492 - .../quay.io_argoproj_argocd_v2.4.19.html | 2649 ---- docs/snyk/v2.4.19/redis_7.0.7-alpine.html | 492 - .../v2.5.7/ghcr.io_dexidp_dex_v2.35.3.html | 492 - docs/snyk/v2.5.7/haproxy_2.6.2-alpine.html | 492 - .../quay.io_argoproj_argocd_v2.5.7.html | 2649 ---- docs/snyk/v2.5.7/redis_7.0.7-alpine.html | 492 - docs/snyk/v2.6.0-rc4/argocd-test.html | 623 - .../ghcr.io_dexidp_dex_v2.35.3.html | 492 - .../snyk/v2.6.0-rc4/haproxy_2.6.2-alpine.html | 492 - .../quay.io_argoproj_argocd_v2.6.0-rc4.html | 2649 ---- docs/snyk/v2.6.0-rc4/redis_7.0.7-alpine.html | 492 - .../argocd-iac-install.html | 724 +- .../argocd-iac-namespace-install.html | 718 +- .../{v2.3.13 => v2.7.17}/argocd-test.html | 2439 ++-- .../v2.7.17/ghcr.io_dexidp_dex_v2.37.0.html | 4337 ++++++ docs/snyk/v2.7.17/haproxy_2.6.14-alpine.html | 1376 ++ .../quay.io_argoproj_argocd_v2.7.17.html} | 6735 ++++------ docs/snyk/v2.7.17/redis_7.0.14-alpine.html | 993 ++ .../argocd-iac-install.html | 732 +- .../argocd-iac-namespace-install.html | 726 +- .../snyk/{v2.5.7 => v2.8.13}/argocd-test.html | 2519 ++-- .../v2.8.13/ghcr.io_dexidp_dex_v2.37.0.html | 4337 ++++++ docs/snyk/v2.8.13/haproxy_2.6.14-alpine.html | 1376 ++ .../quay.io_argoproj_argocd_v2.8.13.html | 5054 +++++++ docs/snyk/v2.8.13/redis_7.0.11-alpine.html | 2032 +++ .../argocd-iac-install.html | 732 +- .../argocd-iac-namespace-install.html | 732 +- .../snyk/{v2.4.19 => v2.9.9}/argocd-test.html | 2577 ++-- .../v2.9.9/ghcr.io_dexidp_dex_v2.37.0.html | 4337 ++++++ docs/snyk/v2.9.9/haproxy_2.6.14-alpine.html | 1376 ++ .../quay.io_argoproj_argocd_v2.9.9.html | 4812 +++++++ docs/snyk/v2.9.9/redis_7.0.11-alpine.html | 2032 +++ docs/understand_the_basics.md | 5 +- docs/user-guide/annotations-and-labels.md | 26 + docs/user-guide/app_deletion.md | 14 +- docs/user-guide/application-set.md | 56 +- docs/user-guide/application-specification.md | 7 + docs/user-guide/best_practices.md | 4 +- docs/user-guide/build-environment.md | 3 +- docs/user-guide/ci_automation.md | 4 +- docs/user-guide/commands/argocd.md | 7 + docs/user-guide/commands/argocd_account.md | 26 +- .../commands/argocd_account_bcrypt.md | 16 +- .../commands/argocd_account_can-i.md | 7 + .../commands/argocd_account_delete-token.md | 7 + .../commands/argocd_account_generate-token.md | 7 + .../commands/argocd_account_get-user-info.md | 17 + .../user-guide/commands/argocd_account_get.md | 7 + .../commands/argocd_account_list.md | 7 + .../argocd_account_update-password.md | 13 +- docs/user-guide/commands/argocd_admin.md | 93 + docs/user-guide/commands/argocd_admin_app.md | 22 + ...argocd_admin_app_diff-reconcile-results.md | 7 + .../argocd_admin_app_generate-spec.md | 13 +- .../argocd_admin_app_get-reconcile-results.md | 9 + .../commands/argocd_admin_cluster.md | 23 +- .../argocd_admin_cluster_generate-spec.md | 9 + .../argocd_admin_cluster_kubeconfig.md | 25 + .../argocd_admin_cluster_namespaces.md | 8 + ...ster_namespaces_disable-namespaced-mode.md | 8 + ...uster_namespaces_enable-namespaced-mode.md | 8 + .../commands/argocd_admin_cluster_shards.md | 13 +- .../commands/argocd_admin_cluster_stats.md | 25 +- .../commands/argocd_admin_dashboard.md | 25 +- .../commands/argocd_admin_export.md | 8 + .../commands/argocd_admin_import.md | 8 + .../commands/argocd_admin_initial-password.md | 8 + .../commands/argocd_admin_notifications.md | 8 + .../argocd_admin_notifications_template.md | 8 + ...argocd_admin_notifications_template_get.md | 8 + ...ocd_admin_notifications_template_notify.md | 8 + .../argocd_admin_notifications_trigger.md | 8 + .../argocd_admin_notifications_trigger_get.md | 8 + .../argocd_admin_notifications_trigger_run.md | 8 + docs/user-guide/commands/argocd_admin_proj.md | 7 + .../argocd_admin_proj_generate-allow-list.md | 15 + .../argocd_admin_proj_generate-spec.md | 20 + .../argocd_admin_proj_update-role-policy.md | 8 + docs/user-guide/commands/argocd_admin_repo.md | 7 + .../argocd_admin_repo_generate-spec.md | 8 + .../commands/argocd_admin_settings.md | 8 + .../commands/argocd_admin_settings_rbac.md | 8 + .../argocd_admin_settings_rbac_can.md | 8 + .../argocd_admin_settings_rbac_validate.md | 73 +- ...rgocd_admin_settings_resource-overrides.md | 9 + ...dmin_settings_resource-overrides_health.md | 8 + ...s_resource-overrides_ignore-differences.md | 8 + ...ource-overrides_ignore-resource-updates.md | 81 + ...ettings_resource-overrides_list-actions.md | 8 + ..._settings_resource-overrides_run-action.md | 8 + .../argocd_admin_settings_validate.md | 10 +- docs/user-guide/commands/argocd_app.md | 10 + .../user-guide/commands/argocd_app_actions.md | 17 + .../commands/argocd_app_actions_list.md | 14 + .../commands/argocd_app_actions_run.md | 14 + .../commands/argocd_app_add-source.md | 109 + docs/user-guide/commands/argocd_app_create.md | 16 +- .../commands/argocd_app_delete-resource.md | 8 + docs/user-guide/commands/argocd_app_delete.md | 9 + docs/user-guide/commands/argocd_app_diff.md | 9 + docs/user-guide/commands/argocd_app_edit.md | 10 +- docs/user-guide/commands/argocd_app_get.md | 51 +- .../user-guide/commands/argocd_app_history.md | 12 +- docs/user-guide/commands/argocd_app_list.md | 7 + docs/user-guide/commands/argocd_app_logs.md | 51 +- .../commands/argocd_app_manifests.md | 7 + .../commands/argocd_app_patch-resource.md | 8 + docs/user-guide/commands/argocd_app_patch.md | 31 +- .../commands/argocd_app_remove-source.md | 57 + .../commands/argocd_app_resources.md | 13 +- .../commands/argocd_app_rollback.md | 15 +- docs/user-guide/commands/argocd_app_set.md | 37 +- docs/user-guide/commands/argocd_app_sync.md | 10 + .../commands/argocd_app_terminate-op.md | 7 + docs/user-guide/commands/argocd_app_unset.md | 39 +- docs/user-guide/commands/argocd_app_wait.md | 10 + docs/user-guide/commands/argocd_appset.md | 8 + .../commands/argocd_appset_create.md | 7 + .../commands/argocd_appset_delete.md | 7 + docs/user-guide/commands/argocd_appset_get.md | 14 + .../user-guide/commands/argocd_appset_list.md | 16 +- docs/user-guide/commands/argocd_cert.md | 8 + .../commands/argocd_cert_add-ssh.md | 7 + .../commands/argocd_cert_add-tls.md | 9 +- docs/user-guide/commands/argocd_cert_list.md | 13 +- docs/user-guide/commands/argocd_cert_rm.md | 7 + docs/user-guide/commands/argocd_cluster.md | 8 + .../user-guide/commands/argocd_cluster_add.md | 9 + .../user-guide/commands/argocd_cluster_get.md | 7 + .../commands/argocd_cluster_list.md | 29 + docs/user-guide/commands/argocd_cluster_rm.md | 7 + .../commands/argocd_cluster_rotate-auth.md | 7 + .../user-guide/commands/argocd_cluster_set.md | 7 + docs/user-guide/commands/argocd_completion.md | 28 +- docs/user-guide/commands/argocd_context.md | 20 + docs/user-guide/commands/argocd_gpg.md | 8 + docs/user-guide/commands/argocd_gpg_add.md | 14 + docs/user-guide/commands/argocd_gpg_get.md | 20 + docs/user-guide/commands/argocd_gpg_list.md | 20 + docs/user-guide/commands/argocd_gpg_rm.md | 7 + docs/user-guide/commands/argocd_login.md | 17 +- docs/user-guide/commands/argocd_logout.md | 16 + docs/user-guide/commands/argocd_proj.md | 26 + .../commands/argocd_proj_add-destination.md | 17 + .../argocd_proj_add-orphaned-ignore.md | 17 + .../commands/argocd_proj_add-signature-key.md | 14 + .../argocd_proj_add-source-namespace.md | 55 + .../commands/argocd_proj_add-source.md | 14 + .../argocd_proj_allow-cluster-resource.md | 14 + .../argocd_proj_allow-namespace-resource.md | 14 + .../user-guide/commands/argocd_proj_create.md | 17 + .../user-guide/commands/argocd_proj_delete.md | 14 + .../argocd_proj_deny-cluster-resource.md | 14 + .../argocd_proj_deny-namespace-resource.md | 14 + docs/user-guide/commands/argocd_proj_edit.md | 14 + docs/user-guide/commands/argocd_proj_get.md | 17 + docs/user-guide/commands/argocd_proj_list.md | 17 + .../argocd_proj_remove-destination.md | 14 + .../argocd_proj_remove-orphaned-ignore.md | 19 +- .../argocd_proj_remove-signature-key.md | 14 + .../argocd_proj_remove-source-namespace.md | 55 + .../commands/argocd_proj_remove-source.md | 14 + docs/user-guide/commands/argocd_proj_role.md | 7 + .../commands/argocd_proj_role_add-group.md | 7 + .../commands/argocd_proj_role_add-policy.md | 36 + .../commands/argocd_proj_role_create-token.md | 19 + .../commands/argocd_proj_role_create.md | 14 + .../commands/argocd_proj_role_delete-token.md | 39 + .../commands/argocd_proj_role_delete.md | 13 + .../commands/argocd_proj_role_get.md | 22 + .../commands/argocd_proj_role_list-tokens.md | 17 + .../commands/argocd_proj_role_list.md | 17 + .../commands/argocd_proj_role_remove-group.md | 7 + .../argocd_proj_role_remove-policy.md | 36 + docs/user-guide/commands/argocd_proj_set.md | 17 + .../commands/argocd_proj_windows.md | 24 + .../commands/argocd_proj_windows_add.md | 30 + .../commands/argocd_proj_windows_delete.md | 18 + ...argocd_proj_windows_disable-manual-sync.md | 18 + .../argocd_proj_windows_enable-manual-sync.md | 21 + .../commands/argocd_proj_windows_list.md | 21 + .../commands/argocd_proj_windows_update.md | 16 + docs/user-guide/commands/argocd_relogin.md | 28 +- docs/user-guide/commands/argocd_repo.md | 26 + docs/user-guide/commands/argocd_repo_add.md | 14 + docs/user-guide/commands/argocd_repo_get.md | 9 +- docs/user-guide/commands/argocd_repo_list.md | 9 +- docs/user-guide/commands/argocd_repo_rm.md | 7 + docs/user-guide/commands/argocd_repocreds.md | 21 + .../commands/argocd_repocreds_add.md | 8 + .../commands/argocd_repocreds_list.md | 23 + .../commands/argocd_repocreds_rm.md | 14 + docs/user-guide/commands/argocd_version.md | 8 + docs/user-guide/config-management-plugins.md | 3 + docs/user-guide/diff-strategies.md | 131 + docs/user-guide/diffing.md | 18 +- docs/user-guide/directory.md | 3 + docs/user-guide/environment-variables.md | 16 +- docs/user-guide/external-url.md | 4 +- docs/user-guide/extra_info.md | 28 + docs/user-guide/helm.md | 228 +- docs/user-guide/jsonnet.md | 2 +- docs/user-guide/kustomize.md | 163 + docs/user-guide/multiple_sources.md | 8 +- docs/user-guide/private-repositories.md | 44 +- docs/user-guide/projects.md | 40 +- docs/user-guide/resource_hooks.md | 10 +- docs/user-guide/resource_tracking.md | 30 +- docs/user-guide/skip_reconcile.md | 70 + docs/user-guide/status-badge.md | 11 +- docs/user-guide/sync-kubectl.md | 144 + docs/user-guide/sync-options.md | 74 +- docs/user-guide/sync-waves.md | 4 +- docs/user-guide/sync_windows.md | 3 +- docs/user-guide/tracking_strategies.md | 3 +- entrypoint.sh | 2 +- examples/dashboard.json | 2146 +-- .../argocd-server-applications/README.md | 11 + ...fications-controller-rbac-clusterrole.yaml | 28 + ...ns-controller-rbac-clusterrolebinding.yaml | 16 + .../argocd-server-rbac-clusterrole.yaml | 24 + ...argocd-server-rbac-clusterrolebinding.yaml | 16 + .../kustomization.yaml | 7 + examples/known-hosts/kustomization.yaml | 10 +- .../argocd-repo-server-deployment-patch.yaml | 6 +- examples/plugins/helm/get-parameters.sh | 4 +- examples/plugins/helm/kustomization.yaml | 6 +- go.mod | 416 +- go.sum | 1965 ++- hack/dev-mounter/main.go | 5 +- hack/gen-catalog/main.go | 4 +- hack/gen-crd-spec/main.go | 2 +- hack/gen-docs/main.go | 5 +- .../generators/application_generator.go | 8 +- .../generators/cluster_generator.go | 2 +- .../generators/project_generator.go | 2 +- .../generators/repo_generator.go | 2 +- hack/gen-resources/util/gen_options_parser.go | 4 +- hack/gen-resources/util/sizedwaitgroup.go | 2 +- hack/generate-proto.sh | 50 +- .../checksums/add-helm-checksums.sh | 5 +- .../helm-v3.11.1-linux-amd64.tar.gz.sha256 | 1 + .../helm-v3.11.1-linux-arm64.tar.gz.sha256 | 1 + .../helm-v3.11.1-linux-ppc64le.tar.gz.sha256 | 1 + .../helm-v3.11.1-linux-s390x.tar.gz.sha256 | 1 + .../helm-v3.11.2-linux-amd64.tar.gz.sha256 | 1 + .../helm-v3.11.2-linux-arm64.tar.gz.sha256 | 1 + .../helm-v3.11.2-linux-ppc64le.tar.gz.sha256 | 1 + .../helm-v3.11.2-linux-s390x.tar.gz.sha256 | 1 + .../helm-v3.12.0-linux-amd64.tar.gz.sha256 | 1 + .../helm-v3.12.0-linux-arm64.tar.gz.sha256 | 1 + .../helm-v3.12.0-linux-ppc64le.tar.gz.sha256 | 1 + .../helm-v3.12.0-linux-s390x.tar.gz.sha256 | 1 + .../helm-v3.12.1-linux-amd64.tar.gz.sha256 | 1 + .../helm-v3.12.1-linux-arm64.tar.gz.sha256 | 1 + .../helm-v3.12.1-linux-ppc64le.tar.gz.sha256 | 1 + .../helm-v3.12.1-linux-s390x.tar.gz.sha256 | 1 + .../helm-v3.13.1-linux-amd64.tar.gz.sha256 | 1 + .../helm-v3.13.1-linux-arm64.tar.gz.sha256 | 1 + .../helm-v3.13.1-linux-ppc64le.tar.gz.sha256 | 1 + .../helm-v3.13.1-linux-s390x.tar.gz.sha256 | 1 + .../helm-v3.13.2-linux-amd64.tar.gz.sha256 | 1 + .../helm-v3.13.2-linux-arm64.tar.gz.sha256 | 1 + .../helm-v3.13.2-linux-ppc64le.tar.gz.sha256 | 1 + .../helm-v3.13.2-linux-s390x.tar.gz.sha256 | 1 + .../helm-v3.14.0-linux-amd64.tar.gz.sha256 | 1 + .../helm-v3.14.0-linux-arm64.tar.gz.sha256 | 1 + .../helm-v3.14.0-linux-ppc64le.tar.gz.sha256 | 1 + .../helm-v3.14.0-linux-s390x.tar.gz.sha256 | 1 + .../helm-v3.14.1-linux-amd64.tar.gz.sha256 | 1 + .../helm-v3.14.1-linux-arm64.tar.gz.sha256 | 1 + .../helm-v3.14.1-linux-ppc64le.tar.gz.sha256 | 1 + .../helm-v3.14.1-linux-s390x.tar.gz.sha256 | 1 + .../helm-v3.14.2-darwin-amd64.tar.gz.sha256 | 1 + .../helm-v3.14.2-darwin-arm64.tar.gz.sha256 | 1 + .../helm-v3.14.2-linux-amd64.tar.gz.sha256 | 1 + .../helm-v3.14.2-linux-arm64.tar.gz.sha256 | 1 + .../helm-v3.14.2-linux-ppc64le.tar.gz.sha256 | 1 + .../helm-v3.14.2-linux-s390x.tar.gz.sha256 | 1 + .../helm-v3.14.3-darwin-amd64.tar.gz.sha256 | 1 + .../helm-v3.14.3-darwin-arm64.tar.gz.sha256 | 1 + .../helm-v3.14.3-linux-amd64.tar.gz.sha256 | 1 + .../helm-v3.14.3-linux-arm64.tar.gz.sha256 | 1 + .../helm-v3.14.3-linux-ppc64le.tar.gz.sha256 | 1 + .../helm-v3.14.3-linux-s390x.tar.gz.sha256 | 1 + ...kustomize_5.0.1_darwin_amd64.tar.gz.sha256 | 1 + ...kustomize_5.0.1_darwin_arm64.tar.gz.sha256 | 1 + .../kustomize_5.0.1_linux_amd64.tar.gz.sha256 | 1 + .../kustomize_5.0.1_linux_arm64.tar.gz.sha256 | 1 + ...ustomize_5.0.1_linux_ppc64le.tar.gz.sha256 | 1 + .../kustomize_5.0.1_linux_s390x.tar.gz.sha256 | 1 + ...kustomize_5.1.0_darwin_amd64.tar.gz.sha256 | 1 + .../kustomize_5.1.0_linux_amd64.tar.gz.sha256 | 1 + .../kustomize_5.1.0_linux_arm64.tar.gz.sha256 | 1 + ...ustomize_5.1.0_linux_ppc64le.tar.gz.sha256 | 1 + .../kustomize_5.1.0_linux_s390x.tar.gz.sha256 | 1 + ...kustomize_5.1.1_darwin_amd64.tar.gz.sha256 | 1 + .../kustomize_5.1.1_linux_amd64.tar.gz.sha256 | 1 + .../kustomize_5.1.1_linux_arm64.tar.gz.sha256 | 1 + ...ustomize_5.1.1_linux_ppc64le.tar.gz.sha256 | 1 + .../kustomize_5.1.1_linux_s390x.tar.gz.sha256 | 1 + ...kustomize_5.2.1_darwin_amd64.tar.gz.sha256 | 1 + ...kustomize_5.2.1_darwin_arm64.tar.gz.sha256 | 1 + .../kustomize_5.2.1_linux_amd64.tar.gz.sha256 | 1 + .../kustomize_5.2.1_linux_arm64.tar.gz.sha256 | 1 + ...ustomize_5.2.1_linux_ppc64le.tar.gz.sha256 | 1 + .../kustomize_5.2.1_linux_s390x.tar.gz.sha256 | 1 + hack/installers/install-codegen-go-tools.sh | 2 +- hack/installers/install-gotestsum.sh | 26 + ...{install-helm-linux.sh => install-helm.sh} | 6 +- hack/installers/install-kustomize.sh | 2 +- hack/installers/install-lint-tools.sh | 2 +- hack/known_types/main.go | 14 +- hack/ssh_known_hosts | 13 +- hack/test.sh | 10 +- hack/tool-versions.sh | 4 +- hack/trigger-release.sh | 73 +- hack/update-codegen.sh | 18 +- hack/update-kubernetes-version.sh | 22 + hack/update-openapi.sh | 20 +- hack/update-ssh-known-hosts.sh | 21 +- hack/update-supported-versions.sh | 23 + ...ocd-application-controller-deployment.yaml | 252 + ...argocd-application-controller-service.yaml | 20 + ...cd-application-controller-statefulset.yaml | 15 + .../kustomization.yaml | 9 + .../argocd-application-controller-role.yaml | 8 + ...cd-application-controller-rolebinding.yaml | 0 .../argocd-application-controller-sa.yaml | 0 .../kustomization.yaml | 7 + ...cd-application-controller-statefulset.yaml | 154 +- .../application-controller/kustomization.yaml | 4 +- ...-applicationset-controller-deployment.yaml | 274 +- ...argocd-applicationset-controller-role.yaml | 4 +- ...applicationset-controller-rolebinding.yaml | 4 +- .../argocd-applicationset-controller-sa.yaml | 4 +- ...ocd-applicationset-controller-service.yaml | 4 +- .../config/argocd-ssh-known-hosts-cm.yaml | 14 +- .../dex/argocd-dex-server-deployment.yaml | 4 +- .../base/dex/argocd-dex-server-service.yaml | 1 + .../notification/argocd-notifications-cm.yaml | 4 + ...d-notifications-controller-deployment.yaml | 33 +- ...ifications-controller-metrics-service.yaml | 2 + ...tifications-controller-network-policy.yaml | 4 + .../argocd-notifications-controller-role.yaml | 4 + ...-notifications-controller-rolebinding.yaml | 4 + .../argocd-notifications-secret.yaml | 4 + .../base/redis/argocd-redis-deployment.yaml | 3 +- .../base/redis/argocd-redis-rolebinding.yaml | 15 - manifests/base/redis/kustomization.yaml | 10 - .../argocd-repo-server-deployment.yaml | 59 +- .../argocd-repo-server-network-policy.yaml | 23 +- .../base/server/argocd-server-deployment.yaml | 285 +- ...applicationset-controller-clusterrole.yaml | 88 + ...tionset-controller-clusterrolebinding.yaml | 16 + .../kustomization.yaml | 6 + manifests/cluster-rbac/kustomization.yaml | 1 + .../server/argocd-server-clusterrole.yaml | 13 + manifests/core-install.yaml | 8655 ++++++++++-- manifests/crds/application-crd.yaml | 1034 +- manifests/crds/applicationset-crd.yaml | 7443 ++++++++-- manifests/crds/appproject-crd.yaml | 8 +- ...cd-application-controller-statefulset.yaml | 15 + .../argocd-cmd-params-cm.yaml | 6 + .../argocd-repo-server-deployment.yaml | 26 + .../argocd-server-deployment.yaml | 29 + .../controller-deployment/kustomization.yaml | 22 + manifests/ha/base/kustomization.yaml | 9 +- ...cd-application-controller-statefulset.yaml | 6 +- .../base/overlays/argocd-cmd-params-cm.yaml | 6 + .../argocd-repo-server-deployment.yaml | 9 +- .../overlays/argocd-server-deployment.yaml | 8 +- .../ha/base/redis-ha/chart/upstream.yaml | 82 +- manifests/ha/base/redis-ha/chart/values.yaml | 6 +- manifests/ha/base/redis-ha/kustomization.yaml | 2 +- ...deployment-containers-securityContext.yaml | 2 + ...tatefulset-containers-securityContext.yaml | 4 + manifests/ha/install.yaml | 8915 ++++++++++-- manifests/ha/namespace-install.yaml | 419 +- manifests/install.yaml | 8916 ++++++++++-- manifests/namespace-install.yaml | 394 +- mkdocs.yml | 58 +- .../controller/controller.go | 107 +- .../controller/controller_test.go | 197 + notifications_catalog/install.yaml | 49 +- .../templates/app-deployed.yaml | 6 +- .../templates/app-health-degraded.yaml | 6 +- .../templates/app-sync-failed.yaml | 6 +- .../templates/app-sync-running.yaml | 6 +- .../templates/app-sync-status-unknown.yaml | 6 +- .../templates/app-sync-succeeded.yaml | 6 +- .../triggers/on-deployed.yaml | 4 +- .../triggers/on-sync-failed.yaml | 2 +- .../triggers/on-sync-running.yaml | 2 +- .../triggers/on-sync-succeeded.yaml | 2 +- pkg/apiclient/apiclient.go | 42 +- pkg/apiclient/apiclient_test.go | 36 +- pkg/apiclient/application/application.pb.go | 1470 +- .../application/application.pb.gw.go | 141 + .../applicationset/applicationset.pb.go | 229 +- .../applicationset/applicationset.pb.gw.go | 36 + pkg/apiclient/grpcproxy.go | 30 +- pkg/apiclient/repository/repository.pb.go | 191 +- pkg/apiclient/settings/settings.pb.go | 454 +- pkg/apiclient/settings/settings.pb.gw.go | 65 + pkg/apiclient/version/version.pb.go | 104 +- pkg/apis/api-rules/violation_exceptions.list | 14 +- pkg/apis/application/register.go | 2 +- .../v1alpha1/application_defaults.go | 3 + .../v1alpha1/applicationset_types.go | 271 +- .../v1alpha1/applicationset_types_test.go | 48 + pkg/apis/application/v1alpha1/generated.pb.go | 11171 +++++++++++++--- pkg/apis/application/v1alpha1/generated.proto | 307 +- .../application/v1alpha1/openapi_generated.go | 1785 ++- .../application/v1alpha1/repository_types.go | 21 +- pkg/apis/application/v1alpha1/types.go | 507 +- pkg/apis/application/v1alpha1/types_test.go | 265 +- pkg/apis/application/v1alpha1/values.go | 61 + pkg/apis/application/v1alpha1/values_test.go | 81 + .../v1alpha1/zz_generated.deepcopy.go | 650 +- pkg/client/clientset/versioned/clientset.go | 3 +- .../clientset/versioned/fake/register.go | 14 +- .../clientset/versioned/scheme/register.go | 14 +- .../informers/externalversions/factory.go | 79 +- pkg/ratelimiter/ratelimiter.go | 124 + reposerver/apiclient/clientset.go | 10 +- reposerver/apiclient/clientset_test.go | 91 + .../mocks/RepoServerServiceClient.go | 148 +- ...Service_GenerateManifestWithFilesClient.go | 167 + reposerver/apiclient/repository.pb.go | 2377 +++- reposerver/cache/cache.go | 270 +- reposerver/cache/cache_test.go | 520 +- reposerver/cache/mocks/reposervercache.go | 71 + reposerver/gpgwatcher.go | 14 +- reposerver/metrics/githandlers.go | 24 + reposerver/metrics/githandlers_test.go | 122 + reposerver/metrics/metrics.go | 15 + reposerver/repository/chart.go | 30 + reposerver/repository/chart_test.go | 63 + reposerver/repository/lock.go | 2 +- reposerver/repository/repository.go | 849 +- reposerver/repository/repository.proto | 86 +- reposerver/repository/repository_test.go | 1230 +- .../git-files-dirs/app/bar/.hidden/.keep | 0 .../testdata/git-files-dirs/app/bar/.keep | 0 .../git-files-dirs/app/foo/bar/config.yaml | 2 + .../git-files-dirs/app/foo/config.yaml | 2 + .../testdata/git-files-dirs/config.yaml | 2 + .../git-files-dirs/somedir/config.yaml | 2 + .../helm-with-dependencies-alias/Chart.yaml | 7 + .../helm-with-dependencies/Chart.yaml | 7 + .../testdata/invalid-metadata/bad.yaml | 17 + .../nil-metadata-accessors.yaml | 8 + .../testdata/oci-dependencies/Chart.yaml | 2 +- .../testdata/values-files/dir/values.yaml | 1 + reposerver/repository/types.go | 14 + reposerver/server.go | 4 +- .../apps.kruise.io/AdvancedCronJob/health.lua | 36 + .../AdvancedCronJob/health_test.yaml | 17 + .../AdvancedCronJob/testdata/activeJobs.yaml | 30 + .../testdata/lastScheduleTime.yaml | 23 + .../testdata/notScheduled.yaml | 22 + .../AdvancedCronJob/testdata/suspended.yaml | 23 + .../apps.kruise.io/BroadcastJob/health.lua | 32 + .../BroadcastJob/health_test.yaml | 17 + .../BroadcastJob/testdata/failed.yaml | 31 + .../BroadcastJob/testdata/running.yaml | 22 + .../BroadcastJob/testdata/succeeded.yaml | 31 + .../BroadcastJob/testdata/suspended.yaml | 31 + .../apps.kruise.io/CloneSet/health.lua | 33 + .../apps.kruise.io/CloneSet/health_test.yaml | 21 + .../CloneSet/testdata/degraded.yaml | 35 + .../CloneSet/testdata/healthy.yaml | 36 + .../testdata/partition_suspended.yaml | 31 + .../CloneSet/testdata/suspended.yaml | 35 + .../CloneSet/testdata/unknown.yaml | 5 + .../apps.kruise.io/DaemonSet/health.lua | 35 + .../apps.kruise.io/DaemonSet/health_test.yaml | 21 + .../DaemonSet/testdata/degraded.yaml | 34 + .../DaemonSet/testdata/healthy.yaml | 34 + .../testdata/partition_suspended.yaml | 33 + .../DaemonSet/testdata/suspended.yaml | 33 + .../DaemonSet/testdata/unknown.yaml | 5 + .../apps.kruise.io/StatefulSet/health.lua | 35 + .../StatefulSet/health_test.yaml | 21 + .../StatefulSet/testdata/degraded.yaml | 42 + .../StatefulSet/testdata/healthy.yaml | 41 + .../testdata/partition_suspended.yaml | 36 + .../StatefulSet/testdata/suspended.yaml | 36 + .../StatefulSet/testdata/unknown.yaml | 5 + .../DeploymentConfig/health.lua | 4 +- .../apps/DaemonSet/actions/discovery.lua | 2 +- .../actions/testdata/daemonset-restarted.yaml | 2 +- .../DaemonSet/actions/testdata/daemonset.yaml | 2 +- .../apps/Deployment/actions/discovery.lua | 2 +- .../actions/testdata/deployment-pause.yaml | 2 + .../actions/testdata/deployment-resume.yaml | 2 + .../apps/StatefulSet/actions/discovery.lua | 2 +- .../testdata/statefulset-restarted.yaml | 2 +- .../actions/testdata/statefulset.yaml | 2 +- .../AnalysisRun/actions/discovery.lua | 2 +- .../argoproj.io/AnalysisRun/health.lua | 2 +- .../argoproj.io/ApplicationSet/health.lua | 2 +- .../CronWorkflow/actions/action_test.yaml | 7 + .../actions/create-workflow/action.lua | 82 + .../CronWorkflow/actions/discovery.lua | 6 + .../testdata/cronworkflow-without-label.yaml | 31 + .../actions/testdata/cronworkflow.yaml | 34 + .../testdata/workflow-without-label.yaml | 26 + .../actions/testdata/workflow.yaml | 28 + .../argoproj.io/CronWorkflow/health.lua | 2 +- .../argoproj.io/EventBus/health.lua | 21 + .../argoproj.io/EventBus/health_test.yaml | 9 + .../EventBus/testdata/degraded.yaml | 21 + .../EventBus/testdata/healthy.yaml | 19 + .../argoproj.io/Experiment/health.lua | 2 +- .../argoproj.io/Rollout/actions/discovery.lua | 6 +- .../Rollout/actions/promote-full/action.lua | 2 +- .../argoproj.io/Rollout/health.lua | 18 +- .../WorkflowTemplate/actions/action_test.yaml | 4 + .../actions/create-workflow/action.lua | 39 + .../WorkflowTemplate/actions/discovery.lua | 6 + .../actions/testdata/workflow.yaml | 16 + .../actions/testdata/workflowtemplate.yaml | 24 + .../batch/CronJob/actions/action_test.yaml | 4 + .../CronJob/actions/create-job/action.lua | 64 + .../batch/CronJob/actions/discovery.lua | 6 + .../CronJob/actions/testdata/cronjob.yaml | 33 + .../batch/CronJob/actions/testdata/job.yaml | 30 + .../beat.k8s.elastic.co/Beat/health.lua | 31 + .../beat.k8s.elastic.co/Beat/health_test.yaml | 29 + .../Beat/testdata/invalid.yaml | 12 + .../Beat/testdata/progressing.yaml | 11 + .../Beat/testdata/ready_green.yaml | 13 + .../Beat/testdata/ready_red.yaml | 10 + .../Beat/testdata/ready_yellow.yaml | 11 + .../testdata/ready_yellow_single_node.yaml | 10 + .../Beat/testdata/unknown.yaml | 8 + .../bitnami.com/SealedSecret/health.lua | 2 +- .../cassandra.rook.io/Cluster/health.lua | 4 +- .../Cluster/testdata/healthy.yaml | 8 +- .../Cluster/testdata/progressing.yaml | 4 +- .../cdi.kubevirt.io/DataVolume/health.lua | 2 +- .../cert-manager.io/Certificate/health.lua | 7 +- .../Certificate/health_test.yaml | 4 + .../testdata/progressing_issuing_last.yaml | 36 + .../cert-manager.io/ClusterIssuer/health.lua | 21 + .../ClusterIssuer/health_test.yaml | 14 + .../testdata/degraded_acmeFailed.yaml | 26 + .../testdata/healthy_registered.yaml | 25 + .../testdata/progressing_noStatus.yaml | 16 + .../cert-manager.io/Issuer/health.lua | 2 +- .../Issuer/testdata/degraded_acmeFailed.yaml | 2 +- .../Issuer/testdata/healthy_registered.yaml | 2 +- .../Issuer/testdata/progressing_noStatus.yaml | 2 +- .../certmanager.k8s.io/Certificate/health.lua | 2 +- .../certmanager.k8s.io/Issuer/health.lua | 2 +- .../Issuer/testdata/degraded_acmeFailed.yaml | 2 +- .../Issuer/testdata/healthy_registered.yaml | 2 +- .../Issuer/testdata/progressing_noStatus.yaml | 2 +- .../Distribution/health.lua | 42 + .../Distribution/health_test.yaml | 37 + .../testdata/degraded_reconcileError.yaml | 96 + .../Distribution/testdata/healthy.yaml | 92 + .../Distribution/testdata/progressing.yaml | 92 + .../testdata/progressing_creating.yaml | 92 + .../testdata/progressing_noStatus.yaml | 82 + .../testdata/progressing_noavailable.yaml | 88 + .../Distribution/testdata/suspended.yaml | 94 + .../CloudFunctionsFunction/health.lua | 2 +- .../CloudSchedulerJob/health.lua | 2 +- .../cluster.x-k8s.io/Cluster/health.lua | 2 +- .../cluster.x-k8s.io/Machine/health.lua | 4 +- .../MachineDeployment/health.lua | 2 +- .../MachineHealthCheck/health.lua | 2 +- .../ComputeDisk/health.lua | 2 +- .../db.atlasgo.io/AtlasMigration/health.lua | 37 + .../AtlasMigration/health_test.yaml | 13 + .../AtlasMigration/testdata/degraded.yaml | 29 + .../AtlasMigration/testdata/healthy.yaml | 30 + .../AtlasMigration/testdata/progressing.yaml | 30 + .../db.atlasgo.io/AtlasSchema/health.lua | 37 + .../AtlasSchema/health_test.yaml | 13 + .../AtlasSchema/testdata/degraded.yaml | 38 + .../AtlasSchema/testdata/healthy.yaml | 39 + .../AtlasSchema/testdata/progressing.yaml | 35 + .../Elasticsearch/health.lua | 4 +- .../Elasticsearch/health_test.yaml | 2 +- resource_customizations/embed.go | 1 + .../ClusterExternalSecret/health.lua | 25 + .../ClusterExternalSecret/health_test.yaml | 21 + .../testdata/healthy.yaml | 37 + .../testdata/notready.yaml | 38 + .../partiallyready-multiple-conditions.yaml | 43 + .../testdata/partiallyready.yaml | 40 + .../testdata/progressing.yaml | 30 + .../ClusterSecretStore/health.lua | 20 + .../ClusterSecretStore/health_test.yaml | 9 + .../ClusterSecretStore/testdata/degraded.yaml | 16 + .../ClusterSecretStore/testdata/healthy.yaml | 17 + .../ExternalSecret/actions/action_test.yaml | 4 + .../ExternalSecret/actions/discovery.lua | 3 + .../ExternalSecret/actions/refresh/action.lua | 6 + .../testdata/external-secret-updated.yaml | 56 + .../actions/testdata/external-secret.yaml | 54 + .../ExternalSecret/health.lua | 2 +- .../PushSecret/actions/action_test.yaml | 4 + .../PushSecret/actions/discovery.lua | 3 + .../PushSecret/actions/push/action.lua | 6 + .../actions/testdata/push-secret-updated.yaml | 41 + .../actions/testdata/push-secret.yaml | 39 + .../external-secrets.io/PushSecret/health.lua | 20 + .../PushSecret/health_test.yaml | 13 + .../PushSecret/testdata/degraded.yaml | 33 + .../PushSecret/testdata/healthy.yaml | 39 + .../PushSecret/testdata/progressing.yaml | 24 + .../SecretStore/health.lua | 2 +- .../flagger.app/Canary/health.lua | 6 +- .../FlinkDeployment/health.lua | 4 +- .../FlinkDeployment/health_test.yaml | 10 +- ...nning.yaml => healthy_running_v0.1.x.yaml} | 0 .../testdata/healthy_running_v1.x.yaml | 11 + ...ded.yaml => healthy_suspended_v0.1.x.yaml} | 0 .../testdata/healthy_suspended_v1.x.yaml | 11 + .../IAMPartialPolicy/health.lua | 2 +- .../IAMPolicy/health.lua | 2 +- .../IAMPolicyMember/health.lua | 2 +- .../IAMServiceAccount/health.lua | 2 +- .../Iamrole/health.lua | 33 + .../Iamrole/health_test.yaml | 20 + .../Iamrole/testdata/degraded_error.yaml | 29 + .../degraded_rolesMaxLimitReached.yaml | 26 + .../Iamrole/testdata/healthy.yaml | 27 + .../testdata/progressing_noStatus.yaml | 20 + .../install.istio.io/IstioOperator/health.lua | 2 +- .../jaegertracing.io/Jaeger/health.lua | 2 +- .../KafkaCluster/health.lua | 25 +- .../KafkaCluster/health_test.yaml | 2 +- .../KafkaCluster/testdata/healthy.yaml | 6 +- .../kafka.strimzi.io/Kafka/health.lua | 2 +- .../kafka.strimzi.io/Kafka/health_test.yaml | 1 - .../kafka.strimzi.io/KafkaConnect/health.lua | 2 +- .../KafkaConnect/testdata/degraded.yaml | 2 +- .../KafkaConnect/testdata/healthy.yaml | 2 +- .../testdata/progressing_noStatus.yaml | 4 +- .../kafka.strimzi.io/KafkaTopic/health.lua | 2 +- .../kafka.strimzi.io/KafkaUser/health.lua | 2 +- .../kiali.io/Kiali/health.lua | 2 +- .../ExternalSecret/health.lua | 2 +- .../kubevirt.io/VirtualMachine/health.lua | 4 +- .../VirtualMachineInstance/health.lua | 2 +- .../KeptnAppVersion/health.lua | 2 +- .../KeptnEvaluation/health.lua | 2 +- .../lifecycle.keptn.sh/KeptnTask/health.lua | 2 +- .../KeptnWorkloadInstance/health.lua | 2 +- .../mariadb.mmontes.io/MariaDB/health.lua | 25 + .../MariaDB/health_test.yaml | 25 + .../MariaDB/testdata/mariadb_error.yaml | 27 + .../MariaDB/testdata/no_status.yaml | 22 + .../MariaDB/testdata/restore_complete.yaml | 32 + .../testdata/restore_not_complete.yaml | 32 + .../testdata/statefulset_not_ready.yaml | 27 + .../MariaDB/testdata/statefulset_ready.yaml | 27 + .../minio.min.io/Tenant/health.lua | 2 +- .../Prometheus/health.lua | 2 +- .../ManagedCertificate/health.lua | 2 +- .../OnePasswordItem/health.lua | 2 +- .../KnativeEventing/health.lua | 8 +- .../KnativeServing/health.lua | 8 +- .../IngressController/health.lua | 31 + .../IngressController/health_test.yaml | 17 + .../IngressController/testdata/degraded.yaml | 103 + .../IngressController/testdata/healthy.yaml | 93 + .../testdata/progressing_initialization.yaml | 36 + .../testdata/progressing_pod_rollout.yaml | 101 + .../Subscription/health.lua | 8 +- .../pkg.crossplane.io/Provider/health.lua | 6 +- .../platform.confluent.io/Connect/health.lua | 2 +- .../ControlCenter/health.lua | 2 +- .../platform.confluent.io/Kafka/health.lua | 2 +- .../platform.confluent.io/KsqlDB/health.lua | 2 +- .../SchemaRegistry/health.lua | 2 +- .../Zookeeper/health.lua | 2 +- .../DNSSDServiceInstance/health.lua | 29 + .../DNSSDServiceInstance/health_test.yaml | 29 + .../testdata/degraded_advertiseError.yaml | 35 + .../testdata/degraded_notAdopted.yaml | 35 + .../testdata/degraded_unadvertiseError.yaml | 35 + .../testdata/healthy.yaml | 35 + .../testdata/progressing_negativeBrowse.yaml | 35 + .../testdata/progressing_negativeLookup.yaml | 35 + .../testdata/unknown_discoveryError.yaml | 35 + .../PubSubSubscription/health.lua | 2 +- .../PubSubTopic/health.lua | 2 +- .../PerconaXtraDBCluster/health.lua | 38 + .../PerconaXtraDBCluster/health_test.yaml | 25 + .../PerconaXtraDBCluster/testdata/error.yaml | 24 + .../testdata/initializing.yaml | 22 + .../PerconaXtraDBCluster/testdata/paused.yaml | 22 + .../PerconaXtraDBCluster/testdata/ready.yaml | 22 + .../testdata/stopping.yaml | 22 + .../testdata/unknown.yaml | 22 + .../Project/health.lua | 2 +- .../rollouts.kruise.io/Rollout/health.lua | 31 + .../Rollout/health_test.yaml | 17 + .../Rollout/testdata/degraded.yaml | 50 + .../Rollout/testdata/healthy.yaml | 56 + .../Rollout/testdata/progressing.yaml | 48 + .../Rollout/testdata/suspended.yaml | 50 + .../route.openshift.io/Route/health.lua | 8 +- .../ResourceRecordSet/health_test.yaml | 25 + .../ResourceRecordSet/heatlh.lua | 41 + .../testdata/degraded_reconcileError.yaml | 35 + .../ResourceRecordSet/testdata/healthy.yaml | 29 + .../testdata/progressing_creating.yaml | 29 + .../testdata/progressing_noStatus.yaml | 19 + .../testdata/suspended_reconcilePaused.yaml | 27 + .../serving.knative.dev/Service/health.lua | 8 +- .../InferenceService/health.lua | 40 + .../InferenceService/health_test.yaml | 13 + .../InferenceService/testdata/degraded.yaml | 30 + .../InferenceService/testdata/healthy.yaml | 25 + .../testdata/progressing.yaml | 28 + .../VolumeSnapshot/health.lua | 18 + .../VolumeSnapshot/health_test.yaml | 14 + .../VolumeSnapshot/testdata/bad.yaml | 14 + .../VolumeSnapshot/testdata/good.yaml | 15 + .../VolumeSnapshot/testdata/initializing.yaml | 7 + .../VolumeSnapshotContent/health.lua | 18 + .../VolumeSnapshotContent/health_test.yaml | 13 + .../VolumeSnapshotContent/testdata/bad.yaml | 12 + .../VolumeSnapshotContent/testdata/good.yaml | 20 + .../testdata/initializing.yaml | 7 + .../SparkApplication/health.lua | 77 +- .../SparkApplication/health_test.yaml | 16 + .../testdata/healthy_dynamic_alloc.yaml | 37 + .../healthy_dynamic_alloc_dstream.yaml | 35 + .../healthy_dynamic_alloc_operator_api.yaml | 38 + ...thy_dynamic_alloc_without_spec_config.yaml | 31 + .../spot.io/SpotDeployment/health.lua | 2 +- .../SQLDatabase/health.lua | 2 +- .../SQLInstance/health.lua | 2 +- .../ClusterStackInstall/health.lua | 2 +- .../StorageBucket/health.lua | 2 +- .../StorageBucketAccessControl/health.lua | 2 +- .../health.lua | 2 +- .../tower.ansible.com/AnsibleJob/health.lua | 25 + .../AnsibleJob/health_test.yaml | 37 + .../testdata/degraded_canceled.yaml | 27 + .../AnsibleJob/testdata/degraded_error.yaml | 27 + .../AnsibleJob/testdata/degraded_failed.yaml | 27 + .../AnsibleJob/testdata/healthy.yaml | 27 + .../AnsibleJob/testdata/progressing_new.yaml | 25 + .../testdata/progressing_noStatus.yaml | 17 + .../testdata/progressing_pending.yaml | 25 + .../testdata/progressing_running.yaml | 25 + .../testdata/progressing_waiting.yaml | 25 + .../TridentBackendConfig/health.lua | 2 +- .../TridentOrchestrator/health.lua | 2 +- .../ClusterResourceBinding/health.lua | 2 +- .../ResourceBinding/health.lua | 2 +- .../ZookeeperCluster/health.lua | 2 +- server/application/application.go | 833 +- server/application/application.proto | 34 +- server/application/application_test.go | 1530 ++- server/application/broadcaster.go | 8 + server/application/mocks/Broadcaster.go | 66 + server/application/terminal.go | 65 +- server/application/terminal_test.go | 11 +- server/application/websocket.go | 105 +- server/application/websocket_test.go | 46 + server/applicationset/applicationset.go | 153 +- server/applicationset/applicationset.proto | 6 + server/applicationset/applicationset_test.go | 476 + server/badge/badge.go | 118 +- server/badge/badge_test.go | 265 +- server/cache/cache.go | 3 +- server/cluster/cluster.go | 103 +- server/cluster/cluster_test.go | 251 +- server/deeplinks/deeplinks.go | 81 +- server/deeplinks/deeplinks_test.go | 123 +- server/extension/extension.go | 625 +- server/extension/extension_test.go | 661 +- .../mocks/ExtensionMetricsRegistry.go | 38 + server/extension/mocks/ProjectGetter.go | 74 + server/extension/mocks/RbacEnforcer.go | 41 + server/logout/logout.go | 2 +- server/logout/logout_test.go | 8 +- server/metrics/metrics.go | 37 +- server/notification/notification_test.go | 4 +- server/project/project.go | 8 +- server/rbacpolicy/rbacpolicy.go | 6 +- server/repository/repository.go | 37 +- server/repository/repository.proto | 2 + server/repository/repository_test.go | 238 +- server/server.go | 283 +- server/server_norace_test.go | 14 +- server/server_test.go | 373 +- server/settings/settings.go | 55 +- server/settings/settings.proto | 15 +- server/version/version.go | 2 + server/version/version.proto | 1 + test/certificates/ssh_known_hosts | 4 +- test/cmp/plugin.yaml | 2 + test/container/Dockerfile | 69 +- test/container/Procfile | 10 +- test/e2e/accounts_test.go | 3 +- test/e2e/api_versions_test.go | 57 + test/e2e/app_deletion_test.go | 15 + test/e2e/app_management_ns_test.go | 70 +- test/e2e/app_management_test.go | 459 +- test/e2e/app_multiple_sources_test.go | 15 +- test/e2e/app_namespaces_test.go | 4 +- test/e2e/app_skipreconcile_test.go | 45 + test/e2e/app_sync_options_test.go | 61 + test/e2e/applicationset_test.go | 1228 +- test/e2e/cli_test.go | 2 +- test/e2e/cluster_generator_test.go | 122 +- test/e2e/cluster_objects_test.go | 34 +- test/e2e/cluster_test.go | 38 +- test/e2e/clusterdecisiongenerator_e2e_test.go | 130 +- test/e2e/custom_tool_test.go | 212 +- ...delarative_test.go => declarative_test.go} | 0 test/e2e/deployment_test.go | 311 + test/e2e/fixture/account/actions.go | 2 +- test/e2e/fixture/app/actions.go | 71 +- test/e2e/fixture/app/consequences.go | 5 - test/e2e/fixture/app/context.go | 19 +- test/e2e/fixture/app/expectation.go | 60 +- test/e2e/fixture/applicationsets/actions.go | 146 +- .../fixture/applicationsets/consequences.go | 30 +- test/e2e/fixture/applicationsets/context.go | 8 +- .../fixture/applicationsets/expectation.go | 27 +- .../fixture/applicationsets/utils/fixture.go | 133 +- test/e2e/fixture/cluster/actions.go | 4 +- test/e2e/fixture/cluster/context.go | 18 +- test/e2e/fixture/fixture.go | 229 +- test/e2e/fixture/http.go | 11 +- test/e2e/fixture/project/actions.go | 4 +- test/e2e/git_submodule_test.go | 22 + test/e2e/helm_test.go | 49 +- test/e2e/hook_test.go | 20 + test/e2e/jsonnet_test.go | 2 +- test/e2e/kustomize_test.go | 68 +- test/e2e/matrix_e2e_test.go | 311 +- test/e2e/merge_e2e_test.go | 161 +- test/e2e/multiarch-container/Dockerfile | 2 +- test/e2e/notification_test.go | 8 +- test/e2e/project_management_test.go | 92 +- test/e2e/repo_creds_test.go | 6 +- test/e2e/repo_management_test.go | 34 + test/e2e/selective_sync_test.go | 2 +- test/e2e/sync_options_test.go | 23 + test/e2e/sync_waves_test.go | 45 + .../cluster-role-hook/cluster-role.yaml | 15 + .../pod.yaml | 0 .../testdata/cluster-role/cluster-role.yaml | 5 +- test/e2e/testdata/cmp-fileName/plugin.yaml | 2 +- test/e2e/testdata/cmp-gitcreds/plugin.yaml | 10 + .../testdata/cmp-gitcreds/subdir/special.yaml | 0 .../testdata/cmp-gitcredstemplate/plugin.yaml | 10 + .../cmp-gitcredstemplate/subdir/special.yaml | 0 test/e2e/testdata/cmp-kustomize/plugin.yaml | 10 + .../cmp-preserve-file-mode/plugin.yaml | 11 + .../testdata/cmp-preserve-file-mode/script.sh | 8 + .../crashing-guestbook/kustomization.yaml | 4 +- .../crd-does-not-exist-instance.yaml | 7 + .../crd-version-differences/crd-v1alpha1.yaml | 35 + .../crd-v1alpha2-instance.yaml | 7 + .../crd-wronggroup-instance.yaml | 7 + .../deprecated-extensions/deployment.yaml | 2 +- .../testdata/git-submodule/submodule-pod.yaml | 4 +- .../guestbook-ui-deployment.yaml | 23 + .../guestbook-ui-namespace.yaml | 9 + .../guestbook-ui-svc.yaml | 10 + .../Chart.yaml | 11 + .../Chart.yaml | 6 +- .../hook-custom-health/kustomization.yaml | 4 +- .../local/kustomization.yaml | 2 +- .../remote/kustomization.yaml | 2 +- .../deployment-with-namespace.yaml | 2 +- .../testdata/multi-namespace/deployment.yaml | 2 +- .../networking/guestbook-ui-svc-ingress.yaml | 4 +- test/e2e/testdata/post-delete-hook/hook.yaml | 14 + .../testdata/resource-actions/cron-job.yaml | 19 + .../local/kustomization.yaml | 2 +- .../remote/kustomization.yaml | 2 +- .../testdata/syncwaves-prune-order/README.md | 15 + .../testdata/syncwaves-prune-order/pod.yaml | 41 + .../testdata/syncwaves-prune-order/rbac.yaml | 37 + test/e2e/testdata2/cmp-symlink/plugin.yaml | 13 + .../guestbook-ui-deployment.yaml | 1 + .../guestbook-ui-svc.yaml | 10 + .../kustomization.yaml | 6 + .../guestbook-ui-deployment.yaml | 1 + .../guestbook-ui-svc.yaml | 1 + .../kustomization.yaml | 1 + test/e2e/testdata2/guestbook-symlink-folder | 1 + .../guestbook/guestbook-ui-deployment.yaml | 23 + .../testdata2/guestbook/guestbook-ui-svc.yaml | 10 + .../testdata2/guestbook/kustomization.yaml | 6 + test/fixture/testrepos/start-git.sh | 2 +- test/manifests/base/kustomization.yaml | 6 +- test/manifests/cmp/kustomization.yaml | 6 +- test/manifests_test.go | 2 +- test/remote/Dockerfile | 32 +- test/remote/Makefile | 4 +- test/remote/README.md | 4 +- test/testdata.go | 19 +- test/testutil.go | 30 +- tools/cmd-docs/main.go | 22 +- ui-test/Dockerfile | 2 +- ui-test/osv-scanner.toml | 15 + ui-test/package.json | 2 +- ui-test/yarn.lock | 20 +- ui/.nvmrc | 2 +- ui/embed.go | 1 + ui/jest.config.js | 20 +- ui/osv-scanner.toml | 3 + ui/package.json | 38 +- ui/src/app/app.tsx | 13 +- .../application-conditions.scss | 6 + .../application-conditions.tsx | 4 +- .../application-create-panel.tsx | 21 +- .../application-deployment-history.tsx | 11 +- .../initiated-by.tsx | 6 + .../revision-metadata-rows.tsx | 46 +- .../application-details.scss | 198 +- .../application-details.tsx | 505 +- .../application-resource-list.scss | 13 + .../application-resource-list.tsx | 225 +- .../application-fullscreen-logs.tsx | 7 - .../application-node-info.scss | 39 +- .../application-node-info.tsx | 193 +- .../readiness-gates-not-passed-warning.scss | 12 + .../readiness-gates-not-passed-warning.tsx | 44 + .../application-parameters.tsx | 275 +- .../application-pod-view/pod-tooltip.tsx | 51 + .../application-pod-view/pod-view.scss | 4 + .../application-pod-view/pod-view.tsx | 32 +- .../application-resource-tree.scss | 61 +- .../application-resource-tree.tsx | 423 +- .../application-resources-diff.scss | 7 +- .../application-status-panel.scss | 42 +- .../application-status-panel.tsx | 53 +- .../application-summary.tsx | 165 +- .../application-sync-options.tsx | 1 + .../application-sync-panel.tsx | 38 +- .../components/application-urls.test.ts | 68 +- .../components/application-urls.tsx | 18 +- .../applications-list/applications-filter.tsx | 4 +- .../applications-list/applications-list.scss | 2 +- .../applications-list/applications-list.tsx | 15 +- .../applications-list/applications-tiles.scss | 18 + .../applications-list/applications-tiles.tsx | 334 +- .../applications-refresh-panel.tsx | 32 +- .../components/filter/filter.scss | 28 +- .../applications/components/filter/filter.tsx | 11 +- .../pod-logs-viewer/auto-scroll-button.tsx | 14 + .../pod-logs-viewer/container-selector.tsx | 40 + .../pod-logs-viewer/copy-logs-button.tsx | 25 + .../dark-mode-toggle-button.tsx | 19 + .../pod-logs-viewer/download-logs-button.tsx | 16 + .../pod-logs-viewer/follow-toggle-button.tsx | 7 + .../pod-logs-viewer/fullscreen-button.tsx | 26 + .../components/pod-logs-viewer/log-loader.ts | 4 + .../pod-logs-viewer/log-message-filter.tsx | 9 + .../pod-logs-viewer/pod-logs-viewer.scss | 62 +- .../pod-logs-viewer/pod-logs-viewer.tsx | 656 +- .../pod-names-toggle-button.tsx | 7 + .../show-previous-logs-toggle-button.tsx | 12 + .../since-seconds-selector.tsx | 24 + .../pod-logs-viewer/tail-selector.tsx | 15 + .../timestamps-toggle-button.tsx | 23 + .../pod-logs-viewer/wrap-lines-button.tsx | 17 + .../pod-terminal-viewer.scss | 4 + .../pod-terminal-viewer.tsx | 10 +- .../resource-details/resource-details.tsx | 11 +- ui/src/app/applications/components/utils.scss | 7 +- ui/src/app/applications/components/utils.tsx | 170 +- ui/src/app/help/components/help.tsx | 2 +- ui/src/app/index.tsx | 2 + ui/src/app/login/components/login.tsx | 17 +- ui/src/app/login/components/pkce-verify.scss | 8 + ui/src/app/login/components/pkce-verify.tsx | 45 + ui/src/app/login/components/utils.ts | 151 + .../clusters-list/cluster-list.scss | 25 + .../clusters-list/clusters-list.tsx | 46 +- .../project-details/project-details.tsx | 59 +- .../project-details/resource-lists-panel.tsx | 32 + .../components/repo-details/repo-details.tsx | 4 +- .../components/repos-list/repos-list.tsx | 54 +- .../components/array-input/array-input.tsx | 245 +- .../components/badge-panel/badge-panel.tsx | 7 +- ui/src/app/shared/components/button.tsx | 37 + ui/src/app/shared/components/colors.ts | 1 + ui/src/app/shared/components/deep-links.tsx | 18 +- .../editable-panel/editable-panel.tsx | 11 +- .../components/events-list/events-list.tsx | 4 +- ui/src/app/shared/components/icon.ts | 1 + .../app/shared/components/layout/layout.tsx | 26 +- .../app/shared/components/monaco-editor.tsx | 17 +- .../shared/components/paginate/paginate.tsx | 40 +- ui/src/app/shared/components/revision.tsx | 2 +- ui/src/app/shared/components/spacer.tsx | 3 + .../app/shared/components/toggle-button.tsx | 40 + ui/src/app/shared/components/urls.test.ts | 2 +- ui/src/app/shared/components/urls.ts | 6 + .../version-info/version-info-panel.tsx | 2 +- ui/src/app/shared/models.ts | 34 +- .../shared/services/applications-service.ts | 78 +- ui/src/app/shared/services/auth-service.ts | 6 +- .../app/shared/services/extensions-service.ts | 33 +- ui/src/app/shared/services/index.ts | 2 +- ui/src/app/shared/services/repo-service.ts | 16 +- ui/src/app/shared/services/requests.ts | 8 +- .../services/view-preferences-service.ts | 8 +- ui/src/app/sidebar/sidebar.scss | 99 +- ui/src/app/sidebar/sidebar.tsx | 74 +- ui/src/app/tsconfig.json | 1 + ui/src/app/ui-banner/ui-banner.scss | 3 +- ui/src/app/ui-banner/ui-banner.tsx | 120 +- ui/src/app/webpack.config.js | 7 +- ui/yarn.lock | 1728 +-- util/app/discovery/discovery.go | 60 +- util/app/discovery/discovery_test.go | 14 +- util/argo/argo.go | 184 +- util/argo/argo_test.go | 249 +- util/argo/audit_logger.go | 60 +- util/argo/audit_logger_test.go | 6 +- util/argo/diff/diff.go | 87 +- .../argo/managedfields/managed_fields_test.go | 2 +- util/argo/normalizers/corev1_known_types.go | 17 +- util/argo/normalizers/diff_normalizer.go | 18 +- util/argo/normalizers/diff_normalizer_test.go | 55 +- util/argo/normalizers/diffing_known_types.txt | 5 + .../argo/normalizers/knowntypes_normalizer.go | 4 + .../normalizers/knowntypes_normalizer_test.go | 34 +- util/argo/resource_tracking.go | 69 +- util/argo/resource_tracking_test.go | 19 +- util/cache/appstate/cache.go | 5 +- util/cache/cache.go | 202 +- util/cache/cache_test.go | 96 +- util/cache/client.go | 15 +- util/cache/inmemory.go | 21 +- util/cache/mocks/cacheclient.go | 74 + util/cache/redis.go | 42 +- util/cache/redis_hook.go | 34 +- util/cache/redis_hook_test.go | 35 +- util/cache/redis_test.go | 94 +- util/cache/twolevelclient.go | 8 + util/cert/cert.go | 4 +- util/cert/cert_test.go | 38 +- util/cli/cli.go | 3 + util/clusterauth/clusterauth_test.go | 2 +- util/cmp/stream.go | 15 +- util/cmp/stream_test.go | 2 +- util/collections/maps.go | 13 + util/collections/maps_test.go | 58 + util/config/env_test.go | 8 +- util/config/reader.go | 10 +- util/db/certificate.go | 7 +- util/db/certificate_test.go | 12 +- util/db/cluster.go | 31 +- util/db/cluster_test.go | 61 +- util/db/db.go | 26 + util/db/db_test.go | 25 + util/db/gpgkeys_test.go | 11 +- util/db/helmrepository.go | 19 +- util/db/mocks/ArgoDB.go | 149 +- util/db/repository.go | 42 +- util/db/repository_legacy.go | 35 +- util/db/repository_secrets.go | 21 +- util/db/secrets.go | 25 +- util/dex/config.go | 68 +- util/dex/dex.go | 3 +- util/dex/dex_test.go | 22 +- util/env/env.go | 80 +- util/env/env_test.go | 57 +- util/exec/exec.go | 25 +- util/exec/exec_test.go | 22 +- util/git/client.go | 162 +- util/git/client_test.go | 68 +- util/git/creds.go | 59 +- util/git/creds_test.go | 182 +- util/git/git.go | 10 +- util/git/git_test.go | 76 +- util/git/mocks/Client.go | 79 +- util/git/workaround.go | 20 + util/gpg/gpg.go | 26 +- util/gpg/gpg_test.go | 121 +- util/grpc/grpc.go | 10 +- util/grpc/trace.go | 33 + util/healthz/healthz_test.go | 4 +- util/helm/client.go | 229 +- util/helm/client_test.go | 198 +- util/helm/cmd.go | 84 +- util/helm/cmd_test.go | 12 + util/helm/helm.go | 7 +- util/helm/helm_test.go | 1 + util/helm/index_test.go | 24 + util/helm/mocks/Client.go | 4 +- util/helm/tags.go | 6 +- .../testdata/minio/templates/deployment.yaml | 2 +- .../redis/templates/metrics-deployment.yaml | 2 +- .../templates/redis-slave-deployment.yaml | 2 +- util/http/http.go | 91 +- util/http/http_test.go | 12 +- util/io/bytereadseeker_test.go | 2 +- util/io/files/tar.go | 15 +- util/io/files/tar_test.go | 25 +- util/io/files/testdata/executable/script.sh | 1 + util/io/files/util.go | 21 +- util/kube/kube.go | 60 +- util/kube/kube_test.go | 59 +- util/kube/portforwarder.go | 2 +- util/kube/testdata/svc-with-invalid-data.yaml | 17 + util/kustomize/kustomize.go | 196 +- util/kustomize/kustomize_test.go | 287 +- .../kustomization_yaml/statefulset.yaml | 2 +- .../components/deployment.yaml | 21 + .../components/kustomization.yaml | 4 + .../kustomization.yaml | 5 + .../deployment.yaml | 21 + .../kustomization.yaml | 2 + .../label_without_selector/deployment.yaml | 22 + .../label_without_selector/kustomization.yaml | 2 + util/localconfig/localconfig.go | 14 +- util/log/logrus.go | 19 +- util/log/logrus_test.go | 17 +- util/lua/custom_actions_test.go | 165 +- util/lua/health_test.go | 3 +- util/lua/impacted_resource.go | 50 + util/lua/lua.go | 60 +- util/lua/lua_test.go | 310 +- util/manifeststream/stream.go | 11 +- util/notification/argocd/service.go | 21 +- util/notification/expression/repo/repo.go | 14 +- .../expression/shared/appdetail.go | 29 +- .../expression/shared/appdetail_test.go | 30 + util/notification/settings/legacy.go | 2 +- util/notification/settings/legacy_test.go | 4 +- util/notification/settings/settings.go | 32 +- util/notification/settings/settings_test.go | 92 + util/oidc/oidc.go | 193 +- util/oidc/oidc_test.go | 320 +- util/oidc/provider.go | 87 +- util/proxy/proxy_test.go | 7 +- util/rbac/rbac.go | 50 +- util/rbac/rbac_test.go | 86 +- util/security/jwt.go | 80 + util/security/jwt_test.go | 56 + util/security/rbac.go | 4 +- util/security/rbac_test.go | 2 +- util/session/sessionmanager.go | 77 +- util/session/sessionmanager_test.go | 413 +- util/session/state.go | 2 +- util/settings/resources_filter.go | 1 - util/settings/settings.go | 420 +- util/settings/settings_test.go | 281 +- util/swagger/swagger_test.go | 2 +- util/test/testutil.go | 80 +- util/tls/tls.go | 10 +- util/tls/tls_test.go | 49 +- util/trace/trace.go | 30 +- .../testdata/azuredevops-git-push-event.json | 107 + util/webhook/webhook.go | 145 +- util/webhook/webhook_test.go | 199 +- 1638 files changed, 175017 insertions(+), 60894 deletions(-) create mode 100644 .github/cherry-pick-bot.yml create mode 100644 .github/pr-title-checker-config.json create mode 100644 .github/workflows/README.md create mode 100644 .github/workflows/image-reuse.yaml create mode 100644 .github/workflows/init-release.yaml create mode 100644 .github/workflows/scorecard.yaml create mode 100644 .goreleaser.yaml create mode 100644 CODEOWNERS create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY-INSIGHTS.yml create mode 100644 applicationset/controllers/requeue_after_test.go create mode 100644 applicationset/controllers/templatePatch.go create mode 100644 applicationset/controllers/templatePatch_test.go create mode 100644 applicationset/examples/applications-sync-policies/create-only.yaml create mode 100644 applicationset/examples/applications-sync-policies/create-update.yaml create mode 100644 applicationset/examples/applications-sync-policies/guestbook/engineering-dev/guestbook-ui-deployment.yaml create mode 100644 applicationset/examples/applications-sync-policies/guestbook/engineering-dev/guestbook-ui-svc.yaml create mode 100644 applicationset/examples/applications-sync-policies/guestbook/engineering-prod/guestbook-ui-deployment.yaml create mode 100644 applicationset/examples/applications-sync-policies/guestbook/engineering-prod/guestbook-ui-svc.yaml create mode 100644 applicationset/examples/list-generator/list-elementsYaml-example.yaml create mode 100644 applicationset/examples/scm-provider-generator/scm-provider-example-fasttemplate-gitlab.yaml create mode 100644 applicationset/generators/plugin.go create mode 100644 applicationset/generators/plugin_test.go create mode 100644 applicationset/generators/scm_utils.go create mode 100644 applicationset/generators/value_interpolation.go create mode 100644 applicationset/generators/value_interpolation_test.go create mode 100644 applicationset/services/internal/http/client.go create mode 100644 applicationset/services/internal/http/client_options.go create mode 100644 applicationset/services/internal/http/client_test.go create mode 100644 applicationset/services/mocks/Repos.go create mode 100644 applicationset/services/mocks/RepositoryDB.go create mode 100644 applicationset/services/plugin/plugin_service.go create mode 100644 applicationset/services/plugin/plugin_service_test.go create mode 100644 applicationset/services/plugin/utils.go create mode 100644 applicationset/services/plugin/utils_test.go create mode 100644 applicationset/services/pull_request/azure_devops.go create mode 100644 applicationset/services/pull_request/azure_devops_test.go create mode 100644 applicationset/services/pull_request/bitbucket_cloud.go create mode 100644 applicationset/services/pull_request/bitbucket_cloud_test.go create mode 100644 applicationset/services/scm_provider/aws_codecommit.go create mode 100644 applicationset/services/scm_provider/aws_codecommit/mocks/AWSCodeCommitClient.go create mode 100644 applicationset/services/scm_provider/aws_codecommit/mocks/AWSTaggingClient.go create mode 100644 applicationset/services/scm_provider/aws_codecommit_test.go create mode 100644 applicationset/utils/createOrUpdate_test.go create mode 100644 applicationset/utils/selector.go create mode 100644 applicationset/utils/template_functions.go create mode 100644 applicationset/webhook/testdata/azuredevops-pull-request.json create mode 100644 applicationset/webhook/testdata/azuredevops-push.json create mode 100644 applicationset/webhook/testdata/github-pull-request-labeled-event.json create mode 100644 cmd/argocd-k8s-auth/commands/azure.go create mode 100644 cmd/argocd/commands/relogin_test.go create mode 100644 cmd/argocd/commands/tree.go create mode 100644 cmd/argocd/commands/tree_test.go create mode 100644 cmpserver/plugin/config_test.go create mode 100644 common/common_test.go create mode 100644 controller/hook.go delete mode 100644 controller/metrics/workqueue.go create mode 100644 controller/sharding/cache.go create mode 100644 controller/sharding/cache_test.go create mode 100644 controller/sharding/shuffle_test.go create mode 100644 controller/testdata/diff-cache.yaml delete mode 100644 docs/assets/api-management.png create mode 100644 docs/assets/argocd-arch-authn-authz.jpg create mode 100644 docs/assets/argocd-components.png create mode 100644 docs/assets/argocd-core-components.png create mode 100644 docs/assets/azure-devops-webhook-config.png create mode 100644 docs/assets/extra_info-1.png create mode 100644 docs/assets/extra_info-2.png create mode 100644 docs/assets/extra_info.png delete mode 100644 docs/assets/groups-claim.png delete mode 100644 docs/assets/groups-scope.png create mode 100644 docs/assets/identity-center-1.png create mode 100644 docs/assets/identity-center-2.png create mode 100644 docs/assets/identity-center-3.png create mode 100644 docs/assets/identity-center-4.png create mode 100644 docs/assets/identity-center-5.png create mode 100644 docs/assets/identity-center-6.png create mode 100644 docs/assets/keycloak-add-client_2.png delete mode 100644 docs/assets/keycloak-client-scope-selected.png create mode 100644 docs/assets/okta-app.png create mode 100644 docs/assets/okta-auth-policy.png create mode 100644 docs/assets/okta-auth-rule.png create mode 100644 docs/assets/okta-create-oidc-app.png create mode 100644 docs/assets/okta-groups-claim.png create mode 100644 docs/assets/okta-groups-scope.png create mode 100644 docs/assets/release-action.png create mode 100644 docs/assets/zitadel-actions.png create mode 100644 docs/assets/zitadel-application-1.png create mode 100644 docs/assets/zitadel-application-2.png create mode 100644 docs/assets/zitadel-application-3.png create mode 100644 docs/assets/zitadel-application-4.png create mode 100644 docs/assets/zitadel-application-secrets.png create mode 100644 docs/assets/zitadel-application-settings.png create mode 100644 docs/assets/zitadel-argocd-login.png create mode 100644 docs/assets/zitadel-argocd-user-info.png create mode 100644 docs/assets/zitadel-project-authorizations.png create mode 100644 docs/assets/zitadel-project-roles.png create mode 100644 docs/assets/zitadel-project-settings.png create mode 100644 docs/assets/zitadel-project.png create mode 100644 docs/developer-guide/architecture/authz-authn.md create mode 100644 docs/developer-guide/architecture/components.md create mode 100644 docs/developer-guide/extensions/proxy-extensions.md create mode 100644 docs/developer-guide/extensions/ui-extensions.md create mode 100644 docs/operator-manual/applicationset/Appset-Any-Namespace.md create mode 100644 docs/operator-manual/applicationset/Generators-Git-File-Globbing.md create mode 100644 docs/operator-manual/applicationset/Generators-Plugin.md create mode 100644 docs/operator-manual/applicationset/Generators-Post-Selector.md rename docs/operator-manual/applicationset/{Progressive-Rollouts.md => Progressive-Syncs.md} (78%) create mode 100644 docs/operator-manual/applicationset/applicationset-specification.md create mode 100644 docs/operator-manual/argocd-cm-yaml.md create mode 100644 docs/operator-manual/argocd-cmd-params-cm-yaml.md create mode 100644 docs/operator-manual/argocd-rbac-cm-yaml.md create mode 100644 docs/operator-manual/argocd-repo-creds-yaml.md create mode 100644 docs/operator-manual/argocd-repositories-yaml.md create mode 100644 docs/operator-manual/argocd-secret-yaml.md create mode 100644 docs/operator-manual/argocd-ssh-known-hosts-cm-yaml.md create mode 100644 docs/operator-manual/argocd-tls-certs-cm-yaml.md create mode 100644 docs/operator-manual/cluster-management.md create mode 100644 docs/operator-manual/core.md create mode 100644 docs/operator-manual/dynamic-cluster-distribution.md create mode 100755 docs/operator-manual/notifications/services/awssqs.md create mode 100755 docs/operator-manual/notifications/services/pagerduty_v2.md create mode 100644 docs/operator-manual/project-specification.md create mode 100644 docs/operator-manual/reconcile.md create mode 100644 docs/operator-manual/tested-kubernetes-versions.md create mode 100644 docs/operator-manual/ui-customization.md create mode 100644 docs/operator-manual/upgrading/2.10-2.11.md create mode 100644 docs/operator-manual/upgrading/2.6-2.7.md create mode 100644 docs/operator-manual/upgrading/2.7-2.8.md create mode 100644 docs/operator-manual/upgrading/2.8-2.9.md create mode 100644 docs/operator-manual/upgrading/2.9-2.10.md create mode 100644 docs/operator-manual/user-management/identity-center.md create mode 100644 docs/operator-manual/user-management/zitadel.md create mode 100644 docs/proposals/004-scalability-benchmarking.md create mode 100644 docs/proposals/applicationset-plugin-generator.md create mode 100644 docs/proposals/decouple-application-sync-user-using-impersonation.md create mode 100644 docs/proposals/feature-bounties.md create mode 100644 docs/proposals/feature-bounties/hide-annotations.md create mode 100644 docs/proposals/native-oci-support.md create mode 100644 docs/proposals/rebalancing-clusters-across-shards-dynamically.md create mode 100644 docs/proposals/respect-rbac-for-resource-exclusions.md create mode 100644 docs/proposals/sync-timeout.md delete mode 100644 docs/snyk/master/ghcr.io_dexidp_dex_v2.35.3.html create mode 100644 docs/snyk/master/ghcr.io_dexidp_dex_v2.38.0.html create mode 100644 docs/snyk/master/haproxy_2.6.14-alpine.html delete mode 100644 docs/snyk/master/haproxy_2.6.2-alpine.html create mode 100644 docs/snyk/master/redis_7.0.14-alpine.html delete mode 100644 docs/snyk/master/redis_7.0.7-alpine.html delete mode 100644 docs/snyk/v2.3.13/argocd-iac-install.html delete mode 100644 docs/snyk/v2.3.13/argocd-iac-namespace-install.html delete mode 100644 docs/snyk/v2.3.13/ghcr.io_dexidp_dex_v2.35.3.html delete mode 100644 docs/snyk/v2.3.13/haproxy_2.0.29-alpine.html delete mode 100644 docs/snyk/v2.3.13/quay.io_argoproj_argocd_v2.3.13.html delete mode 100644 docs/snyk/v2.3.13/redis_6.2.8-alpine.html delete mode 100644 docs/snyk/v2.4.19/ghcr.io_dexidp_dex_v2.35.3.html delete mode 100644 docs/snyk/v2.4.19/haproxy_2.0.29-alpine.html delete mode 100644 docs/snyk/v2.4.19/quay.io_argoproj_argocd_v2.4.19.html delete mode 100644 docs/snyk/v2.4.19/redis_7.0.7-alpine.html delete mode 100644 docs/snyk/v2.5.7/ghcr.io_dexidp_dex_v2.35.3.html delete mode 100644 docs/snyk/v2.5.7/haproxy_2.6.2-alpine.html delete mode 100644 docs/snyk/v2.5.7/quay.io_argoproj_argocd_v2.5.7.html delete mode 100644 docs/snyk/v2.5.7/redis_7.0.7-alpine.html delete mode 100644 docs/snyk/v2.6.0-rc4/argocd-test.html delete mode 100644 docs/snyk/v2.6.0-rc4/ghcr.io_dexidp_dex_v2.35.3.html delete mode 100644 docs/snyk/v2.6.0-rc4/haproxy_2.6.2-alpine.html delete mode 100644 docs/snyk/v2.6.0-rc4/quay.io_argoproj_argocd_v2.6.0-rc4.html delete mode 100644 docs/snyk/v2.6.0-rc4/redis_7.0.7-alpine.html rename docs/snyk/{v2.5.7 => v2.7.17}/argocd-iac-install.html (72%) rename docs/snyk/{v2.5.7 => v2.7.17}/argocd-iac-namespace-install.html (72%) rename docs/snyk/{v2.3.13 => v2.7.17}/argocd-test.html (61%) create mode 100644 docs/snyk/v2.7.17/ghcr.io_dexidp_dex_v2.37.0.html create mode 100644 docs/snyk/v2.7.17/haproxy_2.6.14-alpine.html rename docs/snyk/{v2.3.13/quay.io_argoproj_argocd-applicationset_v0.4.1.html => v2.7.17/quay.io_argoproj_argocd_v2.7.17.html} (51%) create mode 100644 docs/snyk/v2.7.17/redis_7.0.14-alpine.html rename docs/snyk/{v2.4.19 => v2.8.13}/argocd-iac-install.html (73%) rename docs/snyk/{v2.6.0-rc4 => v2.8.13}/argocd-iac-namespace-install.html (73%) rename docs/snyk/{v2.5.7 => v2.8.13}/argocd-test.html (54%) create mode 100644 docs/snyk/v2.8.13/ghcr.io_dexidp_dex_v2.37.0.html create mode 100644 docs/snyk/v2.8.13/haproxy_2.6.14-alpine.html create mode 100644 docs/snyk/v2.8.13/quay.io_argoproj_argocd_v2.8.13.html create mode 100644 docs/snyk/v2.8.13/redis_7.0.11-alpine.html rename docs/snyk/{v2.6.0-rc4 => v2.9.9}/argocd-iac-install.html (73%) rename docs/snyk/{v2.4.19 => v2.9.9}/argocd-iac-namespace-install.html (73%) rename docs/snyk/{v2.4.19 => v2.9.9}/argocd-test.html (61%) create mode 100644 docs/snyk/v2.9.9/ghcr.io_dexidp_dex_v2.37.0.html create mode 100644 docs/snyk/v2.9.9/haproxy_2.6.14-alpine.html create mode 100644 docs/snyk/v2.9.9/quay.io_argoproj_argocd_v2.9.9.html create mode 100644 docs/snyk/v2.9.9/redis_7.0.11-alpine.html create mode 100644 docs/user-guide/annotations-and-labels.md create mode 100644 docs/user-guide/application-specification.md create mode 100644 docs/user-guide/commands/argocd_admin_settings_resource-overrides_ignore-resource-updates.md create mode 100644 docs/user-guide/commands/argocd_app_add-source.md create mode 100644 docs/user-guide/commands/argocd_app_remove-source.md create mode 100644 docs/user-guide/commands/argocd_proj_add-source-namespace.md create mode 100644 docs/user-guide/commands/argocd_proj_remove-source-namespace.md create mode 100644 docs/user-guide/config-management-plugins.md create mode 100644 docs/user-guide/diff-strategies.md create mode 100644 docs/user-guide/extra_info.md create mode 100644 docs/user-guide/skip_reconcile.md create mode 100644 docs/user-guide/sync-kubectl.md create mode 100644 examples/k8s-rbac/argocd-server-applications/README.md create mode 100644 examples/k8s-rbac/argocd-server-applications/argocd-notifications-controller-rbac-clusterrole.yaml create mode 100644 examples/k8s-rbac/argocd-server-applications/argocd-notifications-controller-rbac-clusterrolebinding.yaml create mode 100644 examples/k8s-rbac/argocd-server-applications/argocd-server-rbac-clusterrole.yaml create mode 100644 examples/k8s-rbac/argocd-server-applications/argocd-server-rbac-clusterrolebinding.yaml create mode 100644 examples/k8s-rbac/argocd-server-applications/kustomization.yaml create mode 100644 hack/installers/checksums/helm-v3.11.1-linux-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.11.1-linux-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.11.1-linux-ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.11.1-linux-s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.11.2-linux-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.11.2-linux-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.11.2-linux-ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.11.2-linux-s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.12.0-linux-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.12.0-linux-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.12.0-linux-ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.12.0-linux-s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.12.1-linux-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.12.1-linux-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.12.1-linux-ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.12.1-linux-s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.13.1-linux-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.13.1-linux-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.13.1-linux-ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.13.1-linux-s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.13.2-linux-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.13.2-linux-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.13.2-linux-ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.13.2-linux-s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.0-linux-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.0-linux-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.0-linux-ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.0-linux-s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.1-linux-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.1-linux-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.1-linux-ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.1-linux-s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.2-darwin-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.2-darwin-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.2-linux-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.2-linux-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.2-linux-ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.2-linux-s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.3-darwin-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.3-darwin-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.3-linux-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.3-linux-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.3-linux-ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.14.3-linux-s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.0.1_darwin_amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.0.1_darwin_arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.0.1_linux_amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.0.1_linux_arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.0.1_linux_ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.0.1_linux_s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.1.0_darwin_amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.1.0_linux_amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.1.0_linux_arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.1.0_linux_ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.1.0_linux_s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.1.1_darwin_amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.1.1_linux_amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.1.1_linux_arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.1.1_linux_ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.1.1_linux_s390x.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.2.1_darwin_amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.2.1_darwin_arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.2.1_linux_amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.2.1_linux_arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.2.1_linux_ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/kustomize_5.2.1_linux_s390x.tar.gz.sha256 create mode 100755 hack/installers/install-gotestsum.sh rename hack/installers/{install-helm-linux.sh => install-helm.sh} (63%) create mode 100755 hack/update-kubernetes-version.sh create mode 100644 hack/update-supported-versions.sh create mode 100644 manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml create mode 100644 manifests/base/application-controller-deployment/argocd-application-controller-service.yaml create mode 100644 manifests/base/application-controller-deployment/argocd-application-controller-statefulset.yaml create mode 100644 manifests/base/application-controller-deployment/kustomization.yaml rename manifests/base/{application-controller => application-controller-roles}/argocd-application-controller-role.yaml (87%) rename manifests/base/{application-controller => application-controller-roles}/argocd-application-controller-rolebinding.yaml (100%) rename manifests/base/{application-controller => application-controller-roles}/argocd-application-controller-sa.yaml (100%) create mode 100644 manifests/base/application-controller-roles/kustomization.yaml delete mode 100644 manifests/base/redis/argocd-redis-rolebinding.yaml create mode 100644 manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrole.yaml create mode 100644 manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrolebinding.yaml create mode 100644 manifests/cluster-rbac/applicationset-controller/kustomization.yaml create mode 100644 manifests/ha/base/controller-deployment/argocd-application-controller-statefulset.yaml create mode 100644 manifests/ha/base/controller-deployment/argocd-cmd-params-cm.yaml create mode 100644 manifests/ha/base/controller-deployment/argocd-repo-server-deployment.yaml create mode 100644 manifests/ha/base/controller-deployment/argocd-server-deployment.yaml create mode 100644 manifests/ha/base/controller-deployment/kustomization.yaml create mode 100644 manifests/ha/base/overlays/argocd-cmd-params-cm.yaml create mode 100644 notification_controller/controller/controller_test.go create mode 100644 pkg/apis/application/v1alpha1/values.go create mode 100644 pkg/apis/application/v1alpha1/values_test.go create mode 100644 pkg/ratelimiter/ratelimiter.go create mode 100644 reposerver/apiclient/clientset_test.go create mode 100644 reposerver/apiclient/mocks/RepoServerService_GenerateManifestWithFilesClient.go create mode 100644 reposerver/cache/mocks/reposervercache.go create mode 100644 reposerver/metrics/githandlers_test.go create mode 100644 reposerver/repository/chart.go create mode 100644 reposerver/repository/chart_test.go create mode 100644 reposerver/repository/testdata/git-files-dirs/app/bar/.hidden/.keep create mode 100644 reposerver/repository/testdata/git-files-dirs/app/bar/.keep create mode 100644 reposerver/repository/testdata/git-files-dirs/app/foo/bar/config.yaml create mode 100644 reposerver/repository/testdata/git-files-dirs/app/foo/config.yaml create mode 100644 reposerver/repository/testdata/git-files-dirs/config.yaml create mode 100644 reposerver/repository/testdata/git-files-dirs/somedir/config.yaml create mode 100644 reposerver/repository/testdata/helm-with-dependencies-alias/Chart.yaml create mode 100644 reposerver/repository/testdata/helm-with-dependencies/Chart.yaml create mode 100644 reposerver/repository/testdata/invalid-metadata/bad.yaml create mode 100644 reposerver/repository/testdata/nil-metadata-accessors/nil-metadata-accessors.yaml create mode 100644 reposerver/repository/testdata/values-files/dir/values.yaml create mode 100644 reposerver/repository/types.go create mode 100644 resource_customizations/apps.kruise.io/AdvancedCronJob/health.lua create mode 100644 resource_customizations/apps.kruise.io/AdvancedCronJob/health_test.yaml create mode 100644 resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/activeJobs.yaml create mode 100644 resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/lastScheduleTime.yaml create mode 100644 resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/notScheduled.yaml create mode 100644 resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/suspended.yaml create mode 100644 resource_customizations/apps.kruise.io/BroadcastJob/health.lua create mode 100644 resource_customizations/apps.kruise.io/BroadcastJob/health_test.yaml create mode 100644 resource_customizations/apps.kruise.io/BroadcastJob/testdata/failed.yaml create mode 100644 resource_customizations/apps.kruise.io/BroadcastJob/testdata/running.yaml create mode 100644 resource_customizations/apps.kruise.io/BroadcastJob/testdata/succeeded.yaml create mode 100644 resource_customizations/apps.kruise.io/BroadcastJob/testdata/suspended.yaml create mode 100644 resource_customizations/apps.kruise.io/CloneSet/health.lua create mode 100644 resource_customizations/apps.kruise.io/CloneSet/health_test.yaml create mode 100644 resource_customizations/apps.kruise.io/CloneSet/testdata/degraded.yaml create mode 100644 resource_customizations/apps.kruise.io/CloneSet/testdata/healthy.yaml create mode 100644 resource_customizations/apps.kruise.io/CloneSet/testdata/partition_suspended.yaml create mode 100644 resource_customizations/apps.kruise.io/CloneSet/testdata/suspended.yaml create mode 100644 resource_customizations/apps.kruise.io/CloneSet/testdata/unknown.yaml create mode 100644 resource_customizations/apps.kruise.io/DaemonSet/health.lua create mode 100644 resource_customizations/apps.kruise.io/DaemonSet/health_test.yaml create mode 100644 resource_customizations/apps.kruise.io/DaemonSet/testdata/degraded.yaml create mode 100644 resource_customizations/apps.kruise.io/DaemonSet/testdata/healthy.yaml create mode 100644 resource_customizations/apps.kruise.io/DaemonSet/testdata/partition_suspended.yaml create mode 100644 resource_customizations/apps.kruise.io/DaemonSet/testdata/suspended.yaml create mode 100644 resource_customizations/apps.kruise.io/DaemonSet/testdata/unknown.yaml create mode 100644 resource_customizations/apps.kruise.io/StatefulSet/health.lua create mode 100644 resource_customizations/apps.kruise.io/StatefulSet/health_test.yaml create mode 100644 resource_customizations/apps.kruise.io/StatefulSet/testdata/degraded.yaml create mode 100644 resource_customizations/apps.kruise.io/StatefulSet/testdata/healthy.yaml create mode 100644 resource_customizations/apps.kruise.io/StatefulSet/testdata/partition_suspended.yaml create mode 100644 resource_customizations/apps.kruise.io/StatefulSet/testdata/suspended.yaml create mode 100644 resource_customizations/apps.kruise.io/StatefulSet/testdata/unknown.yaml create mode 100644 resource_customizations/argoproj.io/CronWorkflow/actions/action_test.yaml create mode 100644 resource_customizations/argoproj.io/CronWorkflow/actions/create-workflow/action.lua create mode 100644 resource_customizations/argoproj.io/CronWorkflow/actions/discovery.lua create mode 100644 resource_customizations/argoproj.io/CronWorkflow/actions/testdata/cronworkflow-without-label.yaml create mode 100644 resource_customizations/argoproj.io/CronWorkflow/actions/testdata/cronworkflow.yaml create mode 100644 resource_customizations/argoproj.io/CronWorkflow/actions/testdata/workflow-without-label.yaml create mode 100644 resource_customizations/argoproj.io/CronWorkflow/actions/testdata/workflow.yaml create mode 100644 resource_customizations/argoproj.io/EventBus/health.lua create mode 100644 resource_customizations/argoproj.io/EventBus/health_test.yaml create mode 100644 resource_customizations/argoproj.io/EventBus/testdata/degraded.yaml create mode 100644 resource_customizations/argoproj.io/EventBus/testdata/healthy.yaml create mode 100644 resource_customizations/argoproj.io/WorkflowTemplate/actions/action_test.yaml create mode 100644 resource_customizations/argoproj.io/WorkflowTemplate/actions/create-workflow/action.lua create mode 100644 resource_customizations/argoproj.io/WorkflowTemplate/actions/discovery.lua create mode 100644 resource_customizations/argoproj.io/WorkflowTemplate/actions/testdata/workflow.yaml create mode 100644 resource_customizations/argoproj.io/WorkflowTemplate/actions/testdata/workflowtemplate.yaml create mode 100644 resource_customizations/batch/CronJob/actions/action_test.yaml create mode 100644 resource_customizations/batch/CronJob/actions/create-job/action.lua create mode 100644 resource_customizations/batch/CronJob/actions/discovery.lua create mode 100644 resource_customizations/batch/CronJob/actions/testdata/cronjob.yaml create mode 100644 resource_customizations/batch/CronJob/actions/testdata/job.yaml create mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/health.lua create mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/health_test.yaml create mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/invalid.yaml create mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/progressing.yaml create mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_green.yaml create mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_red.yaml create mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_yellow.yaml create mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_yellow_single_node.yaml create mode 100644 resource_customizations/beat.k8s.elastic.co/Beat/testdata/unknown.yaml create mode 100644 resource_customizations/cert-manager.io/Certificate/testdata/progressing_issuing_last.yaml create mode 100644 resource_customizations/cert-manager.io/ClusterIssuer/health.lua create mode 100644 resource_customizations/cert-manager.io/ClusterIssuer/health_test.yaml create mode 100644 resource_customizations/cert-manager.io/ClusterIssuer/testdata/degraded_acmeFailed.yaml create mode 100644 resource_customizations/cert-manager.io/ClusterIssuer/testdata/healthy_registered.yaml create mode 100644 resource_customizations/cert-manager.io/ClusterIssuer/testdata/progressing_noStatus.yaml create mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/health.lua create mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/health_test.yaml create mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/degraded_reconcileError.yaml create mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/healthy.yaml create mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing.yaml create mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_creating.yaml create mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_noStatus.yaml create mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_noavailable.yaml create mode 100644 resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/suspended.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasMigration/health.lua create mode 100644 resource_customizations/db.atlasgo.io/AtlasMigration/health_test.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasMigration/testdata/degraded.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasMigration/testdata/healthy.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasMigration/testdata/progressing.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasSchema/health.lua create mode 100644 resource_customizations/db.atlasgo.io/AtlasSchema/health_test.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasSchema/testdata/degraded.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasSchema/testdata/healthy.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasSchema/testdata/progressing.yaml create mode 100644 resource_customizations/external-secrets.io/ClusterExternalSecret/health.lua create mode 100644 resource_customizations/external-secrets.io/ClusterExternalSecret/health_test.yaml create mode 100644 resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/healthy.yaml create mode 100644 resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/notready.yaml create mode 100644 resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/partiallyready-multiple-conditions.yaml create mode 100644 resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/partiallyready.yaml create mode 100644 resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/progressing.yaml create mode 100644 resource_customizations/external-secrets.io/ClusterSecretStore/health.lua create mode 100644 resource_customizations/external-secrets.io/ClusterSecretStore/health_test.yaml create mode 100644 resource_customizations/external-secrets.io/ClusterSecretStore/testdata/degraded.yaml create mode 100644 resource_customizations/external-secrets.io/ClusterSecretStore/testdata/healthy.yaml create mode 100644 resource_customizations/external-secrets.io/ExternalSecret/actions/action_test.yaml create mode 100644 resource_customizations/external-secrets.io/ExternalSecret/actions/discovery.lua create mode 100644 resource_customizations/external-secrets.io/ExternalSecret/actions/refresh/action.lua create mode 100644 resource_customizations/external-secrets.io/ExternalSecret/actions/testdata/external-secret-updated.yaml create mode 100644 resource_customizations/external-secrets.io/ExternalSecret/actions/testdata/external-secret.yaml create mode 100644 resource_customizations/external-secrets.io/PushSecret/actions/action_test.yaml create mode 100644 resource_customizations/external-secrets.io/PushSecret/actions/discovery.lua create mode 100644 resource_customizations/external-secrets.io/PushSecret/actions/push/action.lua create mode 100644 resource_customizations/external-secrets.io/PushSecret/actions/testdata/push-secret-updated.yaml create mode 100644 resource_customizations/external-secrets.io/PushSecret/actions/testdata/push-secret.yaml create mode 100644 resource_customizations/external-secrets.io/PushSecret/health.lua create mode 100644 resource_customizations/external-secrets.io/PushSecret/health_test.yaml create mode 100644 resource_customizations/external-secrets.io/PushSecret/testdata/degraded.yaml create mode 100644 resource_customizations/external-secrets.io/PushSecret/testdata/healthy.yaml create mode 100644 resource_customizations/external-secrets.io/PushSecret/testdata/progressing.yaml rename resource_customizations/flink.apache.org/FlinkDeployment/testdata/{healthy_running.yaml => healthy_running_v0.1.x.yaml} (100%) create mode 100644 resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_running_v1.x.yaml rename resource_customizations/flink.apache.org/FlinkDeployment/testdata/{healthy_suspended.yaml => healthy_suspended_v0.1.x.yaml} (100%) create mode 100644 resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_suspended_v1.x.yaml create mode 100644 resource_customizations/iammanager.keikoproj.io/Iamrole/health.lua create mode 100644 resource_customizations/iammanager.keikoproj.io/Iamrole/health_test.yaml create mode 100644 resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/degraded_error.yaml create mode 100644 resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/degraded_rolesMaxLimitReached.yaml create mode 100644 resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/healthy.yaml create mode 100644 resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/progressing_noStatus.yaml create mode 100644 resource_customizations/mariadb.mmontes.io/MariaDB/health.lua create mode 100644 resource_customizations/mariadb.mmontes.io/MariaDB/health_test.yaml create mode 100644 resource_customizations/mariadb.mmontes.io/MariaDB/testdata/mariadb_error.yaml create mode 100644 resource_customizations/mariadb.mmontes.io/MariaDB/testdata/no_status.yaml create mode 100644 resource_customizations/mariadb.mmontes.io/MariaDB/testdata/restore_complete.yaml create mode 100644 resource_customizations/mariadb.mmontes.io/MariaDB/testdata/restore_not_complete.yaml create mode 100644 resource_customizations/mariadb.mmontes.io/MariaDB/testdata/statefulset_not_ready.yaml create mode 100644 resource_customizations/mariadb.mmontes.io/MariaDB/testdata/statefulset_ready.yaml create mode 100644 resource_customizations/operator.openshift.io/IngressController/health.lua create mode 100644 resource_customizations/operator.openshift.io/IngressController/health_test.yaml create mode 100644 resource_customizations/operator.openshift.io/IngressController/testdata/degraded.yaml create mode 100644 resource_customizations/operator.openshift.io/IngressController/testdata/healthy.yaml create mode 100644 resource_customizations/operator.openshift.io/IngressController/testdata/progressing_initialization.yaml create mode 100644 resource_customizations/operator.openshift.io/IngressController/testdata/progressing_pod_rollout.yaml create mode 100644 resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/health.lua create mode 100644 resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/health_test.yaml create mode 100644 resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/degraded_advertiseError.yaml create mode 100644 resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/degraded_notAdopted.yaml create mode 100644 resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/degraded_unadvertiseError.yaml create mode 100644 resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/healthy.yaml create mode 100644 resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/progressing_negativeBrowse.yaml create mode 100644 resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/progressing_negativeLookup.yaml create mode 100644 resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/unknown_discoveryError.yaml create mode 100644 resource_customizations/pxc.percona.com/PerconaXtraDBCluster/health.lua create mode 100644 resource_customizations/pxc.percona.com/PerconaXtraDBCluster/health_test.yaml create mode 100644 resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/error.yaml create mode 100644 resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/initializing.yaml create mode 100644 resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/paused.yaml create mode 100644 resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/ready.yaml create mode 100644 resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/stopping.yaml create mode 100644 resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/unknown.yaml create mode 100644 resource_customizations/rollouts.kruise.io/Rollout/health.lua create mode 100644 resource_customizations/rollouts.kruise.io/Rollout/health_test.yaml create mode 100644 resource_customizations/rollouts.kruise.io/Rollout/testdata/degraded.yaml create mode 100644 resource_customizations/rollouts.kruise.io/Rollout/testdata/healthy.yaml create mode 100644 resource_customizations/rollouts.kruise.io/Rollout/testdata/progressing.yaml create mode 100644 resource_customizations/rollouts.kruise.io/Rollout/testdata/suspended.yaml create mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/health_test.yaml create mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/heatlh.lua create mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/degraded_reconcileError.yaml create mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/healthy.yaml create mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/progressing_creating.yaml create mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/progressing_noStatus.yaml create mode 100644 resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/suspended_reconcilePaused.yaml create mode 100644 resource_customizations/serving.kserve.io/InferenceService/health.lua create mode 100644 resource_customizations/serving.kserve.io/InferenceService/health_test.yaml create mode 100644 resource_customizations/serving.kserve.io/InferenceService/testdata/degraded.yaml create mode 100644 resource_customizations/serving.kserve.io/InferenceService/testdata/healthy.yaml create mode 100644 resource_customizations/serving.kserve.io/InferenceService/testdata/progressing.yaml create mode 100644 resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/health.lua create mode 100644 resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/health_test.yaml create mode 100644 resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/testdata/bad.yaml create mode 100644 resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/testdata/good.yaml create mode 100644 resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/testdata/initializing.yaml create mode 100644 resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/health.lua create mode 100644 resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/health_test.yaml create mode 100644 resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/testdata/bad.yaml create mode 100644 resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/testdata/good.yaml create mode 100644 resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/testdata/initializing.yaml create mode 100644 resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc.yaml create mode 100644 resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc_dstream.yaml create mode 100644 resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc_operator_api.yaml create mode 100644 resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc_without_spec_config.yaml create mode 100644 resource_customizations/tower.ansible.com/AnsibleJob/health.lua create mode 100644 resource_customizations/tower.ansible.com/AnsibleJob/health_test.yaml create mode 100644 resource_customizations/tower.ansible.com/AnsibleJob/testdata/degraded_canceled.yaml create mode 100644 resource_customizations/tower.ansible.com/AnsibleJob/testdata/degraded_error.yaml create mode 100644 resource_customizations/tower.ansible.com/AnsibleJob/testdata/degraded_failed.yaml create mode 100644 resource_customizations/tower.ansible.com/AnsibleJob/testdata/healthy.yaml create mode 100644 resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_new.yaml create mode 100644 resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_noStatus.yaml create mode 100644 resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_pending.yaml create mode 100644 resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_running.yaml create mode 100644 resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_waiting.yaml create mode 100644 server/application/mocks/Broadcaster.go create mode 100644 server/application/websocket_test.go create mode 100644 server/applicationset/applicationset_test.go create mode 100644 server/extension/mocks/ExtensionMetricsRegistry.go create mode 100644 server/extension/mocks/ProjectGetter.go create mode 100644 server/extension/mocks/RbacEnforcer.go create mode 100644 test/e2e/api_versions_test.go create mode 100644 test/e2e/app_skipreconcile_test.go create mode 100644 test/e2e/app_sync_options_test.go rename test/e2e/{delarative_test.go => declarative_test.go} (100%) create mode 100644 test/e2e/testdata/cluster-role-hook/cluster-role.yaml rename test/e2e/testdata/{cluster-role => cluster-role-hook}/pod.yaml (100%) create mode 100644 test/e2e/testdata/cmp-gitcreds/plugin.yaml create mode 100644 test/e2e/testdata/cmp-gitcreds/subdir/special.yaml create mode 100644 test/e2e/testdata/cmp-gitcredstemplate/plugin.yaml create mode 100644 test/e2e/testdata/cmp-gitcredstemplate/subdir/special.yaml create mode 100644 test/e2e/testdata/cmp-kustomize/plugin.yaml create mode 100644 test/e2e/testdata/cmp-preserve-file-mode/plugin.yaml create mode 100755 test/e2e/testdata/cmp-preserve-file-mode/script.sh create mode 100644 test/e2e/testdata/crd-version-differences/crd-does-not-exist-instance.yaml create mode 100644 test/e2e/testdata/crd-version-differences/crd-v1alpha1.yaml create mode 100644 test/e2e/testdata/crd-version-differences/crd-v1alpha2-instance.yaml create mode 100644 test/e2e/testdata/crd-version-differences/crd-wronggroup-instance.yaml create mode 100644 test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-deployment.yaml create mode 100644 test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-namespace.yaml create mode 100644 test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-svc.yaml create mode 100644 test/e2e/testdata/helm-with-multiple-dependencies-permission-denied/Chart.yaml create mode 100644 test/e2e/testdata/post-delete-hook/hook.yaml create mode 100644 test/e2e/testdata/resource-actions/cron-job.yaml create mode 100644 test/e2e/testdata/syncwaves-prune-order/README.md create mode 100644 test/e2e/testdata/syncwaves-prune-order/pod.yaml create mode 100644 test/e2e/testdata/syncwaves-prune-order/rbac.yaml create mode 100644 test/e2e/testdata2/cmp-symlink/plugin.yaml create mode 120000 test/e2e/testdata2/guestbook-partial-symlink-files/guestbook-ui-deployment.yaml create mode 100644 test/e2e/testdata2/guestbook-partial-symlink-files/guestbook-ui-svc.yaml create mode 100644 test/e2e/testdata2/guestbook-partial-symlink-files/kustomization.yaml create mode 120000 test/e2e/testdata2/guestbook-symlink-files/guestbook-ui-deployment.yaml create mode 120000 test/e2e/testdata2/guestbook-symlink-files/guestbook-ui-svc.yaml create mode 120000 test/e2e/testdata2/guestbook-symlink-files/kustomization.yaml create mode 120000 test/e2e/testdata2/guestbook-symlink-folder create mode 100644 test/e2e/testdata2/guestbook/guestbook-ui-deployment.yaml create mode 100644 test/e2e/testdata2/guestbook/guestbook-ui-svc.yaml create mode 100644 test/e2e/testdata2/guestbook/kustomization.yaml create mode 100644 ui-test/osv-scanner.toml create mode 100644 ui/osv-scanner.toml create mode 100644 ui/src/app/applications/components/application-deployment-history/initiated-by.tsx create mode 100644 ui/src/app/applications/components/application-details/application-resource-list.scss create mode 100644 ui/src/app/applications/components/application-node-info/readiness-gates-not-passed-warning.scss create mode 100644 ui/src/app/applications/components/application-node-info/readiness-gates-not-passed-warning.tsx create mode 100644 ui/src/app/applications/components/application-pod-view/pod-tooltip.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/auto-scroll-button.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/container-selector.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/copy-logs-button.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/dark-mode-toggle-button.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/download-logs-button.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/follow-toggle-button.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/fullscreen-button.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/log-loader.ts create mode 100644 ui/src/app/applications/components/pod-logs-viewer/log-message-filter.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/pod-names-toggle-button.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/show-previous-logs-toggle-button.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/since-seconds-selector.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/tail-selector.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/timestamps-toggle-button.tsx create mode 100644 ui/src/app/applications/components/pod-logs-viewer/wrap-lines-button.tsx create mode 100644 ui/src/app/login/components/pkce-verify.scss create mode 100644 ui/src/app/login/components/pkce-verify.tsx create mode 100644 ui/src/app/login/components/utils.ts create mode 100644 ui/src/app/settings/components/clusters-list/cluster-list.scss create mode 100644 ui/src/app/shared/components/button.tsx create mode 100644 ui/src/app/shared/components/icon.ts create mode 100644 ui/src/app/shared/components/spacer.tsx create mode 100644 ui/src/app/shared/components/toggle-button.tsx create mode 100644 util/cache/mocks/cacheclient.go create mode 100644 util/grpc/trace.go create mode 100755 util/io/files/testdata/executable/script.sh create mode 100644 util/kube/testdata/svc-with-invalid-data.yaml create mode 100644 util/kustomize/testdata/kustomization_yaml_components/components/deployment.yaml create mode 100644 util/kustomize/testdata/kustomization_yaml_components/components/kustomization.yaml create mode 100644 util/kustomize/testdata/kustomization_yaml_components/kustomization.yaml create mode 100644 util/kustomize/testdata/kustomization_yaml_patches/deployment.yaml create mode 100644 util/kustomize/testdata/kustomization_yaml_patches/kustomization.yaml create mode 100644 util/kustomize/testdata/label_without_selector/deployment.yaml create mode 100644 util/kustomize/testdata/label_without_selector/kustomization.yaml create mode 100644 util/lua/impacted_resource.go create mode 100644 util/notification/expression/shared/appdetail_test.go create mode 100644 util/notification/settings/settings_test.go create mode 100644 util/security/jwt.go create mode 100644 util/security/jwt_test.go create mode 100644 util/webhook/testdata/azuredevops-git-push-event.json diff --git a/.dockerignore b/.dockerignore index 4bb2469b09e25..e778fce267438 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,3 +11,19 @@ cmd/**/debug debug.test coverage.out ui/node_modules/ +test-results/ +test/ +manifests/ +hack/ +docs/ +examples/ +.github/ +!test/container +!test/e2e/testdata +!test/fixture +!test/remote +!hack/installers +!hack/gpg-wrapper.sh +!hack/git-verify-wrapper.sh +!hack/tool-versions.sh +!hack/install.sh \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/release.md b/.github/ISSUE_TEMPLATE/release.md index dd24ed32aee77..b43b91a0e05ce 100644 --- a/.github/ISSUE_TEMPLATE/release.md +++ b/.github/ISSUE_TEMPLATE/release.md @@ -9,12 +9,6 @@ assignees: '' Target RC1 date: ___. __, ____ Target GA date: ___. __, ____ - - [ ] Create new section in the [Release Planning doc](https://docs.google.com/document/d/1trJIomcgXcfvLw0aYnERrFWfPjQOfYMDJOCh1S8nMBc/edit?usp=sharing) - - [ ] Schedule a Release Planning meeting roughly two weeks before the scheduled Release freeze date by adding it to the community calendar (or delegate this task to someone with write access to the community calendar) - - [ ] Include Zoom link in the invite - - [ ] Post in #argo-cd and #argo-contributors one week before the meeting - - [ ] Post again one hour before the meeting - - [ ] At the meeting, remove issues/PRs from the project's column for that release which have not been “claimed” by at least one Approver (add it to the next column if Approver requests that) - [ ] 1wk before feature freeze post in #argo-contributors that PRs must be merged by DD-MM-YYYY to be included in the release - ask approvers to drop items from milestone they can’t merge - [ ] At least two days before RC1 date, draft RC blog post and submit it for review (or delegate this task) - [ ] Cut RC1 (or delegate this task to an Approver and coordinate timing) diff --git a/.github/cherry-pick-bot.yml b/.github/cherry-pick-bot.yml new file mode 100644 index 0000000000000..a9de2afd04927 --- /dev/null +++ b/.github/cherry-pick-bot.yml @@ -0,0 +1,3 @@ +enabled: true +preservePullRequestTitle: true + diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6e66e66db6630..5540fb7fd93e6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,3 +16,28 @@ updates: directory: "/ui/" schedule: interval: "daily" + + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "daily" + + - package-ecosystem: "docker" + directory: "/test/container/" + schedule: + interval: "daily" + + - package-ecosystem: "docker" + directory: "/test/e2e/multiarch-container/" + schedule: + interval: "daily" + + - package-ecosystem: "docker" + directory: "/test/remote/" + schedule: + interval: "daily" + + - package-ecosystem: "docker" + directory: "/ui-test/" + schedule: + interval: "daily" diff --git a/.github/pr-title-checker-config.json b/.github/pr-title-checker-config.json new file mode 100644 index 0000000000000..c3437def33834 --- /dev/null +++ b/.github/pr-title-checker-config.json @@ -0,0 +1,15 @@ +{ + "LABEL": { + "name": "title needs formatting", + "color": "EEEEEE" + }, + "CHECKS": { + "prefixes": ["[Bot] docs: "], + "regexp": "^(feat|fix|docs|test|ci|chore)!?(\\(.*\\))?!?:.*" + }, + "MESSAGES": { + "success": "PR title is valid", + "failure": "PR title is invalid", + "notice": "PR Title needs to pass regex '^(feat|fix|docs|test|ci|chore)!?(\\(.*\\))?!?:.*" + } + } diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e64d61328d9f1..c1a3f42508aaa 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,17 +1,24 @@ + 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). +* [ ] The title of the PR conforms to the [Toolchain Guide](https://argo-cd.readthedocs.io/en/latest/developer-guide/toolchain-guide/#title-of-the-pr) * [ ] I've included "Closes [ISSUE #]" or "Fixes [ISSUE #]" in the description to automatically close the associated issue. * [ ] 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 have signed off all my commits as required by [DCO](https://github.com/argoproj/argoproj/tree/master/community#contributing-to-argo) +* [ ] I have signed off all my commits as required by [DCO](https://github.com/argoproj/argoproj/blob/master/community/CONTRIBUTING.md#legal) * [ ] I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged. -* [ ] My build is green ([troubleshooting builds](https://argo-cd.readthedocs.io/en/latest/developer-guide/ci/)). +* [ ] My build is green ([troubleshooting builds](https://argo-cd.readthedocs.io/en/latest/developer-guide/ci/)). +* [ ] My new feature complies with the [feature status](https://github.com/argoproj/argoproj/blob/master/community/feature-status.md) guidelines. +* [ ] I have added a brief description of why this PR is necessary and/or what this PR solves. +* [ ] Optional. My organization is added to USERS.md. +* [ ] Optional. For bug fixes, I've indicated what older releases this fix should be cherry-picked into (this may or may not happen depending on risk/complexity). + diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000000000..6d4302d2b540c --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,38 @@ +# Workflows + +| Workflow | Description | +|--------------------|----------------------------------------------------------------| +| ci-build.yaml | Build, lint, test, codegen, build-ui, analyze, e2e-test | +| codeql.yaml | CodeQL analysis | +| image-reuse.yaml | Build, push, and Sign container images | +| image.yaml | Build container image for PR's & publish for push events | +| pr-title-check.yaml| Lint PR for semantic information | +| init-release.yaml | Build manifests and version then create a PR for release branch| +| release.yaml | Build images, cli-binaries, provenances, and post actions | +| update-snyk.yaml | Scheduled snyk reports | + +# Reusable workflows + +## image-reuse.yaml + +- The resuable workflow can be used to publish or build images with multiple container registries(Quay,GHCR, dockerhub), and then sign them with cosign when an image is published. +- A GO version `must` be specified e.g. 1.21 +- The image name for each registry *must* contain the tag. Note: multiple tags are allowed for each registry using a CSV type. +- Multiple platforms can be specified e.g. linux/amd64,linux/arm64 +- Images are not published by default. A boolean value must be set to `true` to push images. +- An optional target can be specified. + +| Inputs | Description | Type | Required | Defaults | +|-------------------|-------------------------------------|-------------|----------|-----------------| +| go-version | Version of Go to be used | string | true | none | +| quay_image_name | Full image name and tag | CSV, string | false | none | +| ghcr_image_name | Full image name and tag | CSV, string | false | none | +| docker_image_name | Full image name and tag | CSV, string | false | none | +| platforms | Platforms to build (linux/amd64) | CSV, string | false | linux/amd64 | +| push | Whether to push image/s to registry | boolean | false | false | +| target | Target build stage | string | false | none | + +| Outputs | Description | Type | +|-------------|------------------------------------------|-------| +|image-digest | Image digest of image container created | string| + diff --git a/.github/workflows/ci-build.yaml b/.github/workflows/ci-build.yaml index 87b1b0917b5cc..84534d518f26b 100644 --- a/.github/workflows/ci-build.yaml +++ b/.github/workflows/ci-build.yaml @@ -1,5 +1,5 @@ name: Integration tests -on: +on: push: branches: - 'master' @@ -9,10 +9,11 @@ on: pull_request: branches: - 'master' + - 'release-*' env: # Golang version to use across CI steps - GOLANG_VERSION: '1.18' + GOLANG_VERSION: '1.21' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -22,36 +23,62 @@ permissions: contents: read jobs: + changes: + runs-on: ubuntu-latest + outputs: + backend: ${{ steps.filter.outputs.backend_any_changed }} + frontend: ${{ steps.filter.outputs.frontend_any_changed }} + steps: + - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + - uses: tj-actions/changed-files@90a06d6ba9543371ab4df8eeca0be07ca6054959 # v42.0.2 + id: filter + with: + # Any file which is not under docs/, ui/ or is not a markdown file is counted as a backend file + files_yaml: | + backend: + - '!ui/**' + - '!**.md' + - '!**/*.md' + - '!docs/**' + frontend: + - 'ui/**' + - Dockerfile check-go: name: Ensure Go modules synchronicity + if: ${{ needs.changes.outputs.backend == 'true' }} runs-on: ubuntu-22.04 + needs: + - changes steps: - name: Checkout code - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 - name: Setup Golang - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: Download all Go modules run: | go mod download - - name: Check for tidyness of go.mod and go.sum + - name: Check for tidiness of go.mod and go.sum run: | go mod tidy git diff --exit-code -- . build-go: name: Build & cache Go code + if: ${{ needs.changes.outputs.backend == 'true' }} runs-on: ubuntu-22.04 + needs: + - changes steps: - name: Checkout code - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 - name: Setup Golang - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: Restore go build cache - uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ~/.cache/go-build key: ${{ runner.os }}-go-build-v1-${{ github.run_id }} @@ -66,37 +93,42 @@ jobs: contents: read # for actions/checkout to fetch code pull-requests: read # for golangci/golangci-lint-action to fetch pull requests name: Lint Go code + if: ${{ needs.changes.outputs.backend == 'true' }} runs-on: ubuntu-22.04 + needs: + - changes steps: - name: Checkout code - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 - name: Setup Golang - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: Run golangci-lint - uses: golangci/golangci-lint-action@0ad9a0988b3973e851ab0a07adf248ec2e100376 # v3.3.1 + uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 with: - version: v1.46.2 - args: --timeout 10m --exclude SA5011 --verbose + version: v1.54.0 + args: --enable gofmt --timeout 10m --exclude SA5011 --verbose --max-issues-per-linter 0 --max-same-issues 0 test-go: name: Run unit tests for Go packages + if: ${{ needs.changes.outputs.backend == 'true' }} runs-on: ubuntu-22.04 needs: - build-go + - changes env: GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }} + GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }} steps: - name: Create checkout directory run: mkdir -p ~/go/src/github.com/argoproj - name: Checkout code - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 - name: Create symlink in GOPATH run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd - name: Setup Golang - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: Install required packages @@ -116,13 +148,17 @@ jobs: run: | echo "/usr/local/bin" >> $GITHUB_PATH - name: Restore go build cache - uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ~/.cache/go-build key: ${{ runner.os }}-go-build-v1-${{ github.run_id }} - name: Install all tools required for building & testing run: | make install-test-tools-local + # We install kustomize in the dist directory + - name: Add dist to PATH + run: | + echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH - name: Setup git username and email run: | git config --global user.name "John Doe" @@ -133,33 +169,35 @@ jobs: - name: Run all unit tests run: make test-local - name: Generate code coverage artifacts - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: code-coverage path: coverage.out - name: Generate test results artifacts - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: test-results path: test-results/ test-go-race: - name: Run unit tests with -race, for Go packages + name: Run unit tests with -race for Go packages + if: ${{ needs.changes.outputs.backend == 'true' }} runs-on: ubuntu-22.04 needs: - build-go + - changes env: GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }} + GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }} steps: - name: Create checkout directory run: mkdir -p ~/go/src/github.com/argoproj - name: Checkout code - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 - name: Create symlink in GOPATH run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd - name: Setup Golang - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: Install required packages @@ -179,13 +217,17 @@ jobs: run: | echo "/usr/local/bin" >> $GITHUB_PATH - name: Restore go build cache - uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ~/.cache/go-build key: ${{ runner.os }}-go-build-v1-${{ github.run_id }} - name: Install all tools required for building & testing run: | make install-test-tools-local + # We install kustomize in the dist directory + - name: Add dist to PATH + run: | + echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH - name: Setup git username and email run: | git config --global user.name "John Doe" @@ -196,19 +238,22 @@ jobs: - name: Run all unit tests run: make test-race-local - name: Generate test results artifacts - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: race-results path: test-results/ codegen: name: Check changes to generated code + if: ${{ needs.changes.outputs.backend == 'true' }} runs-on: ubuntu-22.04 + needs: + - changes steps: - name: Checkout code - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 - name: Setup Golang - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: Create symlink in GOPATH @@ -232,6 +277,10 @@ jobs: make install-codegen-tools-local make install-go-tools-local working-directory: /home/runner/go/src/github.com/argoproj/argo-cd + # We install kustomize in the dist directory + - name: Add dist to PATH + run: | + echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH - name: Run codegen run: | set -x @@ -247,17 +296,20 @@ jobs: build-ui: name: Build, test & lint UI code + if: ${{ needs.changes.outputs.frontend == 'true' }} runs-on: ubuntu-22.04 + needs: + - changes steps: - name: Checkout code - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 - name: Setup NodeJS - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 + uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1 with: - node-version: '12.18.4' + node-version: '21.6.1' - name: Restore node dependency cache id: cache-dependencies - uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ui/node_modules key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }} @@ -279,20 +331,22 @@ jobs: analyze: name: Process & analyze test artifacts + if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }} runs-on: ubuntu-22.04 needs: - test-go - build-ui + - changes env: sonar_secret: ${{ secrets.SONAR_TOKEN }} steps: - name: Checkout code - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 with: fetch-depth: 0 - name: Restore node dependency cache id: cache-dependencies - uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ui/node_modules key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }} @@ -302,7 +356,7 @@ jobs: - name: Create test-results directory run: | mkdir -p test-results - - name: Get code coverage artifiact + - name: Get code coverage artifact uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: code-coverage @@ -312,7 +366,7 @@ jobs: name: test-results path: test-results - name: Upload code coverage information to codecov.io - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4 with: file: coverage.out - name: Perform static code analysis using SonarCloud @@ -323,34 +377,37 @@ jobs: SCANNER_PATH: /tmp/cache/scanner OS: linux run: | - # We do not use the provided action, because it does contain an old - # version of the scanner, and also takes time to build. - set -e - mkdir -p ${SCANNER_PATH} - export SONAR_USER_HOME=${SCANNER_PATH}/.sonar - if [[ ! -x "${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner" ]]; then - curl -Ol https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip - unzip -qq -o sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip -d ${SCANNER_PATH} - fi - - chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner - chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/jre/bin/java - - # Explicitly set NODE_MODULES - export NODE_MODULES=${PWD}/ui/node_modules - export NODE_PATH=${PWD}/ui/node_modules - - ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner + # We do not use the provided action, because it does contain an old + # version of the scanner, and also takes time to build. + set -e + mkdir -p ${SCANNER_PATH} + export SONAR_USER_HOME=${SCANNER_PATH}/.sonar + if [[ ! -x "${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner" ]]; then + curl -Ol https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip + unzip -qq -o sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip -d ${SCANNER_PATH} + fi + + chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner + chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/jre/bin/java + + # Explicitly set NODE_MODULES + export NODE_MODULES=${PWD}/ui/node_modules + export NODE_PATH=${PWD}/ui/node_modules + + ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner if: env.sonar_secret != '' test-e2e: name: Run end-to-end tests + if: ${{ needs.changes.outputs.backend == 'true' }} runs-on: ubuntu-22.04 strategy: + fail-fast: false matrix: - k3s-version: [v1.26.0, v1.25.4, v1.24.3, v1.23.3] - needs: + k3s-version: [v1.29.1, v1.28.6, v1.27.10, v1.26.13, v1.25.16] + needs: - build-go + - changes env: GOPATH: /home/runner/go ARGOCD_FAKE_IN_CLUSTER: "true" @@ -360,15 +417,15 @@ jobs: ARGOCD_E2E_K3S: "true" ARGOCD_IN_CI: "true" ARGOCD_E2E_APISERVER_PORT: "8088" - ARGOCD_APPLICATION_NAMESPACES: "argocd-e2e-external" + ARGOCD_APPLICATION_NAMESPACES: "argocd-e2e-external,argocd-e2e-external-2" ARGOCD_SERVER: "127.0.0.1:8088" GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }} + GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }} steps: - name: Checkout code - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 - name: Setup Golang - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - name: GH actions workaround - Kill XSP4 process @@ -384,9 +441,10 @@ jobs: sudo mkdir -p $HOME/.kube && sudo chown -R runner $HOME/.kube sudo k3s kubectl config view --raw > $HOME/.kube/config sudo chown runner $HOME/.kube/config + sudo chmod go-r $HOME/.kube/config kubectl version - name: Restore go build cache - uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ~/.cache/go-build key: ${{ runner.os }}-go-build-v1-${{ github.run_id }} @@ -412,9 +470,9 @@ jobs: git config --global user.email "john.doe@example.com" - name: Pull Docker image required for tests run: | - docker pull ghcr.io/dexidp/dex:v2.35.3 + docker pull ghcr.io/dexidp/dex:v2.38.0 docker pull argoproj/argo-cd-ci-builder:v1.0.0 - docker pull redis:7.0.7-alpine + docker pull redis:7.0.14-alpine - name: Create target directory for binaries in the build-process run: | mkdir -p dist @@ -442,8 +500,31 @@ jobs: set -x make test-e2e-local - name: Upload e2e-server logs - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: e2e-server-k8s${{ matrix.k3s-version }}.log path: /tmp/e2e-server.log if: ${{ failure() }} + + # workaround for status checks -- check this one job instead of each individual E2E job in the matrix + # this allows us to skip the entire matrix when it doesn't need to run while still having accurate status checks + # see: + # https://github.com/argoproj/argo-workflows/pull/12006 + # https://github.com/orgs/community/discussions/9141#discussioncomment-2296809 + # https://github.com/orgs/community/discussions/26822#discussioncomment-3305794 + test-e2e-composite-result: + name: E2E Tests - Composite result + if: ${{ always() }} + needs: + - test-e2e + - changes + runs-on: ubuntu-22.04 + steps: + - run: | + result="${{ needs.test-e2e.result }}" + # mark as successful even if skipped + if [[ $result == "success" || $result == "skipped" ]]; then + exit 0 + else + exit 1 + fi \ No newline at end of file diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 09b941f00a119..5d745d222d2fb 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -5,6 +5,7 @@ on: # Secrets aren't available for dependabot on push. https://docs.github.com/en/enterprise-cloud@latest/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/troubleshooting-the-codeql-workflow#error-403-resource-not-accessible-by-integration-when-using-dependabot branches-ignore: - 'dependabot/**' + - 'cherry-pick-*' pull_request: schedule: - cron: '0 19 * * 0' @@ -26,10 +27,15 @@ jobs: # CodeQL runs on ubuntu-latest and windows-latest runs-on: ubuntu-22.04 - steps: - name: Checkout repository - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + + # Use correct go version. https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087 + - name: Setup Golang + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 + with: + go-version-file: go.mod # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/image-reuse.yaml b/.github/workflows/image-reuse.yaml new file mode 100644 index 0000000000000..5b5a12b346fa1 --- /dev/null +++ b/.github/workflows/image-reuse.yaml @@ -0,0 +1,171 @@ +name: Publish and Sign Container Image +on: + workflow_call: + inputs: + go-version: + required: true + type: string + quay_image_name: + required: false + type: string + ghcr_image_name: + required: false + type: string + docker_image_name: + required: false + type: string + platforms: + required: true + type: string + default: linux/amd64 + push: + required: true + type: boolean + default: false + target: + required: false + type: string + + secrets: + quay_username: + required: false + quay_password: + required: false + ghcr_username: + required: false + ghcr_password: + required: false + docker_username: + required: false + docker_password: + required: false + + outputs: + image-digest: + description: "sha256 digest of container image" + value: ${{ jobs.publish.outputs.image-digest }} + +permissions: {} + +jobs: + publish: + permissions: + contents: read + packages: write # Used to push images to `ghcr.io` if used. + id-token: write # Needed to create an OIDC token for keyless signing + runs-on: ubuntu-22.04 + outputs: + image-digest: ${{ steps.image.outputs.digest }} + steps: + - name: Checkout code + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + if: ${{ github.ref_type == 'tag'}} + + - name: Checkout code + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + if: ${{ github.ref_type != 'tag'}} + + - name: Setup Golang + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + with: + go-version: ${{ inputs.go-version }} + + - name: Install cosign + uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0 + + - uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2.2.0 + - uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + - name: Setup tags for container image as a CSV type + run: | + IMAGE_TAGS=$(for str in \ + ${{ inputs.quay_image_name }} \ + ${{ inputs.ghcr_image_name }} \ + ${{ inputs.docker_image_name}}; do + echo -n "${str}",;done | sed 's/,$//') + + echo $IMAGE_TAGS + echo "TAGS=$IMAGE_TAGS" >> $GITHUB_ENV + + - name: Setup image namespace for signing, strip off the tag + run: | + TAGS=$(for tag in \ + ${{ inputs.quay_image_name }} \ + ${{ inputs.ghcr_image_name }} \ + ${{ inputs.docker_image_name}}; do + echo -n "${tag}" | awk -F ":" '{print $1}' -;done) + + echo $TAGS + echo 'SIGNING_TAGS<> $GITHUB_ENV + echo $TAGS >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Login to Quay.io + uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 + with: + registry: quay.io + username: ${{ secrets.quay_username }} + password: ${{ secrets.quay_password }} + if: ${{ inputs.quay_image_name && inputs.push }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 + with: + registry: ghcr.io + username: ${{ secrets.ghcr_username }} + password: ${{ secrets.ghcr_password }} + if: ${{ inputs.ghcr_image_name && inputs.push }} + + - name: Login to dockerhub Container Registry + uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 + with: + username: ${{ secrets.docker_username }} + password: ${{ secrets.docker_password }} + if: ${{ inputs.docker_image_name && inputs.push }} + + - name: Set up build args for container image + run: | + echo "GIT_TAG=$(if [ -z "`git status --porcelain`" ]; then git describe --exact-match --tags HEAD 2>/dev/null; fi)" >> $GITHUB_ENV + echo "GIT_COMMIT=$(git rev-parse HEAD)" >> $GITHUB_ENV + echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV + echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV + + - name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@4d9e71b726748f254fe64fa44d273194bd18ec91 + with: + large-packages: false + docker-images: false + swap-storage: false + tool-cache: false + + - name: Build and push container image + id: image + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 #v5.3.0 + with: + context: . + platforms: ${{ inputs.platforms }} + push: ${{ inputs.push }} + tags: ${{ env.TAGS }} + target: ${{ inputs.target }} + provenance: false + sbom: false + build-args: | + GIT_TAG=${{env.GIT_TAG}} + GIT_COMMIT=${{env.GIT_COMMIT}} + BUILD_DATE=${{env.BUILD_DATE}} + GIT_TREE_STATE=${{env.GIT_TREE_STATE}} + + - name: Sign container images + run: | + for signing_tag in $SIGNING_TAGS; do + cosign sign \ + -a "repo=${{ github.repository }}" \ + -a "workflow=${{ github.workflow }}" \ + -a "sha=${{ github.sha }}" \ + -y \ + "$signing_tag"@${{ steps.image.outputs.digest }} + done + if: ${{ inputs.push }} diff --git a/.github/workflows/image.yaml b/.github/workflows/image.yaml index 6da3eac6ed670..a7174e10de9db 100644 --- a/.github/workflows/image.yaml +++ b/.github/workflows/image.yaml @@ -9,97 +9,109 @@ on: - master types: [ labeled, unlabeled, opened, synchronize, reopened ] -env: - GOLANG_VERSION: '1.18' - concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -permissions: - contents: read +permissions: {} jobs: - publish: + set-vars: permissions: - contents: write # for git to push upgrade commit if not already deployed - packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags + contents: read if: github.repository == 'argoproj/argo-cd' runs-on: ubuntu-22.04 - env: - GOPATH: /home/runner/work/argo-cd/argo-cd + outputs: + image-tag: ${{ steps.image.outputs.tag}} + platforms: ${{ steps.platforms.outputs.platforms }} steps: - - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 - with: - go-version: ${{ env.GOLANG_VERSION }} - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - with: - path: src/github.com/argoproj/argo-cd + - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 - # get image tag - - run: echo "tag=$(cat ./VERSION)-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT - working-directory: ./src/github.com/argoproj/argo-cd + - name: Set image tag for ghcr + run: echo "tag=$(cat ./VERSION)-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT id: image - # login - - run: | - docker login ghcr.io --username $USERNAME --password-stdin <<< "$PASSWORD" - docker login quay.io --username "$DOCKER_USERNAME" --password-stdin <<< "$DOCKER_TOKEN" - if: github.event_name == 'push' - env: - USERNAME: ${{ github.actor }} - PASSWORD: ${{ secrets.GITHUB_TOKEN }} - DOCKER_USERNAME: ${{ secrets.RELEASE_QUAY_USERNAME }} - DOCKER_TOKEN: ${{ secrets.RELEASE_QUAY_TOKEN }} - - # build - - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0 - - uses: docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325 # v2.2.1 - - run: | + - name: Determine image platforms to use + id: platforms + run: | IMAGE_PLATFORMS=linux/amd64 - if [[ "${{ github.event_name }}" == "push" || "${{ contains(github.event.pull_request.labels.*.name, 'test-arm-image') }}" == "true" ]] + if [[ "${{ github.event_name }}" == "push" || "${{ contains(github.event.pull_request.labels.*.name, 'test-multi-image') }}" == "true" ]] then IMAGE_PLATFORMS=linux/amd64,linux/arm64,linux/s390x,linux/ppc64le fi echo "Building image for platforms: $IMAGE_PLATFORMS" - docker buildx build --platform $IMAGE_PLATFORMS --sbom=false --provenance=false --push="${{ github.event_name == 'push' }}" \ - -t ghcr.io/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }} \ - -t quay.io/argoproj/argocd:latest . - working-directory: ./src/github.com/argoproj/argo-cd - - # sign container images - - name: Install cosign - uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # v2.8.1 - with: - cosign-release: 'v1.13.1' + echo "platforms=$IMAGE_PLATFORMS" >> $GITHUB_OUTPUT - - name: Install crane to get digest of image - uses: imjasonh/setup-crane@e82f1b9a8007d399333baba4d75915558e9fb6a4 + build-only: + needs: [set-vars] + permissions: + contents: read + packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags + id-token: write # for creating OIDC tokens for signing. + if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name != 'push' }} + uses: ./.github/workflows/image-reuse.yaml + with: + # Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations) + go-version: 1.21 + platforms: ${{ needs.set-vars.outputs.platforms }} + push: false - - name: Get digest of image - run: | - echo "IMAGE_DIGEST=$(crane digest quay.io/argoproj/argocd:latest)" >> $GITHUB_ENV + build-and-publish: + needs: [set-vars] + permissions: + contents: read + packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags + id-token: write # for creating OIDC tokens for signing. + if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }} + uses: ./.github/workflows/image-reuse.yaml + with: + quay_image_name: quay.io/argoproj/argocd:latest + ghcr_image_name: ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }} + # Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations) + go-version: 1.21 + platforms: ${{ needs.set-vars.outputs.platforms }} + push: true + secrets: + quay_username: ${{ secrets.RELEASE_QUAY_USERNAME }} + quay_password: ${{ secrets.RELEASE_QUAY_TOKEN }} + ghcr_username: ${{ github.actor }} + ghcr_password: ${{ secrets.GITHUB_TOKEN }} - - name: Sign Argo CD latest image - run: | - cosign sign --key env://COSIGN_PRIVATE_KEY quay.io/argoproj/argocd@${{ env.IMAGE_DIGEST }} - # Displays the public key to share. - cosign public-key --key env://COSIGN_PRIVATE_KEY - env: - COSIGN_PRIVATE_KEY: ${{secrets.COSIGN_PRIVATE_KEY}} - COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}} - if: ${{ github.event_name == 'push' }} + build-and-publish-provenance: # Push attestations to GHCR, latest image is polluting quay.io + needs: + - build-and-publish + permissions: + actions: read # for detecting the Github Actions environment. + id-token: write # for creating OIDC tokens for signing. + packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues) + if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }} + # Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0 + with: + image: ghcr.io/argoproj/argo-cd/argocd + digest: ${{ needs.build-and-publish.outputs.image-digest }} + registry-username: ${{ github.actor }} + secrets: + registry-password: ${{ secrets.GITHUB_TOKEN }} - # deploy + Deploy: + needs: + - build-and-publish + - set-vars + permissions: + contents: write # for git to push upgrade commit if not already deployed + packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags + if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }} + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 - run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments" - if: github.event_name == 'push' env: TOKEN: ${{ secrets.TOKEN }} - run: | - docker run -u $(id -u):$(id -g) -v $(pwd):/src -w /src --rm -t ghcr.io/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }} kustomize edit set image quay.io/argoproj/argocd=ghcr.io/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }} + docker run -u $(id -u):$(id -g) -v $(pwd):/src -w /src --rm -t ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }} kustomize edit set image quay.io/argoproj/argocd=ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }} git config --global user.email 'ci@argoproj.com' git config --global user.name 'CI' - git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ steps.image.outputs.tag }}' && git push) - if: github.event_name == 'push' + git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ needs.set-vars.outputs.image-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 + diff --git a/.github/workflows/init-release.yaml b/.github/workflows/init-release.yaml new file mode 100644 index 0000000000000..0a0430f27f96b --- /dev/null +++ b/.github/workflows/init-release.yaml @@ -0,0 +1,77 @@ +name: Init ArgoCD Release +on: + workflow_dispatch: + inputs: + TARGET_BRANCH: + description: 'TARGET_BRANCH to checkout (e.g. release-2.5)' + required: true + type: string + + TARGET_VERSION: + description: 'TARGET_VERSION to build manifests (e.g. 2.5.0-rc1) Note: the `v` prefix is not used' + required: true + type: string + +permissions: {} + +jobs: + prepare-release: + permissions: + contents: write # for peter-evans/create-pull-request to create branch + pull-requests: write # for peter-evans/create-pull-request to create a PR + name: Automatically generate version and manifests on ${{ inputs.TARGET_BRANCH }} + runs-on: ubuntu-22.04 + steps: + - name: Checkout code + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ inputs.TARGET_BRANCH }} + + - name: Check if TARGET_VERSION is well formed. + run: | + set -xue + # Target version must not contain 'v' prefix + if echo "${{ inputs.TARGET_VERSION }}" | grep -e '^v'; then + echo "::error::Target version '${{ inputs.TARGET_VERSION }}' should not begin with a 'v' prefix, refusing to continue." >&2 + exit 1 + fi + + - name: Create VERSION information + run: | + set -ue + echo "Bumping version from $(cat VERSION) to ${{ inputs.TARGET_VERSION }}" + echo "${{ inputs.TARGET_VERSION }}" > VERSION + + # We install kustomize in the dist directory + - name: Add dist to PATH + run: | + echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH + + - name: Generate new set of manifests + run: | + set -ue + make install-codegen-tools-local + make manifests-local VERSION=${{ inputs.TARGET_VERSION }} + git diff + + - name: Generate version compatibility table + run: | + git stash + bash hack/update-supported-versions.sh + git add -u . + git stash pop + + - name: Create pull request + uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2 + with: + commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}" + title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch" + body: Updating VERSION and manifests to ${{ inputs.TARGET_VERSION }} + branch: update-version + branch-suffix: random + signoff: true + labels: release + + diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/pr-title-check.yml index 4c11903ac9504..020535d7b8afa 100644 --- a/.github/workflows/pr-title-check.yml +++ b/.github/workflows/pr-title-check.yml @@ -2,15 +2,11 @@ name: "Lint PR" on: pull_request_target: - types: - - opened - - edited - - synchronize + types: [opened, edited, reopened, synchronize] # IMPORTANT: No checkout actions, scripts, or builds should be added to this workflow. Permissions should always be used -# with extreme caution. -permissions: - contents: read +# with extreme caution. https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target +permissions: {} # PR updates can happen in quick succession leading to this # workflow being trigger a number of times. This limits it @@ -18,24 +14,16 @@ permissions: concurrency: group: ${{ github.workflow }}-${{ github.ref }} + jobs: - main: + validate: permissions: - pull-requests: read # for amannn/action-semantic-pull-request to analyze PRs - statuses: write # for amannn/action-semantic-pull-request to mark status of analyzed PR - name: Validate PR title + contents: read + pull-requests: read + name: Validate PR Title runs-on: ubuntu-latest steps: - # IMPORTANT: Carefully review changes when updating this action. Using the pull_request_target event requires caution. - - uses: amannn/action-semantic-pull-request@01d5fd8a8ebb9aafe902c40c53f0f4744f7381eb # v5.0.2 + - uses: thehanimo/pr-title-checker@0cf5902181e78341bb97bb06646396e5bd354b3f # v1.4.0 with: - types: | - feat - fix - docs - test - ci - chore - [Bot] docs - env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + configuration_path: ".github/pr-title-checker-config.json" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 6899fedff51f7..d332c075d0bd0 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,266 +1,161 @@ -name: Create ArgoCD release +name: Publish ArgoCD Release on: push: tags: - - "release-v*" - - "!release-v1.5*" - - "!release-v1.4*" - - "!release-v1.3*" - - "!release-v1.2*" - - "!release-v1.1*" - - "!release-v1.0*" - - "!release-v0*" + - 'v*' + - '!v2.4*' + - '!v2.5*' + - '!v2.6*' -env: - GOLANG_VERSION: '1.18' +permissions: {} -permissions: - contents: read +env: + GOLANG_VERSION: '1.21' # Note: go-version must also be set in job argocd-image.with.go-version jobs: - prepare-release: + argocd-image: + permissions: + contents: read + id-token: write # for creating OIDC tokens for signing. + packages: write # used to push images to `ghcr.io` if used. + if: github.repository == 'argoproj/argo-cd' + uses: ./.github/workflows/image-reuse.yaml + with: + quay_image_name: quay.io/argoproj/argocd:${{ github.ref_name }} + # Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations) + go-version: 1.21 + platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le + push: true + secrets: + quay_username: ${{ secrets.RELEASE_QUAY_USERNAME }} + quay_password: ${{ secrets.RELEASE_QUAY_TOKEN }} + + argocd-image-provenance: + needs: [argocd-image] + permissions: + actions: read # for detecting the Github Actions environment. + id-token: write # for creating OIDC tokens for signing. + packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues) + # Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator + if: github.repository == 'argoproj/argo-cd' + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0 + with: + image: quay.io/argoproj/argocd + digest: ${{ needs.argocd-image.outputs.image-digest }} + secrets: + registry-username: ${{ secrets.RELEASE_QUAY_USERNAME }} + registry-password: ${{ secrets.RELEASE_QUAY_TOKEN }} + + goreleaser: + needs: + - argocd-image + - argocd-image-provenance permissions: - contents: write # To push changes to release branch - name: Perform automatic release on trigger ${{ github.ref }} + contents: write # used for uploading assets if: github.repository == 'argoproj/argo-cd' runs-on: ubuntu-22.04 - env: - # The name of the tag as supplied by the GitHub event - SOURCE_TAG: ${{ github.ref }} - # The image namespace where Docker image will be published to - IMAGE_NAMESPACE: quay.io/argoproj - # Whether to create & push image and release assets - DRY_RUN: false - # Whether a draft release should be created, instead of public one - DRAFT_RELEASE: false - # Whether to update homebrew with this release as well - # Set RELEASE_HOMEBREW_TOKEN secret in repository for this to work - needs - # access to public repositories - UPDATE_HOMEBREW: false - # Name of the GitHub user for Git config - GIT_USERNAME: argo-bot - # E-Mail of the GitHub user for Git config - GIT_EMAIL: argoproj@gmail.com + outputs: + hashes: ${{ steps.hash.outputs.hashes }} + steps: - name: Checkout code - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - - name: Check if the published tag is well formed and setup vars - run: | - set -xue - # Target version must match major.minor.patch and optional -rcX suffix - # where X must be a number. - TARGET_VERSION=${SOURCE_TAG#*release-v} - if ! echo "${TARGET_VERSION}" | egrep '^[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)*$'; then - echo "::error::Target version '${TARGET_VERSION}' is malformed, refusing to continue." >&2 - exit 1 - fi - - # Target branch is the release branch we're going to operate on - # Its name is 'release-.' - TARGET_BRANCH="release-${TARGET_VERSION%\.[0-9]*}" - - # The release tag is the source tag, minus the release- prefix - RELEASE_TAG="${SOURCE_TAG#*release-}" - - # Whether this is a pre-release (indicated by -rc suffix) - PRE_RELEASE=false - if echo "${RELEASE_TAG}" | egrep -- '-rc[0-9]+$'; then - PRE_RELEASE=true - fi - - # We must not have a release trigger within the same release branch, - # because that means a release for this branch is already running. - if git tag -l | grep "release-v${TARGET_VERSION%\.[0-9]*}" | grep -v "release-v${TARGET_VERSION}"; then - echo "::error::Another release for branch ${TARGET_BRANCH} is currently in progress." - exit 1 - fi - - # Ensure that release do not yet exist - if git rev-parse ${RELEASE_TAG}; then - echo "::error::Release tag ${RELEASE_TAG} already exists in repository. Refusing to continue." - exit 1 - fi + - name: Fetch all tags + run: git fetch --force --tags - # Make the variables available in follow-up steps - echo "TARGET_VERSION=${TARGET_VERSION}" >> $GITHUB_ENV - echo "TARGET_BRANCH=${TARGET_BRANCH}" >> $GITHUB_ENV - echo "RELEASE_TAG=${RELEASE_TAG}" >> $GITHUB_ENV - echo "PRE_RELEASE=${PRE_RELEASE}" >> $GITHUB_ENV - - - name: Check if our release tag has a correct annotation + - name: Set GORELEASER_PREVIOUS_TAG # Workaround, GoReleaser uses 'git-describe' to determine a previous tag. Our tags are created in realease branches. run: | - set -ue - # Fetch all tag information as well - git fetch --prune --tags --force - - echo "=========== BEGIN COMMIT MESSAGE =============" - git show ${SOURCE_TAG} - echo "============ END COMMIT MESSAGE ==============" - - # Quite dirty hack to get the release notes from the annotated tag - # into a temporary file. - RELEASE_NOTES=$(mktemp -p /tmp release-notes.XXXXXX) - - prefix=true - begin=false - git show ${SOURCE_TAG} | while read line; do - # Whatever is in commit history for the tag, we only want that - # annotation from our tag. We discard everything else. - if test "$begin" = "false"; then - if echo "$line" | grep -q "tag ${SOURCE_TAG#refs/tags/}"; then begin="true"; fi - continue - fi - if test "$prefix" = "true"; then - if test -z "$line"; then prefix=false; fi - else - if echo "$line" | egrep -q '^commit [0-9a-f]+'; then - break - fi - echo "$line" >> ${RELEASE_NOTES} - fi - done - - # For debug purposes - echo "============BEGIN RELEASE NOTES=================" - cat ${RELEASE_NOTES} - echo "=============END RELEASE NOTES==================" - - # Too short release notes are suspicious. We need at least 100 bytes. - relNoteLen=$(stat -c '%s' $RELEASE_NOTES) - if test $relNoteLen -lt 100; then - echo "::error::No release notes provided in tag annotation (or tag is not annotated)" - exit 1 - fi - - # Check for magic string '## Quick Start' in head of release notes - if ! head -2 ${RELEASE_NOTES} | grep -iq '## Quick Start'; then - echo "::error::Release notes seem invalid, quick start section not found." - exit 1 + set -xue + if echo ${{ github.ref_name }} | grep -E -- '-rc1+$';then + echo "GORELEASER_PREVIOUS_TAG=$(git -c 'versionsort.suffix=-rc' tag --list --sort=version:refname | tail -n 2 | head -n 1)" >> $GITHUB_ENV + else + echo "This is not the first release on the branch, Using GoReleaser defaults" fi - # We store path to temporary release notes file for later reading, we - # need it when creating release. - echo "RELEASE_NOTES=${RELEASE_NOTES}" >> $GITHUB_ENV - - name: Setup Golang - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 with: go-version: ${{ env.GOLANG_VERSION }} - - name: Setup Git author information - run: | - set -ue - git config --global user.email "${GIT_EMAIL}" - git config --global user.name "${GIT_USERNAME}" - - - name: Checkout corresponding release branch - run: | - set -ue - echo "Switching to release branch '${TARGET_BRANCH}'" - if ! git checkout ${TARGET_BRANCH}; then - echo "::error::Checking out release branch '${TARGET_BRANCH}' for target version '${TARGET_VERSION}' (tagged '${RELEASE_TAG}') failed. Does it exist in repo?" - exit 1 - fi - - - name: Create VERSION information - run: | - set -ue - echo "Bumping version from $(cat VERSION) to ${TARGET_VERSION}" - echo "${TARGET_VERSION}" > VERSION - git commit -m "Bump version to ${TARGET_VERSION}" VERSION - - - name: Generate new set of manifests + - name: Set environment variables for ldflags + id: set_ldflag run: | - set -ue - make install-codegen-tools-local - make manifests-local VERSION=${TARGET_VERSION} - git diff - git commit manifests/ -m "Bump version to ${TARGET_VERSION}" - - - name: Create the release tag - run: | - set -ue - echo "Creating release ${RELEASE_TAG}" - git tag ${RELEASE_TAG} - - - name: Login to docker repositories - env: - DOCKER_USERNAME: ${{ secrets.RELEASE_DOCKERHUB_USERNAME }} - DOCKER_TOKEN: ${{ secrets.RELEASE_DOCKERHUB_TOKEN }} - QUAY_USERNAME: ${{ secrets.RELEASE_QUAY_USERNAME }} - QUAY_TOKEN: ${{ secrets.RELEASE_QUAY_TOKEN }} - run: | - set -ue - docker login quay.io --username "${QUAY_USERNAME}" --password-stdin <<< "${QUAY_TOKEN}" - # Remove the following when Docker Hub is gone - docker login --username "${DOCKER_USERNAME}" --password-stdin <<< "${DOCKER_TOKEN}" - if: ${{ env.DRY_RUN != 'true' }} - - - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0 - - uses: docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325 # v2.2.1 - - name: Build and push Docker image for release - run: | - set -ue - git clean -fd - mkdir -p dist/ - docker buildx build --platform linux/amd64,linux/arm64,linux/s390x,linux/ppc64le --sbom=false --provenance=false --push -t ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION} -t argoproj/argocd:v${TARGET_VERSION} . - make release-cli - make checksums - chmod +x ./dist/argocd-linux-amd64 - ./dist/argocd-linux-amd64 version --client - if: ${{ env.DRY_RUN != 'true' }} + echo "KUBECTL_VERSION=$(go list -m k8s.io/client-go | head -n 1 | rev | cut -d' ' -f1 | rev)" >> $GITHUB_ENV + echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV - - name: Install cosign - uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # v2.8.1 + - name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@4d9e71b726748f254fe64fa44d273194bd18ec91 with: - cosign-release: 'v1.13.1' - - - name: Install crane to get digest of image - uses: imjasonh/setup-crane@e82f1b9a8007d399333baba4d75915558e9fb6a4 + large-packages: false + docker-images: false + swap-storage: false + tool-cache: false + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0 + id: run-goreleaser + with: + version: latest + args: release --clean --timeout 55m + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + KUBECTL_VERSION: ${{ env.KUBECTL_VERSION }} + GIT_TREE_STATE: ${{ env.GIT_TREE_STATE }} - - name: Get digest of image + - name: Generate subject for provenance + id: hash + env: + ARTIFACTS: "${{ steps.run-goreleaser.outputs.artifacts }}" run: | - echo "IMAGE_DIGEST=$(crane digest quay.io/argoproj/argocd:v${TARGET_VERSION})" >> $GITHUB_ENV + set -euo pipefail - - name: Sign Argo CD container images and assets - run: | - cosign sign --key env://COSIGN_PRIVATE_KEY ${IMAGE_NAMESPACE}/argocd@${{ env.IMAGE_DIGEST }} - cosign sign-blob --key env://COSIGN_PRIVATE_KEY ./dist/argocd-${TARGET_VERSION}-checksums.txt > ./dist/argocd-${TARGET_VERSION}-checksums.sig - # Retrieves the public key to release as an asset - cosign public-key --key env://COSIGN_PRIVATE_KEY > ./dist/argocd-cosign.pub - env: - COSIGN_PRIVATE_KEY: ${{secrets.COSIGN_PRIVATE_KEY}} - COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}} - if: ${{ env.DRY_RUN != 'true' }} + hashes=$(echo $ARTIFACTS | jq --raw-output '.[] | {name, "digest": (.extra.Digest // .extra.Checksum)} | select(.digest) | {digest} + {name} | join(" ") | sub("^sha256:";"")' | base64 -w0) + if test "$hashes" = ""; then # goreleaser < v1.13.0 + checksum_file=$(echo "$ARTIFACTS" | jq -r '.[] | select (.type=="Checksum") | .path') + hashes=$(cat $checksum_file | base64 -w0) + fi + echo "hashes=$hashes" >> $GITHUB_OUTPUT - - name: Read release notes file - id: release-notes - uses: juliangruber/read-file-action@02bbba9876a8f870efd4ad64e3b9088d3fb94d4b # v1.1.6 + goreleaser-provenance: + needs: [goreleaser] + permissions: + actions: read # for detecting the Github Actions environment + id-token: write # Needed for provenance signing and ID + contents: write # Needed for release uploads + if: github.repository == 'argoproj/argo-cd' + # Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0 + with: + base64-subjects: "${{ needs.goreleaser.outputs.hashes }}" + provenance-name: "argocd-cli.intoto.jsonl" + upload-assets: true + + generate-sbom: + name: Create SBOM and generate hash + needs: + - argocd-image + - goreleaser + permissions: + contents: write # Needed for release uploads + outputs: + hashes: ${{ steps.sbom-hash.outputs.hashes}} + if: github.repository == 'argoproj/argo-cd' + runs-on: ubuntu-22.04 + steps: + - name: Checkout code + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 with: - path: ${{ env.RELEASE_NOTES }} - - - name: Push changes to release branch - run: | - set -ue - git push origin ${TARGET_BRANCH} - git push origin ${RELEASE_TAG} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} - - name: Dry run GitHub release - uses: actions/create-release@0cb9c9b65d5d1901c1f53e5e66eaf4afd303e70e # v1.1.4 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - id: create_release + - name: Setup Golang + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: - tag_name: ${{ env.RELEASE_TAG }} - release_name: ${{ env.RELEASE_TAG }} - draft: ${{ env.DRAFT_RELEASE }} - prerelease: ${{ env.PRE_RELEASE }} - body: ${{ steps.release-notes.outputs.content }} - if: ${{ env.DRY_RUN == 'true' }} + go-version: ${{ env.GOLANG_VERSION }} - name: Generate SBOM (spdx) id: spdx-builder @@ -273,7 +168,7 @@ jobs: # managers (gomod, yarn, npm). PROJECT_FOLDERS: ".,./ui" # full qualified name of the docker image to be inspected - DOCKER_IMAGE: ${{env.IMAGE_NAMESPACE}}/argocd:v${{env.TARGET_VERSION}} + DOCKER_IMAGE: quay.io/argoproj/argocd:${{ github.ref_name }} run: | yarn install --cwd ./ui go install github.com/spdx/spdx-sbom-generator/cmd/generator@$SPDX_GEN_VERSION @@ -291,43 +186,122 @@ jobs: fi cd /tmp && tar -zcf sbom.tar.gz *.spdx - if: ${{ env.DRY_RUN != 'true' }} - - - name: Sign sbom + + - name: Generate SBOM hash + shell: bash + id: sbom-hash run: | - cosign sign-blob --key env://COSIGN_PRIVATE_KEY /tmp/sbom.tar.gz > /tmp/sbom.tar.gz.sig - env: - COSIGN_PRIVATE_KEY: ${{secrets.COSIGN_PRIVATE_KEY}} - COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}} - if: ${{ env.DRY_RUN != 'true' }} - - - name: Create GitHub release + # sha256sum generates sha256 hash for sbom. + # base64 -w0 encodes to base64 and outputs on a single line. + # sha256sum /tmp/sbom.tar.gz ... | base64 -w0 + echo "hashes=$(sha256sum /tmp/sbom.tar.gz | base64 -w0)" >> "$GITHUB_OUTPUT" + + - name: Upload SBOM uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - name: ${{ env.RELEASE_TAG }} - tag_name: ${{ env.RELEASE_TAG }} - draft: ${{ env.DRAFT_RELEASE }} - prerelease: ${{ env.PRE_RELEASE }} - body: ${{ steps.release-notes.outputs.content }} # Pre-pended to the generated notes files: | - dist/argocd-* /tmp/sbom.tar.gz - /tmp/sbom.tar.gz.sig - if: ${{ env.DRY_RUN != 'true' }} - - - name: Update homebrew formula - env: - HOMEBREW_TOKEN: ${{ secrets.RELEASE_HOMEBREW_TOKEN }} - uses: dawidd6/action-homebrew-bump-formula@02e79d9da43d79efa846d73695b6052cbbdbf48a # v3.8.3 + + sbom-provenance: + needs: [generate-sbom] + permissions: + actions: read # for detecting the Github Actions environment + id-token: write # Needed for provenance signing and ID + contents: write # Needed for release uploads + if: github.repository == 'argoproj/argo-cd' + # Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0 + with: + base64-subjects: "${{ needs.generate-sbom.outputs.hashes }}" + provenance-name: "argocd-sbom.intoto.jsonl" + upload-assets: true + + post-release: + needs: + - argocd-image + - goreleaser + - generate-sbom + permissions: + contents: write # Needed to push commit to update stable tag + pull-requests: write # Needed to create PR for VERSION update. + if: github.repository == 'argoproj/argo-cd' + runs-on: ubuntu-22.04 + steps: + - name: Checkout code + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 with: - token: ${{env.HOMEBREW_TOKEN}} - formula: argocd - if: ${{ env.HOMEBREW_TOKEN != '' && env.UPDATE_HOMEBREW == 'true' && env.PRE_RELEASE != 'true' }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} - - name: Delete original request tag from repository + - name: Setup Git author information run: | set -ue - git push --delete origin ${SOURCE_TAG} - if: ${{ always() }} + git config --global user.email 'ci@argoproj.com' + git config --global user.name 'CI' + + - name: Check if tag is the latest version and not a pre-release + run: | + set -xue + # Fetch all tag information + git fetch --prune --tags --force + + LATEST_TAG=$(git -c 'versionsort.suffix=-rc' tag --list --sort=version:refname | tail -n1) + + PRE_RELEASE=false + # Check if latest tag is a pre-release + if echo $LATEST_TAG | grep -E -- '-rc[0-9]+$';then + PRE_RELEASE=true + fi + + # Ensure latest tag matches github.ref_name & not a pre-release + if [[ $LATEST_TAG == ${{ github.ref_name }} ]] && [[ $PRE_RELEASE != 'true' ]];then + echo "TAG_STABLE=true" >> $GITHUB_ENV + else + echo "TAG_STABLE=false" >> $GITHUB_ENV + fi + + - name: Update stable tag to latest version + run: | + git tag -f stable ${{ github.ref_name }} + git push -f origin stable + if: ${{ env.TAG_STABLE == 'true' }} + + - name: Check to see if VERSION should be updated on master branch + run: | + set -xue + SOURCE_TAG=${{ github.ref_name }} + VERSION_REF="${SOURCE_TAG#*v}" + COMMIT_HASH=$(git rev-parse HEAD) + if echo "$VERSION_REF" | grep -E -- '^[0-9]+\.[0-9]+\.0-rc1';then + VERSION=$(awk 'BEGIN {FS=OFS="."} {$2++; print}' <<< "${VERSION_REF%-rc1}") + echo "Updating VERSION to: $VERSION" + echo "UPDATE_VERSION=true" >> $GITHUB_ENV + echo "NEW_VERSION=$VERSION" >> $GITHUB_ENV + echo "COMMIT_HASH=$COMMIT_HASH" >> $GITHUB_ENV + else + echo "Not updating VERSION" + echo "UPDATE_VERSION=false" >> $GITHUB_ENV + fi + + - name: Update VERSION on master branch + run: | + echo ${{ env.NEW_VERSION }} > VERSION + # Replace the 'project-release: vX.X.X-rcX' line in SECURITY-INSIGHTS.yml + sed -i "s/project-release: v.*$/project-release: v${{ env.NEW_VERSION }}/" SECURITY-INSIGHTS.yml + # Update the 'commit-hash: XXXXXXX' line in SECURITY-INSIGHTS.yml + sed -i "s/commit-hash: .*/commit-hash: ${{ env.NEW_VERSION }}/" SECURITY-INSIGHTS.yml + if: ${{ env.UPDATE_VERSION == 'true' }} + + - name: Create PR to update VERSION on master branch + uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2 + with: + commit-message: Bump version in master + title: "chore: Bump version in master" + body: All images built from master should indicate which version we are on track for. + signoff: true + branch: update-version + branch-suffix: random + base: master + if: ${{ env.UPDATE_VERSION == 'true' }} diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml new file mode 100644 index 0000000000000..ec3151949541d --- /dev/null +++ b/.github/workflows/scorecard.yaml @@ -0,0 +1,67 @@ +name: Scorecards supply-chain security +on: + # Only the default branch is supported. + branch_protection_rule: + schedule: + - cron: "39 9 * * 2" + push: + branches: ["master"] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecards analysis + runs-on: ubuntu-22.04 + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Used to receive a badge. (Upcoming feature) + id-token: write + # Needs for private repositories. + contents: read + actions: read + if: github.repository == 'argoproj/argo-cd' + + steps: + - name: "Checkout code" + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + with: + results_file: results.sarif + results_format: sarif + # (Optional) Read-only PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecards on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_READ_TOKEN }} + + # Publish the results for public repositories to enable scorecard badges. For more details, see + # https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories, `publish_results` will automatically be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@83a02f7883b12e0e4e1a146174f5e2292a01e601 # v2.16.4 + with: + sarif_file: results.sarif diff --git a/.github/workflows/update-snyk.yaml b/.github/workflows/update-snyk.yaml index 2e7d68345f2b1..b4d98134e84ad 100644 --- a/.github/workflows/update-snyk.yaml +++ b/.github/workflows/update-snyk.yaml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout code - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} - name: Build reports diff --git a/.gitignore b/.gitignore index 869bb876f8110..ab17deb0db139 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ node_modules/ .kube/ ./test/cmp/*.sock .envrc.remote +.*.swp # ignore built binaries cmd/argocd/argocd diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 0560d5987f427..d105f49fde2b1 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -1,4 +1,4 @@ -FROM gitpod/workspace-full +FROM gitpod/workspace-full@sha256:511cecde4dc129ca9eb4cc4c479d61f95e5485ebe320a07f5b902f11899956a3 USER root @@ -13,6 +13,8 @@ ENV GOCACHE=/go-build-cache RUN apt-get install redis-server -y RUN go install github.com/mattn/goreman@latest +RUN chown -R gitpod:gitpod /go-build-cache + USER gitpod ENV ARGOCD_REDIS_LOCAL=true diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000000000..26341aa1d80c1 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,121 @@ +project_name: argocd + +before: + hooks: + - go mod download + - make build-ui + +builds: + - id: argocd-cli + main: ./cmd + binary: argocd-{{ .Os}}-{{ .Arch}} + env: + - CGO_ENABLED=0 + flags: + - -v + ldflags: + - -X github.com/argoproj/argo-cd/v2/common.version={{ .Version }} + - -X github.com/argoproj/argo-cd/v2/common.buildDate={{ .Date }} + - -X github.com/argoproj/argo-cd/v2/common.gitCommit={{ .FullCommit }} + - -X github.com/argoproj/argo-cd/v2/common.gitTreeState={{ .Env.GIT_TREE_STATE }} + - -X github.com/argoproj/argo-cd/v2/common.kubectlVersion={{ .Env.KUBECTL_VERSION }} + - -extldflags="-static" + goos: + - linux + - darwin + - windows + goarch: + - amd64 + - arm64 + - s390x + - ppc64le + ignore: + - goos: darwin + goarch: s390x + - goos: darwin + goarch: ppc64le + - goos: windows + goarch: s390x + - goos: windows + goarch: ppc64le + - goos: windows + goarch: arm64 + +archives: + - id: argocd-archive + builds: + - argocd-cli + name_template: |- + {{ .ProjectName }}-{{ .Os }}-{{ .Arch }} + format: binary + +checksum: + name_template: 'cli_checksums.txt' + algorithm: sha256 + +release: + prerelease: auto + draft: false + header: | + ## Quick Start + + ### Non-HA: + + ```shell + kubectl create namespace argocd + kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/{{.Tag}}/manifests/install.yaml + ``` + + ### HA: + + ```shell + kubectl create namespace argocd + kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/{{.Tag}}/manifests/ha/install.yaml + ``` + + ## Release Signatures and Provenance + + All Argo CD container images are signed by cosign. A Provenance is generated for container images and CLI binaries which meet the SLSA Level 3 specifications. See the [documentation](https://argo-cd.readthedocs.io/en/stable/operator-manual/signed-release-assets) on how to verify. + + + ## Upgrading + + If upgrading from a different minor version, be sure to read the [upgrading](https://argo-cd.readthedocs.io/en/stable/operator-manual/upgrading/overview/) documentation. + footer: | + **Full Changelog**: https://github.com/argoproj/argo-cd/compare/{{ .PreviousTag }}...{{ .Tag }} + + + + +snapshot: #### To be removed for PR + name_template: "2.6.0" + +changelog: + use: + github + sort: asc + abbrev: 0 + groups: # Regex use RE2 syntax as defined here: https://github.com/google/re2/wiki/Syntax. + - title: 'Features' + regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$' + order: 100 + - title: 'Bug fixes' + regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$' + order: 200 + - title: 'Documentation' + regexp: '^.*?docs(\([[:word:]]+\))??!?:.+$' + order: 300 + - title: 'Dependency updates' + regexp: '^.*?(feat|fix|chore)\(deps?.+\)!?:.+$' + order: 400 + - title: 'Other work' + order: 999 + filters: + exclude: + - '^test:' + - '^.*?Bump(\([[:word:]]+\))?.+$' + - '^.*?[Bot](\([[:word:]]+\))?.+$' + + +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json + diff --git a/.readthedocs.yml b/.readthedocs.yml index 7b50ab9415bd7..3d69498d27c09 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -4,4 +4,8 @@ mkdocs: fail_on_warning: false python: install: - - requirements: docs/requirements.txt \ No newline at end of file + - requirements: docs/requirements.txt +build: + os: "ubuntu-22.04" + tools: + python: "3.7" diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000000..83bb38871d96d --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,12 @@ +# All +** @argoproj/argocd-approvers + +# Docs +/docs/** @argoproj/argocd-approvers @argoproj/argocd-approvers-docs +/USERS.md @argoproj/argocd-approvers @argoproj/argocd-approvers-docs +/README.md @argoproj/argocd-approvers @argoproj/argocd-approvers-docs +/mkdocs.yml @argoproj/argocd-approvers @argoproj/argocd-approvers-docs + +# CI +/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci +/.goreleaser.yaml @argoproj/argocd-approvers @argoproj/argocd-approvers-ci diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000000..0cef196dca5a1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1 @@ +Please refer to [the Contribution Guide](https://argo-cd.readthedocs.io/en/latest/developer-guide/code-contributions/) diff --git a/Dockerfile b/Dockerfile index 3a434bc3bbda2..a73da0be1f067 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,10 @@ -ARG BASE_IMAGE=docker.io/library/ubuntu:22.04 +ARG BASE_IMAGE=docker.io/library/ubuntu:22.04@sha256:0bced47fffa3361afa981854fcabcd4577cd43cebbb808cea2b1f33a3dd7f508 #################################################################################################### # Builder image # Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image # Also used as the image in CI jobs so needs all dependencies #################################################################################################### -FROM docker.io/library/golang:1.18 AS builder +FROM docker.io/library/golang:1.21.8@sha256:856073656d1a517517792e6cdd2f7a5ef080d3ca2dff33e518c8412f140fdd2d AS builder RUN echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list @@ -28,7 +28,7 @@ WORKDIR /tmp COPY hack/install.sh hack/tool-versions.sh ./ COPY hack/installers installers -RUN ./install.sh helm-linux && \ +RUN ./install.sh helm && \ INSTALL_PATH=/usr/local/bin ./install.sh kustomize #################################################################################################### @@ -36,6 +36,8 @@ RUN ./install.sh helm-linux && \ #################################################################################################### FROM $BASE_IMAGE AS argocd-base +LABEL org.opencontainers.image.source="https://github.com/argoproj/argo-cd" + USER root ENV ARGOCD_USER_ID=999 @@ -49,7 +51,7 @@ RUN groupadd -g $ARGOCD_USER_ID argocd && \ apt-get update && \ apt-get dist-upgrade -y && \ apt-get install -y \ - git git-lfs tini gpg tzdata && \ + git git-lfs tini gpg tzdata connect-proxy && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* @@ -81,7 +83,7 @@ WORKDIR /home/argocd #################################################################################################### # Argo CD UI stage #################################################################################################### -FROM --platform=$BUILDPLATFORM docker.io/library/node:12.18.4 AS argocd-ui +FROM --platform=$BUILDPLATFORM docker.io/library/node:21.6.2@sha256:65998e325b06014d4f1417a8a6afb1540d1ac66521cca76f2221a6953947f9ee AS argocd-ui WORKDIR /src COPY ["ui/package.json", "ui/yarn.lock", "./"] @@ -99,7 +101,7 @@ RUN HOST_ARCH=$TARGETARCH NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OP #################################################################################################### # Argo CD Build stage which performs the actual build of Argo CD binaries #################################################################################################### -FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.18 AS argocd-build +FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.21.3@sha256:02d7116222536a5cf0fcf631f90b507758b669648e0f20186d2dc94a9b419a9b AS argocd-build WORKDIR /go/src/github.com/argoproj/argo-cd @@ -111,7 +113,18 @@ COPY . . COPY --from=argocd-ui /src/dist/app /go/src/github.com/argoproj/argo-cd/ui/dist/app ARG TARGETOS ARG TARGETARCH -RUN GOOS=$TARGETOS GOARCH=$TARGETARCH make argocd-all +# These build args are optional; if not specified the defaults will be taken from the Makefile +ARG GIT_TAG +ARG BUILD_DATE +ARG GIT_TREE_STATE +ARG GIT_COMMIT +RUN GIT_COMMIT=$GIT_COMMIT \ + GIT_TREE_STATE=$GIT_TREE_STATE \ + GIT_TAG=$GIT_TAG \ + BUILD_DATE=$BUILD_DATE \ + GOOS=$TARGETOS \ + GOARCH=$TARGETARCH \ + make argocd-all #################################################################################################### # Final image @@ -130,3 +143,4 @@ RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server && \ ln -s /usr/local/bin/argocd /usr/local/bin/argocd-k8s-auth USER $ARGOCD_USER_ID +ENTRYPOINT ["/usr/bin/tini", "--"] diff --git a/Makefile b/Makefile index 37edfbd618021..96275f9bff76e 100644 --- a/Makefile +++ b/Makefile @@ -3,31 +3,41 @@ CURRENT_DIR=$(shell pwd) DIST_DIR=${CURRENT_DIR}/dist CLI_NAME=argocd BIN_NAME=argocd +CGO_FLAG=0 GEN_RESOURCES_CLI_NAME=argocd-resources-gen HOST_OS:=$(shell go env GOOS) HOST_ARCH:=$(shell go env GOARCH) +TARGET_ARCH?=linux/amd64 + VERSION=$(shell cat ${CURRENT_DIR}/VERSION) -BUILD_DATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ') -GIT_COMMIT=$(shell git rev-parse HEAD) -GIT_TAG=$(shell if [ -z "`git status --porcelain`" ]; then git describe --exact-match --tags HEAD 2>/dev/null; fi) -GIT_TREE_STATE=$(shell if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi) +BUILD_DATE:=$(if $(BUILD_DATE),$(BUILD_DATE),$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')) +GIT_COMMIT:=$(if $(GIT_COMMIT),$(GIT_COMMIT),$(shell git rev-parse HEAD)) +GIT_TAG:=$(if $(GIT_TAG),$(GIT_TAG),$(shell if [ -z "`git status --porcelain`" ]; then git describe --exact-match --tags HEAD 2>/dev/null; fi)) +GIT_TREE_STATE:=$(if $(GIT_TREE_STATE),$(GIT_TREE_STATE),$(shell if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)) VOLUME_MOUNT=$(shell if test "$(go env GOOS)" = "darwin"; then echo ":delegated"; elif test selinuxenabled; then echo ":delegated"; else echo ""; fi) KUBECTL_VERSION=$(shell go list -m k8s.io/client-go | head -n 1 | rev | cut -d' ' -f1 | rev) GOPATH?=$(shell if test -x `which go`; then go env GOPATH; else echo "$(HOME)/go"; fi) GOCACHE?=$(HOME)/.cache/go-build +# Docker command to use +DOCKER?=docker +ifeq ($(DOCKER),podman) +PODMAN_ARGS=--userns keep-id +else +PODMAN_ARGS= +endif + DOCKER_SRCDIR?=$(GOPATH)/src DOCKER_WORKDIR?=/go/src/github.com/argoproj/argo-cd ARGOCD_PROCFILE?=Procfile -# Strict mode has been disabled in latest versions of mkdocs-material. -# Thus pointing to the older image of mkdocs-material matching the version used by argo-cd. -MKDOCS_DOCKER_IMAGE?=squidfunk/mkdocs-material:4.1.1 +# pointing to python 3.7 to match https://github.com/argoproj/argo-cd/blob/master/.readthedocs.yml +MKDOCS_DOCKER_IMAGE?=python:3.7-alpine MKDOCS_RUN_ARGS?= # Configuration for building argocd-test-tools image @@ -47,7 +57,7 @@ ARGOCD_E2E_DEX_PORT?=5556 ARGOCD_E2E_YARN_HOST?=localhost ARGOCD_E2E_DISABLE_AUTH?= -ARGOCD_E2E_TEST_TIMEOUT?=45m +ARGOCD_E2E_TEST_TIMEOUT?=90m ARGOCD_IN_CI?=false ARGOCD_TEST_E2E?=true @@ -74,7 +84,7 @@ SUDO?= # Runs any command in the argocd-test-utils container in server mode # Server mode container will start with uid 0 and drop privileges during runtime define run-in-test-server - $(SUDO) docker run --rm -it \ + $(SUDO) $(DOCKER) run --rm -it \ --name argocd-test-server \ -u $(CONTAINER_UID):$(CONTAINER_GID) \ -e USER_ID=$(CONTAINER_UID) \ @@ -99,13 +109,14 @@ define run-in-test-server -p ${ARGOCD_E2E_APISERVER_PORT}:8080 \ -p 4000:4000 \ -p 5000:5000 \ + $(PODMAN_ARGS) \ $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \ bash -c "$(1)" endef # Runs any command in the argocd-test-utils container in client mode define run-in-test-client - $(SUDO) docker run --rm -it \ + $(SUDO) $(DOCKER) run --rm -it \ --name argocd-test-client \ -u $(CONTAINER_UID):$(CONTAINER_GID) \ -e HOME=/home/user \ @@ -120,13 +131,14 @@ define run-in-test-client -v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \ -v /tmp:/tmp${VOLUME_MOUNT} \ -w ${DOCKER_WORKDIR} \ + $(PODMAN_ARGS) \ $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \ bash -c "$(1)" endef # define exec-in-test-server - $(SUDO) docker exec -it -u $(CONTAINER_UID):$(CONTAINER_GID) -e ARGOCD_E2E_RECORD=$(ARGOCD_E2E_RECORD) -e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) argocd-test-server $(1) + $(SUDO) $(DOCKER) exec -it -u $(CONTAINER_UID):$(CONTAINER_GID) -e ARGOCD_E2E_RECORD=$(ARGOCD_E2E_RECORD) -e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) argocd-test-server $(1) endef PATH:=$(PATH):$(PWD)/hack @@ -146,7 +158,8 @@ override LDFLAGS += \ -X ${PACKAGE}.buildDate=${BUILD_DATE} \ -X ${PACKAGE}.gitCommit=${GIT_COMMIT} \ -X ${PACKAGE}.gitTreeState=${GIT_TREE_STATE}\ - -X ${PACKAGE}.kubectlVersion=${KUBECTL_VERSION} + -X ${PACKAGE}.kubectlVersion=${KUBECTL_VERSION}\ + -X "${PACKAGE}.extraBuildInfo=${EXTRA_BUILD_INFO}" ifeq (${STATIC_BUILD}, true) override LDFLAGS += -extldflags "-static" @@ -172,29 +185,21 @@ endif .PHONY: all all: cli image -# 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: ensure-gopath +gogen: export GO111MODULE=off go generate ./util/argo/... .PHONY: protogen -protogen: ensure-gopath mod-vendor-local +protogen: mod-vendor-local protogen-fast + +.PHONY: protogen-fast +protogen-fast: export GO111MODULE=off ./hack/generate-proto.sh .PHONY: openapigen -openapigen: ensure-gopath +openapigen: export GO111MODULE=off ./hack/update-openapi.sh @@ -209,19 +214,22 @@ notification-docs: .PHONY: clientgen -clientgen: ensure-gopath +clientgen: export GO111MODULE=off ./hack/update-codegen.sh .PHONY: clidocsgen -clidocsgen: ensure-gopath +clidocsgen: go run tools/cmd-docs/main.go .PHONY: codegen-local -codegen-local: ensure-gopath mod-vendor-local notification-docs notification-catalog gogen protogen clientgen openapigen clidocsgen manifests-local +codegen-local: mod-vendor-local gogen protogen clientgen openapigen clidocsgen manifests-local notification-docs notification-catalog rm -rf vendor/ +.PHONY: codegen-local-fast +codegen-local-fast: gogen protogen-fast clientgen openapigen clidocsgen manifests-local notification-docs notification-catalog + .PHONY: codegen codegen: test-tools-image $(call run-in-test-client,make codegen-local) @@ -232,11 +240,11 @@ cli: test-tools-image .PHONY: cli-local cli-local: clean-debug - CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd + CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd .PHONY: gen-resources-cli-local gen-resources-cli-local: clean-debug - CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${GEN_RESOURCES_CLI_NAME} ./hack/gen-resources/cmd + CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${GEN_RESOURCES_CLI_NAME} ./hack/gen-resources/cmd .PHONY: release-cli release-cli: clean-debug build-ui @@ -251,8 +259,8 @@ release-cli: clean-debug build-ui .PHONY: test-tools-image test-tools-image: ifndef SKIP_TEST_TOOLS_IMAGE - $(SUDO) docker build --build-arg UID=$(CONTAINER_UID) -t $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) -f test/container/Dockerfile . - $(SUDO) docker tag $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) + $(SUDO) $(DOCKER) build --build-arg UID=$(CONTAINER_UID) -t $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) -f test/container/Dockerfile . + $(SUDO) $(DOCKER) tag $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) endif .PHONY: manifests-local @@ -266,25 +274,25 @@ manifests: test-tools-image # consolidated binary for cli, util, server, repo-server, controller .PHONY: argocd-all argocd-all: clean-debug - CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd + CGO_ENABLED=${CGO_FLAG} GOOS=${GOOS} GOARCH=${GOARCH} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd .PHONY: server server: clean-debug - CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd + CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd .PHONY: repo-server repo-server: - CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd + CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd .PHONY: controller controller: - CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd + CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd .PHONY: build-ui build-ui: - DOCKER_BUILDKIT=1 docker build -t argocd-ui --target argocd-ui . + DOCKER_BUILDKIT=1 $(DOCKER) build -t argocd-ui --platform=$(TARGET_ARCH) --target argocd-ui . find ./ui/dist -type f -not -name gitkeep -delete - docker run -v ${CURRENT_DIR}/ui/dist/app:/tmp/app --rm -t argocd-ui sh -c 'cp -r ./dist/app/* /tmp/app/' + $(DOCKER) run -v ${CURRENT_DIR}/ui/dist/app:/tmp/app --rm -t argocd-ui sh -c 'cp -r ./dist/app/* /tmp/app/' .PHONY: image ifeq ($(DEV_IMAGE), true) @@ -293,29 +301,29 @@ ifeq ($(DEV_IMAGE), true) # the dist directory is under .dockerignore. IMAGE_TAG="dev-$(shell git describe --always --dirty)" image: build-ui - DOCKER_BUILDKIT=1 docker build --platform=linux/amd64 -t argocd-base --target argocd-base . - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd + DOCKER_BUILDKIT=1 $(DOCKER) build --platform=$(TARGET_ARCH) -t argocd-base --target argocd-base . + CGO_ENABLED=${CGO_FLAG} GOOS=linux GOARCH=amd64 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-server ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-application-controller ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-repo-server ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-cmp-server ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-dex cp Dockerfile.dev dist - DOCKER_BUILDKIT=1 docker build --platform=linux/amd64 -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) -f dist/Dockerfile.dev dist + DOCKER_BUILDKIT=1 $(DOCKER) build --platform=$(TARGET_ARCH) -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) -f dist/Dockerfile.dev dist else image: - DOCKER_BUILDKIT=1 docker build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) . + DOCKER_BUILDKIT=1 $(DOCKER) build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) --platform=$(TARGET_ARCH) . endif - @if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) ; fi + @if [ "$(DOCKER_PUSH)" = "true" ] ; then $(DOCKER) push $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) ; fi .PHONY: armimage armimage: - docker build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)-arm . + $(DOCKER) build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)-arm . .PHONY: builder-image builder-image: - docker build -t $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) --target builder . - @if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) ; fi + $(DOCKER) build -t $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) --target builder . + @if [ "$(DOCKER_PUSH)" = "true" ] ; then $(DOCKER) push $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) ; fi .PHONY: mod-download mod-download: test-tools-image @@ -333,7 +341,7 @@ mod-vendor: test-tools-image mod-vendor-local: mod-download-local go mod vendor -# Deprecated - replace by install-local-tools +# Deprecated - replace by install-tools-local .PHONY: install-lint-tools install-lint-tools: ./hack/install.sh lint-tools @@ -349,7 +357,7 @@ lint-local: golangci-lint --version # NOTE: If you get a "Killed" OOM message, try reducing the value of GOGC # See https://github.com/golangci/golangci-lint#memory-usage-of-golangci-lint - GOGC=$(ARGOCD_LINT_GOGC) GOMAXPROCS=2 golangci-lint run --fix --verbose --timeout 3000s + GOGC=$(ARGOCD_LINT_GOGC) GOMAXPROCS=2 golangci-lint run --enable gofmt --fix --verbose --timeout 3000s --max-issues-per-linter 0 --max-same-issues 0 .PHONY: lint-ui lint-ui: test-tools-image @@ -368,7 +376,7 @@ build: test-tools-image # Build all Go code (local version) .PHONY: build-local build-local: - go build -v `go list ./... | grep -v 'resource_customizations\|test/e2e'` + GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v `go list ./... | grep -v 'resource_customizations\|test/e2e'` # Run all unit tests # @@ -383,9 +391,9 @@ test: test-tools-image .PHONY: test-local test-local: if test "$(TEST_MODULE)" = ""; then \ - ./hack/test.sh -coverprofile=coverage.out `go list ./... | grep -v 'test/e2e'`; \ + DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -coverprofile=coverage.out; \ else \ - ./hack/test.sh -coverprofile=coverage.out "$(TEST_MODULE)"; \ + DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -coverprofile=coverage.out "$(TEST_MODULE)"; \ fi .PHONY: test-race @@ -397,9 +405,9 @@ test-race: test-tools-image .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'`; \ + DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -race -coverprofile=coverage.out; \ else \ - ./hack/test.sh -race -coverprofile=coverage.out "$(TEST_MODULE)"; \ + DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -race -coverprofile=coverage.out; \ fi # Run the E2E test suite. E2E test servers (see start-e2e target) must be @@ -413,7 +421,7 @@ test-e2e: test-e2e-local: cli-local # NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system export GO111MODULE=off - ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v ./test/e2e + DIST_DIR=${DIST_DIR} RERUN_FAILS=5 PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v # Spawns a shell in the test server container for debugging purposes debug-test-server: test-tools-image @@ -426,7 +434,7 @@ debug-test-client: test-tools-image # Starts e2e server in a container .PHONY: start-e2e start-e2e: test-tools-image - docker version + $(DOCKER) version mkdir -p ${GOCACHE} $(call run-in-test-server,make ARGOCD_PROCFILE=test/container/Procfile start-e2e-local) @@ -435,6 +443,7 @@ start-e2e: test-tools-image start-e2e-local: mod-vendor-local dep-ui-local cli-local kubectl create ns argocd-e2e || true kubectl create ns argocd-e2e-external || true + kubectl create ns argocd-e2e-external-2 || true kubectl config set-context --current --namespace=argocd-e2e kustomize build test/manifests/base | kubectl apply -f - kubectl apply -f https://raw.githubusercontent.com/open-cluster-management/api/a6845f2ebcb186ec26b832f60c988537a58f3859/cluster/v1alpha1/0000_04_clusters.open-cluster-management.io_placementdecisions.crd.yaml @@ -455,7 +464,9 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local ARGOCD_ZJWT_FEATURE_FLAG=always \ ARGOCD_IN_CI=$(ARGOCD_IN_CI) \ BIN_MODE=$(ARGOCD_BIN_MODE) \ - ARGOCD_APPLICATION_NAMESPACES=argocd-e2e-external \ + ARGOCD_APPLICATION_NAMESPACES=argocd-e2e-external,argocd-e2e-external-2 \ + ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES=argocd-e2e-external,argocd-e2e-external-2 \ + ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS=http://127.0.0.1:8341,http://127.0.0.1:8342,http://127.0.0.1:8343,http://127.0.0.1:8344 \ ARGOCD_E2E_TEST=true \ goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START} @@ -470,7 +481,7 @@ clean: clean-debug .PHONY: start start: test-tools-image - docker version + $(DOCKER) version $(call run-in-test-server,make ARGOCD_PROCFILE=test/container/Procfile start-local ARGOCD_START=${ARGOCD_START}) # Starts a local instance of ArgoCD @@ -486,6 +497,7 @@ start-local: mod-vendor-local dep-ui-local cli-local ARGOCD_ZJWT_FEATURE_FLAG=always \ ARGOCD_IN_CI=false \ ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \ + BIN_MODE=$(ARGOCD_BIN_MODE) \ ARGOCD_E2E_TEST=false \ ARGOCD_APPLICATION_NAMESPACES=$(ARGOCD_APPLICATION_NAMESPACES) \ goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START} @@ -519,7 +531,7 @@ build-docs-local: .PHONY: build-docs build-docs: - docker run ${MKDOCS_RUN_ARGS} --rm -it -v ${CURRENT_DIR}:/docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install -r docs/requirements.txt; mkdocs build' + $(DOCKER) run ${MKDOCS_RUN_ARGS} --rm -it -v ${CURRENT_DIR}:/docs -w /docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install -r docs/requirements.txt; mkdocs build' .PHONY: serve-docs-local serve-docs-local: @@ -527,8 +539,7 @@ serve-docs-local: .PHONY: serve-docs serve-docs: - docker run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}/site:/site -w /site --entrypoint "" ${MKDOCS_DOCKER_IMAGE} python3 -m http.server --bind 0.0.0.0 8000 - + $(DOCKER) run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}:/docs -w /docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install -r docs/requirements.txt; mkdocs serve -a $$(ip route get 1 | awk '\''{print $$7}'\''):8000' # Verify that kubectl can connect to your K8s cluster from Docker .PHONY: verify-kube-connect @@ -551,7 +562,8 @@ install-tools-local: install-test-tools-local install-codegen-tools-local instal .PHONY: install-test-tools-local install-test-tools-local: ./hack/install.sh kustomize - ./hack/install.sh helm-linux + ./hack/install.sh helm + ./hack/install.sh gotestsum # Installs all tools required for running codegen (Linux packages) .PHONY: install-codegen-tools-local @@ -579,7 +591,7 @@ list: .PHONY: applicationset-controller applicationset-controller: - CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-applicationset-controller ./cmd + GODEBUG="tarinsecurepath=0,zipinsecurepath=0" CGO_ENABLED=${CGO_FLAG} go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-applicationset-controller ./cmd .PHONY: checksums checksums: @@ -596,3 +608,55 @@ snyk-non-container-tests: .PHONY: snyk-report snyk-report: ./hack/snyk-report.sh $(target_branch) + +.PHONY: help +help: + @echo 'Note: Generally an item w/ (-local) will run inside docker unless you use the -local variant' + @echo + @echo 'Common targets' + @echo + @echo 'all -- make cli and image' + @echo + @echo 'components:' + @echo ' applicationset-controller -- applicationset controller' + @echo ' cli(-local) -- argocd cli program' + @echo ' controller -- controller (orchestrator)' + @echo ' repo-server -- repo server (manage repository instances)' + @echo ' server -- argocd web application' + @echo + @echo 'build:' + @echo ' image -- make image of the following items' + @echo ' build(-local) -- compile go' + @echo ' build-docs(-local) -- build docs' + @echo ' build-ui -- compile typescript' + @echo + @echo 'run:' + @echo ' run -- run the components locally' + @echo ' serve-docs(-local) -- expose the documents for viewing in a browser' + @echo + @echo 'release:' + @echo ' release-cli' + @echo ' release-precheck' + @echo ' checksums' + @echo + @echo 'docs:' + @echo ' build-docs(-local)' + @echo ' serve-docs(-local)' + @echo ' notification-docs' + @echo ' clidocsgen' + @echo + @echo 'testing:' + @echo ' test(-local)' + @echo ' start-e2e(-local)' + @echo ' test-e2e(-local)' + @echo ' test-race(-local)' + @echo + @echo 'debug:' + @echo ' list -- list all make targets' + @echo ' install-tools-local -- install all the tools below' + @echo ' install-lint-tools(-local)' + @echo + @echo 'codegen:' + @echo ' codegen(-local) -- if using -local, run the following targets first' + @echo ' install-codegen-tools-local -- run this to install the codegen tools' + @echo ' install-go-tools-local -- run this to install go libraries for codegen' diff --git a/OWNERS b/OWNERS index 2dc34bf6fc359..56e037e282a0a 100644 --- a/OWNERS +++ b/OWNERS @@ -5,6 +5,7 @@ owners: approvers: - alexec - alexmt +- gdsoumya - jannfis - jessesuen - jgwest @@ -29,3 +30,4 @@ reviewers: - saumeya - zachaller - 34fathombelow +- alexef diff --git a/Procfile b/Procfile index 56c9440177bfc..4862b0230062f 100644 --- a/Procfile +++ b/Procfile @@ -1,12 +1,13 @@ -controller: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}" -api-server: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}" -dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml" -redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" == 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:$(grep "image: redis" manifests/base/redis/argocd-redis-deployment.yaml | cut -d':' -f3) --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi" -repo-server: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --otlp-address=${ARGOCD_OTLP_ADDRESS}" -cmp-server: [ "$ARGOCD_E2E_TEST" == 'true' ] && exit 0 || [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-cmp-server ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} $COMMAND --config-dir-path ./test/cmp --loglevel debug --otlp-address=${ARGOCD_OTLP_ADDRESS}" +controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "HOSTNAME=testappcontroller-1 FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --server-side-diff-enabled=${ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF:-'false'}" +api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}" +dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && (test -f dist/dex.yaml || { echo 'Failed to generate dex configuration'; exit 1; }) && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml" +redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" = 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} docker.io/library/redis:$(grep "image: redis" manifests/base/redis/argocd-redis-deployment.yaml | cut -d':' -f3) --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi" +repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --otlp-address=${ARGOCD_OTLP_ADDRESS}" +cmp-server: [ "$ARGOCD_E2E_TEST" = 'true' ] && exit 0 || [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-cmp-server ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} $COMMAND --config-dir-path ./test/cmp --loglevel debug --otlp-address=${ARGOCD_OTLP_ADDRESS}" ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start' git-server: test/fixture/testrepos/start-git.sh helm-registry: test/fixture/testrepos/start-helm-registry.sh dev-mounter: [[ "$ARGOCD_E2E_TEST" != "true" ]] && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} -applicationset-controller: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_ASK_PASS_SOCK=/tmp/applicationset-ask-pass.sock ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}" -notification: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug" +applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}" +notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --self-service-notification-enabled=${ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED:-'false'}" + diff --git a/README.md b/README.md index 1ed4164fd60ff..707848191c830 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,19 @@ **Releases:** [![Release Version](https://img.shields.io/github/v/release/argoproj/argo-cd?label=argo-cd)](https://github.com/argoproj/argo-cd/releases/latest) [![Artifact HUB](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/argo-cd)](https://artifacthub.io/packages/helm/argo/argo-cd) +[![SLSA 3](https://slsa.dev/images/gh-badge-level3.svg)](https://slsa.dev) **Code:** [![Integration tests](https://github.com/argoproj/argo-cd/workflows/Integration%20tests/badge.svg?branch=master)](https://github.com/argoproj/argo-cd/actions?query=workflow%3A%22Integration+tests%22) [![codecov](https://codecov.io/gh/argoproj/argo-cd/branch/master/graph/badge.svg)](https://codecov.io/gh/argoproj/argo-cd) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4486/badge)](https://bestpractices.coreinfrastructure.org/projects/4486) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/argoproj/argo-cd/badge)](https://api.securityscorecards.dev/projects/github.com/argoproj/argo-cd) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fargoproj%2Fargo-cd.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fargoproj%2Fargo-cd?ref=badge_shield) **Social:** [![Twitter Follow](https://img.shields.io/twitter/follow/argoproj?style=social)](https://twitter.com/argoproj) [![Slack](https://img.shields.io/badge/slack-argoproj-brightgreen.svg?logo=slack)](https://argoproj.github.io/community/join-slack) +[![LinkedIn](https://img.shields.io/badge/LinkedIn-argoproj-blue.svg?logo=linkedin)](https://www.linkedin.com/company/argoproj/) # Argo CD - Declarative Continuous Delivery for Kubernetes @@ -54,7 +57,7 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h ### Blogs and Presentations 1. [Awesome-Argo: A Curated List of Awesome Projects and Resources Related to Argo](https://github.com/terrytangyuan/awesome-argo) -1. [Unveil the Secret Ingredients of Continuous Delivery at Enterprise Scale with Argo CD](https://blog.akuity.io/unveil-the-secret-ingredients-of-continuous-delivery-at-enterprise-scale-with-argo-cd-7c5b4057ee49) +1. [Unveil the Secret Ingredients of Continuous Delivery at Enterprise Scale with Argo CD](https://akuity.io/blog/unveil-the-secret-ingredients-of-continuous-delivery-at-enterprise-scale-with-argocd-kubecon-china-2021/) 1. [GitOps Without Pipelines With ArgoCD Image Updater](https://youtu.be/avPUQin9kzU) 1. [Combining Argo CD (GitOps), Crossplane (Control Plane), And KubeVela (OAM)](https://youtu.be/eEcgn_gU3SM) 1. [How to Apply GitOps to Everything - Combining Argo CD and Crossplane](https://youtu.be/yrj4lmScKHQ) @@ -80,7 +83,8 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h 1. [Applied GitOps with Argo CD](https://thenewstack.io/applied-gitops-with-argocd/) 1. [Solving configuration drift using GitOps with Argo CD](https://www.cncf.io/blog/2020/12/17/solving-configuration-drift-using-gitops-with-argo-cd/) 1. [Decentralized GitOps over environments](https://blogs.sap.com/2021/05/06/decentralized-gitops-over-environments/) -1. [How GitOps and Operators mark the rise of Infrastructure-As-Software](https://paytmlabs.com/blog/2021/10/how-to-improve-operational-work-with-operators-and-gitops/) 1. [Getting Started with ArgoCD for GitOps Deployments](https://youtu.be/AvLuplh1skA) 1. [Using Argo CD & Datree for Stable Kubernetes CI/CD Deployments](https://youtu.be/17894DTru2Y) +1. [How to create Argo CD Applications Automatically using ApplicationSet? "Automation of GitOps"](https://amralaayassen.medium.com/how-to-create-argocd-applications-automatically-using-applicationset-automation-of-the-gitops-59455eaf4f72) +1. [Progressive Delivery with Service Mesh – Argo Rollouts with Istio](https://www.cncf.io/blog/2022/12/16/progressive-delivery-with-service-mesh-argo-rollouts-with-istio/) diff --git a/SECURITY-INSIGHTS.yml b/SECURITY-INSIGHTS.yml new file mode 100644 index 0000000000000..8ac4bc36b04ae --- /dev/null +++ b/SECURITY-INSIGHTS.yml @@ -0,0 +1,128 @@ +header: + schema-version: 1.0.0 + expiration-date: '2024-10-31T00:00:00.000Z' # One year from initial release. + last-updated: '2023-10-27' + last-reviewed: '2023-10-27' + commit-hash: b71277c6beb949d0199d647a582bc25822b88838 + project-url: https://github.com/argoproj/argo-cd + project-release: v2.9.0-rc3 + changelog: https://github.com/argoproj/argo-cd/releases + license: https://github.com/argoproj/argo-cd/blob/master/LICENSE +project-lifecycle: + status: active + roadmap: https://github.com/orgs/argoproj/projects/25 + bug-fixes-only: false + core-maintainers: + - https://github.com/argoproj/argoproj/blob/master/MAINTAINERS.md + release-cycle: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/ + release-process: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/#release-process +contribution-policy: + accepts-pull-requests: true + accepts-automated-pull-requests: true + automated-tools-list: + - automated-tool: dependabot + action: allowed + path: + - / + - automated-tool: snyk-report + action: allowed + path: + - docs/snyk + comment: | + This tool runs Snyk and generates a report of vulnerabilities in the project's dependencies. The report is + placed in the project's documentation. The workflow is defined here: + https://github.com/argoproj/argo-cd/blob/master/.github/workflows/update-snyk.yaml + contributing-policy: https://argo-cd.readthedocs.io/en/stable/developer-guide/code-contributions/ + code-of-conduct: https://github.com/cncf/foundation/blob/master/code-of-conduct.md +documentation: + - https://argo-cd.readthedocs.io/ +distribution-points: + - https://github.com/argoproj/argo-cd/releases + - https://quay.io/repository/argoproj/argocd +security-artifacts: + threat-model: + threat-model-created: true + evidence-url: + - https://github.com/argoproj/argoproj/blob/master/docs/argo_threat_model.pdf + - https://github.com/argoproj/argoproj/blob/master/docs/end_user_threat_model.pdf + self-assessment: + self-assessment-created: false + comment: | + An extensive self-assessment was performed for CNCF graduation. Because the self-assessment process was evolving + at the time, no standardized document has been published. +security-testing: + - tool-type: sca + tool-name: Dependabot + tool-version: "2" + tool-url: https://github.com/dependabot + integration: + ad-hoc: false + ci: false + before-release: false + tool-rulesets: + - https://github.com/argoproj/argo-cd/blob/master/.github/dependabot.yml + - tool-type: sca + tool-name: Snyk + tool-version: latest + tool-url: https://snyk.io/ + integration: + ad-hoc: true + ci: true + before-release: false + - tool-type: sast + tool-name: CodeQL + tool-version: latest + tool-url: https://codeql.github.com/ + integration: + ad-hoc: false + ci: true + before-release: false + comment: | + We use the default configuration with the latest version. +security-assessments: + - auditor-name: Trail of Bits + auditor-url: https://trailofbits.com + auditor-report: https://github.com/argoproj/argoproj/blob/master/docs/argo_security_final_report.pdf + report-year: 2021 + - auditor-name: Ada Logics + auditor-url: https://adalogics.com + auditor-report: https://github.com/argoproj/argoproj/blob/master/docs/argo_security_audit_2022.pdf + report-year: 2022 + - auditor-name: Ada Logics + auditor-url: https://adalogics.com + auditor-report: https://github.com/argoproj/argoproj/blob/master/docs/audit_fuzzer_adalogics_2022.pdf + report-year: 2022 + comment: | + Part of the audit was performed by Ada Logics, focussed on fuzzing. + - auditor-name: Chainguard + auditor-url: https://chainguard.dev + auditor-report: https://github.com/argoproj/argoproj/blob/master/docs/software_supply_chain_slsa_assessment_chainguard_2023.pdf + report-year: 2023 + comment: | + Confirmed the project's release process as achieving SLSA (v0.1) level 3. +security-contacts: + - type: email + value: cncf-argo-security@lists.cncf.io + primary: true +vulnerability-reporting: + accepts-vulnerability-reports: true + email-contact: cncf-argo-security@lists.cncf.io + security-policy: https://github.com/argoproj/argo-cd/security/policy + bug-bounty-available: true + bug-bounty-url: https://hackerone.com/ibb/policy_scopes + out-scope: + - vulnerable and outdated components # See https://github.com/argoproj/argo-cd/blob/master/SECURITY.md#a-word-about-security-scanners + - security logging and monitoring failures +dependencies: + third-party-packages: true + dependencies-lists: + - https://github.com/argoproj/argo-cd/blob/master/go.mod + - https://github.com/argoproj/argo-cd/blob/master/Dockerfile + - https://github.com/argoproj/argo-cd/blob/master/ui/package.json + sbom: + - sbom-file: https://github.com/argoproj/argo-cd/releases # Every release's assets include SBOMs. + sbom-format: SPDX + dependencies-lifecycle: + policy-url: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/#dependencies-lifecycle-policy + env-dependencies-policy: + policy-url: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/#dependencies-lifecycle-policy diff --git a/SECURITY.md b/SECURITY.md index c1f2e92e45c8c..479cd5ef29c97 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,6 +1,6 @@ # Security Policy for Argo CD -Version: **v1.4 (2022-01-23)** +Version: **v1.5 (2023-03-06)** ## Preface @@ -35,13 +35,11 @@ impact on Argo CD before opening an issue at least roughly. ## 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 currently support the last 3 minor versions of Argo CD with security and bug fixes. 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 +important bugs. Prior releases might receive critical security fixes on best effort basis, however, it cannot be guaranteed that security fixes get back-ported to these unsupported versions. @@ -52,7 +50,7 @@ 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 +If you find a security related bug in Argo CD, 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. @@ -61,13 +59,28 @@ and disclosure with you. Sometimes, it might take a little longer for us to react (e.g. out of office conditions), so please bear with us in these cases. We will publish security advisories using the -[Git Hub Security Advisories](https://github.com/argoproj/argo-cd/security/advisories) -feature to keep our community well informed, and will credit you for your +[GitHub Security Advisories](https://github.com/argoproj/argo-cd/security/advisories) +feature to keep our community well-informed, and will credit you for your findings (unless you prefer to stay anonymous, of course). -Please report vulnerabilities by e-mail to the following address: +There are two ways to report a vulnerability to the Argo CD team: -* cncf-argo-security@lists.cncf.io +* By opening a draft GitHub security advisory: https://github.com/argoproj/argo-cd/security/advisories/new +* By e-mail to the following address: cncf-argo-security@lists.cncf.io + +## Internet Bug Bounty collaboration + +We're happy to announce that the Argo project is collaborating with the great +folks over at +[Hacker One](https://hackerone.com/) and their +[Internet Bug Bounty program](https://hackerone.com/ibb) +to reward the awesome people who find security vulnerabilities in the four +main Argo projects (CD, Events, Rollouts and Workflows) and then work with +us to fix and disclose them in a responsible manner. + +If you report a vulnerability to us as outlined in this security policy, we +will work together with you to find out whether your finding is eligible for +claiming a bounty, and also on how to claim it. ## Securing your Argo CD Instance diff --git a/SECURITY_CONTACTS b/SECURITY_CONTACTS index 1d5575609323e..e4651b11531dc 100644 --- a/SECURITY_CONTACTS +++ b/SECURITY_CONTACTS @@ -1,7 +1,7 @@ # Defined below are the security contacts for this repo. # # DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE -# INSTRUCTIONS AT https://argo-cd.readthedocs.io/en/latest/security_considerations/#reporting-vulnerabilities +# INSTRUCTIONS AT https://github.com/argoproj/argo-cd/security/policy alexmt edlee2121 diff --git a/USERS.md b/USERS.md index 426760908b1d2..6f35c32acb661 100644 --- a/USERS.md +++ b/USERS.md @@ -7,22 +7,30 @@ Currently, the following organizations are **officially** using Argo CD: 1. [127Labs](https://127labs.com/) 1. [3Rein](https://www.3rein.com/) +1. [4data](https://4data.ch/) 1. [7shifts](https://www.7shifts.com/) 1. [Adevinta](https://www.adevinta.com/) 1. [Adfinis](https://adfinis.com) 1. [Adventure](https://jp.adventurekk.com/) +1. [Adyen](https://www.adyen.com) 1. [AirQo](https://airqo.net/) 1. [Akuity](https://akuity.io/) +1. [Albert Heijn](https://ah.nl/) 1. [Alibaba Group](https://www.alibabagroup.com/) 1. [Allianz Direct](https://www.allianzdirect.de/) 1. [Amadeus IT Group](https://amadeus.com/) 1. [Ambassador Labs](https://www.getambassador.io/) +1. [Ancestry](https://www.ancestry.com/) 1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/) 1. [Ant Group](https://www.antgroup.com/) 1. [AppDirect](https://www.appdirect.com) 1. [Arctiq Inc.](https://www.arctiq.ca) +2. [Arturia](https://www.arturia.com) 1. [ARZ Allgemeines Rechenzentrum GmbH](https://www.arz.at/) +1. [Autodesk](https://www.autodesk.com) +1. [Axians ACSP](https://www.axians.fr) 1. [Axual B.V.](https://axual.com) +1. [Back Market](https://www.backmarket.com) 1. [Baloise](https://www.baloise.com) 1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform) 1. [Beat](https://thebeat.co/en/) @@ -31,37 +39,46 @@ Currently, the following organizations are **officially** using Argo CD: 1. [BigPanda](https://bigpanda.io) 1. [BioBox Analytics](https://biobox.io) 1. [BMW Group](https://www.bmwgroup.com/) -1. [PT Boer Technology (Btech)](https://btech.id/) 1. [Boozt](https://www.booztgroup.com/) 1. [Boticario](https://www.boticario.com.br/) 1. [Bulder Bank](https://bulderbank.no) +1. [CAM](https://cam-inc.co.jp) 1. [Camptocamp](https://camptocamp.com) +1. [Candis](https://www.candis.io) 1. [Capital One](https://www.capitalone.com) -1. [CARFAX](https://www.carfax.com) 1. [CARFAX Europe](https://www.carfax.eu) +1. [CARFAX](https://www.carfax.com) +1. [Carrefour Group](https://www.carrefour.com) 1. [Casavo](https://casavo.com) 1. [Celonis](https://www.celonis.com/) 1. [CERN](https://home.cern/) +1. [Chainnodes](https://chainnodes.org) 1. [Chargetrip](https://chargetrip.com) 1. [Chime](https://www.chime.com) 1. [Cisco ET&I](https://eti.cisco.com/) +1. [Cloud Posse](https://www.cloudposse.com/) 1. [Cloud Scale](https://cloudscaleinc.com/) +1. [Cloudmate](https://cloudmt.co.kr/) +1. [Cloudogu](https://cloudogu.com/) 1. [Cobalt](https://www.cobalt.io/) 1. [Codefresh](https://www.codefresh.io/) 1. [Codility](https://www.codility.com/) 1. [Commonbond](https://commonbond.co/) 1. [Coralogix](https://coralogix.com/) -1. [CROZ d.o.o.](https://croz.net/) 1. [Crédit Agricole CIB](https://www.ca-cib.com) +1. [CROZ d.o.o.](https://croz.net/) 1. [CyberAgent](https://www.cyberagent.co.jp/en/) 1. [Cybozu](https://cybozu-global.com) 1. [D2iQ](https://www.d2iq.com) +1. [DaoCloud](https://daocloud.io/) 1. [Datarisk](https://www.datarisk.io/) 1. [Deloitte](https://www.deloitte.com/) 1. [Deutsche Telekom AG](https://telekom.com) 1. [Devopsi - Poland Software/DevOps Consulting](https://devopsi.pl/) 1. [Devtron Labs](https://github.com/devtron-labs/devtron) +1. [DigitalOcean](https://www.digitalocean.com) 1. [Divistant](https://divistant.com) +1. [Dott](https://ridedott.com) 1. [Doximity](https://www.doximity.com/) 1. [EDF Renewables](https://www.edf-re.com/) 1. [edX](https://edx.org) @@ -73,10 +90,15 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Energisme](https://energisme.com/) 1. [enigmo](https://enigmo.co.jp/) 1. [Envoy](https://envoy.com/) +1. [Factorial](https://factorialhr.com/) +1. [Farfetch](https://www.farfetch.com) 1. [Faro](https://www.faro.com/) 1. [Fave](https://myfave.com) +1. [Flexport](https://www.flexport.com/) 1. [Flip](https://flip.id) +1. [Fly Security](https://www.flysecurity.com.br/) 1. [Fonoa](https://www.fonoa.com/) +1. [Fortra](https://www.fortra.com) 1. [freee](https://corp.freee.co.jp/en/company/) 1. [Freshop, Inc](https://www.freshop.com/) 1. [Future PLC](https://www.futureplc.com/) @@ -90,10 +112,14 @@ Currently, the following organizations are **officially** using Argo CD: 1. [gloat](https://gloat.com/) 1. [GLOBIS](https://globis.com) 1. [Glovo](https://www.glovoapp.com) +1. [GlueOps](https://glueops.dev) 1. [GMETRI](https://gmetri.com/) 1. [Gojek](https://www.gojek.io/) +1. [GoTo Financial](https://gotofinancial.com/) +1. [GoTo](https://www.goto.com/) 1. [Greenpass](https://www.greenpass.com.br/) 1. [Gridfuse](https://gridfuse.com/) +1. [Groww](https://groww.in) 1. [Grupo MasMovil](https://grupomasmovil.com/en/) 1. [Handelsbanken](https://www.handelsbanken.se) 1. [Healy](https://www.healyworld.net) @@ -102,33 +128,44 @@ Currently, the following organizations are **officially** using Argo CD: 1. [hipages](https://hipages.com.au/) 1. [Hiya](https://hiya.com) 1. [Honestbank](https://honestbank.com) +1. [Hostinger](https://www.hostinger.com) +1. [IABAI](https://www.iab.ai) 1. [IBM](https://www.ibm.com/) 1. [Ibotta](https://home.ibotta.com) 1. [IITS-Consulting](https://iits-consulting.de) +1. [IllumiDesk](https://www.illumidesk.com) 1. [imaware](https://imaware.health) 1. [Indeed](https://indeed.com) 1. [Index Exchange](https://www.indexexchange.com/) +1. [Info Support](https://www.infosupport.com/) 1. [InsideBoard](https://www.insideboard.com) 1. [Intuit](https://www.intuit.com/) +1. [Jellysmack](https://www.jellysmack.com) 1. [Joblift](https://joblift.com/) 1. [JovianX](https://www.jovianx.com/) 1. [Kaltura](https://corp.kaltura.com/) 1. [Kandji](https://www.kandji.io/) -1. [KarrotPay](https://www.daangnpay.com/) 1. [Karrot](https://www.daangn.com/) +1. [KarrotPay](https://www.daangnpay.com/) 1. [Kasa](https://kasa.co.kr/) 1. [Keeeb](https://www.keeeb.com/) +1. [KelkooGroup](https://www.kelkoogroup.com) 1. [Keptn](https://keptn.sh) 1. [Kinguin](https://www.kinguin.net/) 1. [KintoHub](https://www.kintohub.com/) 1. [KompiTech GmbH](https://www.kompitech.com/) +1. [Kong Inc.](https://konghq.com/) +1. [KPMG](https://kpmg.com/uk) 1. [KubeSphere](https://github.com/kubesphere) 1. [Kurly](https://www.kurly.com/) +1. [Kvist](https://kvistsolutions.com) 1. [LexisNexis](https://www.lexisnexis.com/) 1. [Lian Chu Securities](https://lczq.com) 1. [Liatrio](https://www.liatrio.com) 1. [Lightricks](https://www.lightricks.com/) 1. [LINE](https://linecorp.com/en/) +1. [Loom](https://www.loom.com/) +1. [Lucid Motors](https://www.lucidmotors.com/) 1. [Lytt](https://www.lytt.co/) 1. [Magic Leap](https://www.magicleap.com/) 1. [Majid Al Futtaim](https://www.majidalfuttaim.com/) @@ -139,10 +176,13 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Max Kelsen](https://www.maxkelsen.com/) 1. [MeDirect](https://medirect.com.mt/) 1. [Meican](https://meican.com/) +1. [Meilleurs Agents](https://www.meilleursagents.com/) 1. [Mercedes-Benz Tech Innovation](https://www.mercedes-benz-techinnovation.com/) +1. [Mercedes-Benz.io](https://www.mercedes-benz.io/) 1. [Metanet](http://www.metanet.co.kr/en/) 1. [MindSpore](https://mindspore.cn) 1. [Mirantis](https://mirantis.com/) +1. [Mission Lane](https://missionlane.com) 1. [mixi Group](https://mixi.co.jp/) 1. [Moengage](https://www.moengage.com/) 1. [Money Forward](https://corp.moneyforward.com/en/) @@ -151,15 +191,21 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Natura &Co](https://naturaeco.com/) 1. [Nethopper](https://nethopper.io) 1. [New Relic](https://newrelic.com/) +1. [Nextbasket](https://nextbasket.com) 1. [Nextdoor](https://nextdoor.com/) 1. [Nikkei](https://www.nikkei.co.jp/nikkeiinfo/en/) 1. [Nitro](https://gonitro.com) +1. [NYCU, CS IT Center](https://it.cs.nycu.edu.tw) 1. [Objective](https://www.objective.com.br/) 1. [OCCMundial](https://occ.com.mx) 1. [Octadesk](https://octadesk.com) +1. [Olfeo](https://www.olfeo.com/) 1. [omegaUp](https://omegaUp.com) +1. [Omni](https://omni.se/) +1. [Oncourse Home Solutions](https://oncoursehome.com/) 1. [openEuler](https://openeuler.org) 1. [openGauss](https://opengauss.org/) +1. [OpenGov](https://opengov.com) 1. [openLooKeng](https://openlookeng.io) 1. [OpenSaaS Studio](https://opensaas.studio) 1. [Opensurvey](https://www.opensurvey.co.kr/) @@ -167,42 +213,64 @@ Currently, the following organizations are **officially** using Argo CD: 1. [OpsVerse](https://opsverse.io) 1. [Optoro](https://www.optoro.com/) 1. [Orbital Insight](https://orbitalinsight.com/) +1. [Oscar Health Insurance](https://hioscar.com/) 1. [p3r](https://www.p3r.one/) 1. [Packlink](https://www.packlink.com/) -1. [Pandosearch](https://www.pandosearch.com/en/home) 1. [PagerDuty](https://www.pagerduty.com/) +1. [Pandosearch](https://www.pandosearch.com/en/home) 1. [Patreon](https://www.patreon.com/) +1. [PayIt](https://payitgov.com/) 1. [PayPay](https://paypay.ne.jp/) 1. [Peloton Interactive](https://www.onepeloton.com/) +1. [Percona](https://percona.com/) +1. [PGS](https://www.pgs.com) 1. [Pigment](https://www.gopigment.com/) 1. [Pipefy](https://www.pipefy.com/) 1. [Pismo](https://pismo.io/) +1. [PITS Globale Datenrettungsdienste](https://www.pitsdatenrettung.de/) +1. [Platform9 Systems](https://platform9.com/) 1. [Polarpoint.io](https://polarpoint.io) 1. [PostFinance](https://github.com/postfinance) 1. [Preferred Networks](https://preferred.jp/en/) +1. [Previder BV](https://previder.nl) +1. [Procore](https://www.procore.com) 1. [Productboard](https://www.productboard.com/) 1. [Prudential](https://prudential.com.sg) +1. [PT Boer Technology (Btech)](https://btech.id/) 1. [PUBG](https://www.pubg.com) +1. [Puzzle ITC](https://www.puzzle.ch/) 1. [Qonto](https://qonto.com) 1. [QuintoAndar](https://quintoandar.com.br) 1. [Quipper](https://www.quipper.com/) 1. [RapidAPI](https://www.rapidapi.com/) 1. [Recreation.gov](https://www.recreation.gov/) 1. [Red Hat](https://www.redhat.com/) +1. [Redpill Linpro](https://www.redpill-linpro.com/) +1. [Reenigne Cloud](https://reenigne.ca) 1. [reev.com](https://www.reev.com/) 1. [RightRev](https://rightrev.com/) +1. [Rijkswaterstaat](https://www.rijkswaterstaat.nl/en) 1. [Rise](https://www.risecard.eu/) 1. [Riskified](https://www.riskified.com/) 1. [Robotinfra](https://www.robotinfra.com) +1. [Rocket.Chat](https://rocket.chat) 1. [Rubin Observatory](https://www.lsst.org) 1. [Saildrone](https://www.saildrone.com/) +1. [Salad Technologies](https://salad.com/) 1. [Saloodo! GmbH](https://www.saloodo.com) 1. [Sap Labs](http://sap.com) +1. [Sauce Labs](https://saucelabs.com/) 1. [Schwarz IT](https://jobs.schwarz/it-mission) +1. [SCRM Lidl International Hub](https://scrm.lidl) +1. [SEEK](https://seek.com.au) +1. [Semgrep](https://semgrep.com) +1. [Shield](https://shield.com) 1. [SI Analytics](https://si-analytics.ai) 1. [Skit](https://skit.ai/) 1. [Skyscanner](https://www.skyscanner.net/) +1. [Smart Pension](https://www.smartpension.co.uk/) 1. [Smilee.io](https://smilee.io) +1. [Smood.ch](https://www.smood.ch/) 1. [Snapp](https://snapp.ir/) 1. [Snyk](https://snyk.io/) 1. [Softway Medical](https://www.softwaymedical.fr/) @@ -211,6 +279,9 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Spendesk](https://spendesk.com/) 1. [Splunk](https://splunk.com/) 1. [Spores Labs](https://spores.app) +1. [Statsig](https://statsig.com) +1. [SternumIOT](https://sternumiot.com) +1. [StreamNative](https://streamnative.io) 1. [Stuart](https://stuart.com/) 1. [Sumo Logic](https://sumologic.com/) 1. [Sutpc](http://www.sutpc.com/) @@ -223,7 +294,9 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Tamkeen Technologies](https://tamkeentech.sa/) 1. [Techcombank](https://www.techcombank.com.vn/trang-chu) 1. [Technacy](https://www.technacy.it/) +1. [Telavita](https://www.telavita.com.br/) 1. [Tesla](https://tesla.com/) +1. [The Scale Factory](https://www.scalefactory.com/) 1. [ThousandEyes](https://www.thousandeyes.com/) 1. [Ticketmaster](https://ticketmaster.com) 1. [Tiger Analytics](https://www.tigeranalytics.com/) @@ -232,14 +305,21 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Trendyol](https://www.trendyol.com/) 1. [tru.ID](https://tru.id) 1. [Trusting Social](https://trustingsocial.com/) +1. [Twilio Segment](https://segment.com/) 1. [Twilio SendGrid](https://sendgrid.com) 1. [tZERO](https://www.tzero.com/) +1. [U.S. Veterans Affairs Department](https://www.va.gov/) 1. [UBIO](https://ub.io/) 1. [UFirstGroup](https://www.ufirstgroup.com/en/) 1. [ungleich.ch](https://ungleich.ch/) 1. [Unifonic Inc](https://www.unifonic.com/) 1. [Universidad Mesoamericana](https://www.umes.edu.gt/) +1. [Upsider Inc.](https://up-sider.com/lp/) +1. [Urbantz](https://urbantz.com/) +1. [Vectra](https://www.vectra.ai) +1. [Veepee](https://www.veepee.com) 1. [Viaduct](https://www.viaduct.ai/) +1. [VietMoney](https://vietmoney.vn/) 1. [Vinted](https://vinted.com/) 1. [Virtuo](https://www.govirtuo.com/) 1. [VISITS Technologies](https://visits.world/en) @@ -260,5 +340,6 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Yieldlab](https://www.yieldlab.de/) 1. [Youverify](https://youverify.co/) 1. [Yubo](https://www.yubo.live/) +1. [ZDF](https://www.zdf.de/) 1. [Zimpler](https://www.zimpler.com/) 1. [ZOZO](https://corp.zozo.com/) diff --git a/VERSION b/VERSION index e70b4523ae7ff..46b81d815a23b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6.0 +2.11.0 diff --git a/applicationset/controllers/applicationset_controller.go b/applicationset/controllers/applicationset_controller.go index 9afc278b35a6c..e1275e75d3ba2 100644 --- a/applicationset/controllers/applicationset_controller.go +++ b/applicationset/controllers/applicationset_controller.go @@ -17,6 +17,7 @@ package controllers import ( "context" "fmt" + "reflect" "time" log "github.com/sirupsen/logrus" @@ -27,21 +28,30 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/kubernetes" + k8scache "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/source" "github.com/argoproj/argo-cd/v2/applicationset/generators" "github.com/argoproj/argo-cd/v2/applicationset/utils" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/util/db" + "github.com/argoproj/argo-cd/v2/util/glob" argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" argoutil "github.com/argoproj/argo-cd/v2/util/argo" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) const ( @@ -53,7 +63,7 @@ const ( ) var ( - preservedAnnotations = []string{ + defaultPreservedAnnotations = []string{ NotifiedAnnotationKey, argov1alpha1.AnnotationKeyRefresh, } @@ -62,16 +72,22 @@ var ( // ApplicationSetReconciler reconciles a ApplicationSet object type ApplicationSetReconciler struct { client.Client - Scheme *runtime.Scheme - Recorder record.EventRecorder - Generators map[string]generators.Generator - ArgoDB db.ArgoDB - ArgoAppClientset appclientset.Interface - KubeClientset kubernetes.Interface - utils.Policy + Scheme *runtime.Scheme + Recorder record.EventRecorder + Generators map[string]generators.Generator + ArgoDB db.ArgoDB + ArgoAppClientset appclientset.Interface + KubeClientset kubernetes.Interface + Policy argov1alpha1.ApplicationsSyncPolicy + EnablePolicyOverride bool utils.Renderer - - EnableProgressiveRollouts bool + ArgoCDNamespace string + ApplicationSetNamespaces []string + EnableProgressiveSyncs bool + SCMRootCAPath string + GlobalPreservedAnnotations []string + GlobalPreservedLabels []string + Cache cache.Cache } // +kubebuilder:rbac:groups=argoproj.io,resources=applicationsets,verbs=get;list;watch;create;update;patch;delete @@ -92,29 +108,41 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque // Do not attempt to further reconcile the ApplicationSet if it is being deleted. if applicationSetInfo.ObjectMeta.DeletionTimestamp != nil { + deleteAllowed := utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowDelete() + if !deleteAllowed { + if err := r.removeOwnerReferencesOnDeleteAppSet(ctx, applicationSetInfo); err != nil { + return ctrl.Result{}, err + } + controllerutil.RemoveFinalizer(&applicationSetInfo, argov1alpha1.ResourcesFinalizerName) + if err := r.Update(ctx, &applicationSetInfo); err != nil { + return ctrl.Result{}, err + } + } return ctrl.Result{}, nil } // Log a warning if there are unrecognized generators _ = utils.CheckInvalidGenerators(&applicationSetInfo) // desiredApplications is the main list of all expected Applications from all generators in this appset. - desiredApplications, applicationSetReason, err := r.generateApplications(applicationSetInfo) - if err != nil { + desiredApplications, applicationSetReason, generatorsErr := r.generateApplications(logCtx, applicationSetInfo) + if generatorsErr != nil { _ = r.setApplicationSetStatusCondition(ctx, &applicationSetInfo, argov1alpha1.ApplicationSetCondition{ Type: argov1alpha1.ApplicationSetConditionErrorOccurred, - Message: err.Error(), + Message: generatorsErr.Error(), Reason: string(applicationSetReason), Status: argov1alpha1.ApplicationSetConditionStatusTrue, }, parametersGenerated, ) - return ctrl.Result{}, err + if len(desiredApplications) < 1 { + return ctrl.Result{}, generatorsErr + } } parametersGenerated = true - validateErrors, err := r.validateGeneratedApplications(ctx, desiredApplications, applicationSetInfo, req.Namespace) + validateErrors, err := r.validateGeneratedApplications(ctx, desiredApplications, applicationSetInfo) if err != nil { // While some generators may return an error that requires user intervention, // other generators reference external resources that may change to cause @@ -142,19 +170,30 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque // appSyncMap tracks which apps will be synced during this reconciliation. appSyncMap := map[string]bool{} - if r.EnableProgressiveRollouts && applicationSetInfo.Spec.Strategy != nil { - applications, err := r.getCurrentApplications(ctx, applicationSetInfo) - if err != nil { - return ctrl.Result{}, fmt.Errorf("failed to get current applications for application set: %w", err) - } + if r.EnableProgressiveSyncs { + if applicationSetInfo.Spec.Strategy == nil && len(applicationSetInfo.Status.ApplicationStatus) > 0 { + // If appset used progressive sync but stopped, clean up the progressive sync application statuses + logCtx.Infof("Removing %v unnecessary AppStatus entries from ApplicationSet %v", len(applicationSetInfo.Status.ApplicationStatus), applicationSetInfo.Name) - for _, app := range applications { - appMap[app.Name] = app - } + err := r.setAppSetApplicationStatus(ctx, logCtx, &applicationSetInfo, []argov1alpha1.ApplicationSetApplicationStatus{}) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to clear previous AppSet application statuses for %v: %w", applicationSetInfo.Name, err) + } + } else if applicationSetInfo.Spec.Strategy != nil { + // appset uses progressive sync + applications, err := r.getCurrentApplications(ctx, applicationSetInfo) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to get current applications for application set: %w", err) + } - appSyncMap, err = r.performProgressiveRollouts(ctx, applicationSetInfo, applications, desiredApplications, appMap) - if err != nil { - return ctrl.Result{}, fmt.Errorf("failed to perform progressive rollouts reconciliation for application set: %w", err) + for _, app := range applications { + appMap[app.Name] = app + } + + appSyncMap, err = r.performProgressiveSyncs(ctx, logCtx, applicationSetInfo, applications, desiredApplications, appMap) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to perform progressive sync reconciliation for application set: %w", err) + } } } @@ -186,10 +225,10 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque ) } - if r.EnableProgressiveRollouts { + if r.EnableProgressiveSyncs { // trigger appropriate application syncs if RollingSync strategy is enabled - if progressiveRolloutStrategyEnabled(&applicationSetInfo, "RollingSync") { - validApps, err = r.syncValidApplications(ctx, &applicationSetInfo, appSyncMap, appMap, validApps) + if progressiveSyncsStrategyEnabled(&applicationSetInfo, "RollingSync") { + validApps, err = r.syncValidApplications(logCtx, &applicationSetInfo, appSyncMap, appMap, validApps) if err != nil { _ = r.setApplicationSetStatusCondition(ctx, @@ -206,8 +245,8 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque } } - if r.Policy.Update() { - err = r.createOrUpdateInCluster(ctx, applicationSetInfo, validApps) + if utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowUpdate() { + err = r.createOrUpdateInCluster(ctx, logCtx, applicationSetInfo, validApps) if err != nil { _ = r.setApplicationSetStatusCondition(ctx, &applicationSetInfo, @@ -221,7 +260,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, err } } else { - err = r.createInCluster(ctx, applicationSetInfo, validApps) + err = r.createInCluster(ctx, logCtx, applicationSetInfo, validApps) if err != nil { _ = r.setApplicationSetStatusCondition(ctx, &applicationSetInfo, @@ -236,8 +275,8 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque } } - if r.Policy.Delete() { - err = r.deleteInCluster(ctx, applicationSetInfo, desiredApplications) + if utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowDelete() { + err = r.deleteInCluster(ctx, logCtx, applicationSetInfo, desiredApplications) if err != nil { _ = r.setApplicationSetStatusCondition(ctx, &applicationSetInfo, @@ -271,9 +310,8 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque } requeueAfter := r.getMinRequeueAfter(&applicationSetInfo) - logCtx.WithField("requeueAfter", requeueAfter).Info("end reconcile") - if len(validateErrors) == 0 { + if len(validateErrors) == 0 && generatorsErr == nil { if err := r.setApplicationSetStatusCondition(ctx, &applicationSetInfo, argov1alpha1.ApplicationSetCondition{ @@ -285,8 +323,13 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque ); err != nil { return ctrl.Result{}, err } + } else if requeueAfter == time.Duration(0) { + // Ensure that the request is requeued if there are validation errors. + requeueAfter = ReconcileRequeueOnValidationError } + logCtx.WithField("requeueAfter", requeueAfter).Info("end reconcile") + return ctrl.Result{ RequeueAfter: requeueAfter, }, nil @@ -396,7 +439,7 @@ func (r *ApplicationSetReconciler) setApplicationSetStatusCondition(ctx context. // validateGeneratedApplications uses the Argo CD validation functions to verify the correctness of the // generated applications. -func (r *ApplicationSetReconciler) validateGeneratedApplications(ctx context.Context, desiredApplications []argov1alpha1.Application, applicationSetInfo argov1alpha1.ApplicationSet, namespace string) (map[int]error, error) { +func (r *ApplicationSetReconciler) validateGeneratedApplications(ctx context.Context, desiredApplications []argov1alpha1.Application, applicationSetInfo argov1alpha1.ApplicationSet) (map[int]error, error) { errorsByIndex := map[int]error{} namesSet := map[string]bool{} for i, app := range desiredApplications { @@ -407,8 +450,7 @@ func (r *ApplicationSetReconciler) validateGeneratedApplications(ctx context.Con errorsByIndex[i] = fmt.Errorf("ApplicationSet %s contains applications with duplicate name: %s", applicationSetInfo.Name, app.Name) continue } - - proj, err := r.ArgoAppClientset.ArgoprojV1alpha1().AppProjects(namespace).Get(ctx, app.Spec.GetProject(), metav1.GetOptions{}) + _, err := r.ArgoAppClientset.ArgoprojV1alpha1().AppProjects(r.ArgoCDNamespace).Get(ctx, app.Spec.GetProject(), metav1.GetOptions{}) if err != nil { if apierr.IsNotFound(err) { errorsByIndex[i] = fmt.Errorf("application references project %s which does not exist", app.Spec.Project) @@ -417,20 +459,11 @@ func (r *ApplicationSetReconciler) validateGeneratedApplications(ctx context.Con return nil, err } - if err := utils.ValidateDestination(ctx, &app.Spec.Destination, r.KubeClientset, namespace); err != nil { + if err := utils.ValidateDestination(ctx, &app.Spec.Destination, r.KubeClientset, r.ArgoCDNamespace); err != nil { errorsByIndex[i] = fmt.Errorf("application destination spec is invalid: %s", err.Error()) continue } - conditions, err := argoutil.ValidatePermissions(ctx, &app.Spec, proj, r.ArgoDB) - if err != nil { - return nil, err - } - if len(conditions) > 0 { - errorsByIndex[i] = fmt.Errorf("application spec is invalid: %s", argoutil.FormatAppConditions(conditions)) - continue - } - } return errorsByIndex, nil @@ -468,7 +501,7 @@ func getTempApplication(applicationSetTemplate argov1alpha1.ApplicationSetTempla return &tmplApplication } -func (r *ApplicationSetReconciler) generateApplications(applicationSetInfo argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, argov1alpha1.ApplicationSetReasonType, error) { +func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, applicationSetInfo argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, argov1alpha1.ApplicationSetReasonType, error) { var res []argov1alpha1.Application var firstError error @@ -477,7 +510,7 @@ func (r *ApplicationSetReconciler) generateApplications(applicationSetInfo argov for _, requestedGenerator := range applicationSetInfo.Spec.Generators { t, err := generators.Transform(requestedGenerator, r.Generators, applicationSetInfo.Spec.Template, &applicationSetInfo, map[string]interface{}{}) if err != nil { - log.WithError(err).WithField("generator", requestedGenerator). + logCtx.WithError(err).WithField("generator", requestedGenerator). Error("error generating application from params") if firstError == nil { firstError = err @@ -490,9 +523,10 @@ func (r *ApplicationSetReconciler) generateApplications(applicationSetInfo argov tmplApplication := getTempApplication(a.Template) for _, p := range a.Params { - app, err := r.Renderer.RenderTemplateParams(tmplApplication, applicationSetInfo.Spec.SyncPolicy, p, applicationSetInfo.Spec.GoTemplate) + app, err := r.Renderer.RenderTemplateParams(tmplApplication, applicationSetInfo.Spec.SyncPolicy, p, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions) + if err != nil { - log.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator). + logCtx.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator). Error("error generating application from params") if firstError == nil { @@ -501,39 +535,81 @@ func (r *ApplicationSetReconciler) generateApplications(applicationSetInfo argov } continue } + + if applicationSetInfo.Spec.TemplatePatch != nil { + patchedApplication, err := r.applyTemplatePatch(app, applicationSetInfo, p) + + if err != nil { + log.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator). + Error("error generating application from params") + + if firstError == nil { + firstError = err + applicationSetReason = argov1alpha1.ApplicationSetReasonRenderTemplateParamsError + } + continue + } + + app = patchedApplication + } + res = append(res, *app) } } - log.WithField("generator", requestedGenerator).Infof("generated %d applications", len(res)) - log.WithField("generator", requestedGenerator).Debugf("apps from generator: %+v", res) + logCtx.WithField("generator", requestedGenerator).Infof("generated %d applications", len(res)) + logCtx.WithField("generator", requestedGenerator).Debugf("apps from generator: %+v", res) } return res, applicationSetReason, firstError } -func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager) error { - if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &argov1alpha1.Application{}, ".metadata.controller", func(rawObj client.Object) []string { - // grab the job object, extract the owner... - app := rawObj.(*argov1alpha1.Application) - owner := metav1.GetControllerOf(app) - if owner == nil { - return nil - } - // ...make sure it's a application set... - if owner.APIVersion != argov1alpha1.SchemeGroupVersion.String() || owner.Kind != "ApplicationSet" { - return nil - } +func (r *ApplicationSetReconciler) applyTemplatePatch(app *argov1alpha1.Application, applicationSetInfo argov1alpha1.ApplicationSet, params map[string]interface{}) (*argov1alpha1.Application, error) { + replacedTemplate, err := r.Renderer.Replace(*applicationSetInfo.Spec.TemplatePatch, params, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions) + + if err != nil { + return nil, fmt.Errorf("error replacing values in templatePatch: %w", err) + } + + return applyTemplatePatch(app, replacedTemplate) +} + +func ignoreNotAllowedNamespaces(namespaces []string) predicate.Predicate { + return predicate.Funcs{ + CreateFunc: func(e event.CreateEvent) bool { + return glob.MatchStringInList(namespaces, e.Object.GetNamespace(), false) + }, + } +} + +func appControllerIndexer(rawObj client.Object) []string { + // grab the job object, extract the owner... + app := rawObj.(*argov1alpha1.Application) + owner := metav1.GetControllerOf(app) + if owner == nil { + return nil + } + // ...make sure it's a application set... + if owner.APIVersion != argov1alpha1.SchemeGroupVersion.String() || owner.Kind != "ApplicationSet" { + return nil + } + + // ...and if so, return it + return []string{owner.Name} +} - // ...and if so, return it - return []string{owner.Name} - }); err != nil { +func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProgressiveSyncs bool, maxConcurrentReconciliations int) error { + if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &argov1alpha1.Application{}, ".metadata.controller", appControllerIndexer); err != nil { return fmt.Errorf("error setting up with manager: %w", err) } - return ctrl.NewControllerManagedBy(mgr). - For(&argov1alpha1.ApplicationSet{}). - Owns(&argov1alpha1.Application{}). + ownsHandler := getOwnsHandlerPredicates(enableProgressiveSyncs) + + return ctrl.NewControllerManagedBy(mgr).WithOptions(controller.Options{ + MaxConcurrentReconciles: maxConcurrentReconciliations, + }).For(&argov1alpha1.ApplicationSet{}). + Owns(&argov1alpha1.Application{}, builder.WithPredicates(ownsHandler)). + WithEventFilter(ignoreNotAllowedNamespaces(r.ApplicationSetNamespaces)). Watches( &source.Kind{Type: &corev1.Secret{}}, &clusterSecretEventHandler{ @@ -544,31 +620,55 @@ func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } +func (r *ApplicationSetReconciler) updateCache(ctx context.Context, obj client.Object, logger *log.Entry) { + informer, err := r.Cache.GetInformer(ctx, obj) + if err != nil { + logger.Errorf("failed to get informer: %v", err) + return + } + // The controller runtime abstract away informers creation + // so unfortunately could not find any other way to access informer store. + k8sInformer, ok := informer.(k8scache.SharedInformer) + if !ok { + logger.Error("informer is not a kubernetes informer") + return + } + if err := k8sInformer.GetStore().Update(obj); err != nil { + logger.Errorf("failed to update cache: %v", err) + return + } +} + // createOrUpdateInCluster will create / update application resources in the cluster. // - For new applications, it will call create // - For existing application, it will call update // The function also adds owner reference to all applications, and uses it to delete them. -func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error { +func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error { var firstError error // Creates or updates the application in appList for _, generatedApp := range desiredApplications { - - appLog := log.WithFields(log.Fields{"app": generatedApp.Name, "appSet": applicationSet.Name}) + // The app's namespace must be the same as the AppSet's namespace to preserve the appsets-in-any-namespace + // security boundary. generatedApp.Namespace = applicationSet.Namespace + appLog := logCtx.WithFields(log.Fields{"app": generatedApp.QualifiedName()}) + + // Normalize to avoid fighting with the application controller. + generatedApp.Spec = *argoutil.NormalizeApplicationSpec(&generatedApp.Spec) + found := &argov1alpha1.Application{ ObjectMeta: metav1.ObjectMeta{ Name: generatedApp.Name, Namespace: generatedApp.Namespace, }, TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, } - action, err := utils.CreateOrUpdate(ctx, r.Client, found, func() error { + action, err := utils.CreateOrUpdate(ctx, appLog, r.Client, applicationSet.Spec.IgnoreApplicationDifferences, found, func() error { // Copy only the Application/ObjectMeta fields that are significant, from the generatedApp found.Spec = generatedApp.Spec @@ -577,9 +677,27 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, found.Operation = generatedApp.Operation } + preservedAnnotations := make([]string, 0) + preservedLabels := make([]string, 0) + + if applicationSet.Spec.PreservedFields != nil { + preservedAnnotations = append(preservedAnnotations, applicationSet.Spec.PreservedFields.Annotations...) + preservedLabels = append(preservedLabels, applicationSet.Spec.PreservedFields.Labels...) + } + + if len(r.GlobalPreservedAnnotations) > 0 { + preservedAnnotations = append(preservedAnnotations, r.GlobalPreservedAnnotations...) + } + + if len(r.GlobalPreservedLabels) > 0 { + preservedLabels = append(preservedLabels, r.GlobalPreservedLabels...) + } + // Preserve specially treated argo cd annotations: // * https://github.com/argoproj/applicationset/issues/180 // * https://github.com/argoproj/argo-cd/issues/10500 + preservedAnnotations = append(preservedAnnotations, defaultPreservedAnnotations...) + for _, key := range preservedAnnotations { if state, exists := found.ObjectMeta.Annotations[key]; exists { if generatedApp.Annotations == nil { @@ -588,10 +706,21 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, generatedApp.Annotations[key] = state } } + + for _, key := range preservedLabels { + if state, exists := found.ObjectMeta.Labels[key]; exists { + if generatedApp.Labels == nil { + generatedApp.Labels = map[string]string{} + } + generatedApp.Labels[key] = state + } + } + found.ObjectMeta.Annotations = generatedApp.Annotations found.ObjectMeta.Finalizers = generatedApp.Finalizers found.ObjectMeta.Labels = generatedApp.Labels + return controllerutil.SetControllerReference(&applicationSet, found, r.Scheme) }) @@ -602,16 +731,24 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, } continue } + r.updateCache(ctx, found, appLog) - r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, fmt.Sprint(action), "%s Application %q", action, generatedApp.Name) - appLog.Logf(log.InfoLevel, "%s Application", action) + if action != controllerutil.OperationResultNone { + // Don't pollute etcd with "unchanged Application" events + r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, fmt.Sprint(action), "%s Application %q", action, generatedApp.Name) + appLog.Logf(log.InfoLevel, "%s Application", action) + } else { + // "unchanged Application" can be inferred by Reconcile Complete with no action being listed + // Or enable debug logging + appLog.Logf(log.DebugLevel, "%s Application", action) + } } return firstError } // createInCluster will filter from the desiredApplications only the application that needs to be created // Then it will call createOrUpdateInCluster to do the actual create -func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error { +func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error { var createApps []argov1alpha1.Application current, err := r.getCurrentApplications(ctx, applicationSet) @@ -634,16 +771,15 @@ func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, applicat } } - return r.createOrUpdateInCluster(ctx, applicationSet, createApps) + return r.createOrUpdateInCluster(ctx, logCtx, applicationSet, createApps) } -func (r *ApplicationSetReconciler) getCurrentApplications(_ context.Context, applicationSet argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error) { - // TODO: Should this use the context param? +func (r *ApplicationSetReconciler) getCurrentApplications(ctx context.Context, applicationSet argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error) { var current argov1alpha1.ApplicationList - err := r.Client.List(context.Background(), ¤t, client.MatchingFields{".metadata.controller": applicationSet.Name}) + err := r.Client.List(ctx, ¤t, client.MatchingFields{".metadata.controller": applicationSet.Name}, client.InNamespace(applicationSet.Namespace)) if err != nil { - return nil, err + return nil, fmt.Errorf("error retrieving applications: %w", err) } return current.Items, nil @@ -651,11 +787,11 @@ func (r *ApplicationSetReconciler) getCurrentApplications(_ context.Context, app // deleteInCluster will delete Applications that are currently on the cluster, but not in appList. // The function must be called after all generators had been called and generated applications -func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error { +func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error { // settingsMgr := settings.NewSettingsManager(context.TODO(), r.KubeClientset, applicationSet.Namespace) // argoDB := db.NewDB(applicationSet.Namespace, settingsMgr, r.KubeClientset) // clusterList, err := argoDB.ListClusters(ctx) - clusterList, err := utils.ListClusters(ctx, r.KubeClientset, applicationSet.Namespace) + clusterList, err := utils.ListClusters(ctx, r.KubeClientset, r.ArgoCDNamespace) if err != nil { return fmt.Errorf("error listing clusters: %w", err) } @@ -675,15 +811,15 @@ func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, applicat // Delete apps that are not in m[string]bool var firstError error for _, app := range current { - appLog := log.WithFields(log.Fields{"app": app.Name, "appSet": applicationSet.Name}) + logCtx = logCtx.WithField("app", app.QualifiedName()) _, exists := m[app.Name] if !exists { // Removes the Argo CD resources finalizer if the application contains an invalid target (eg missing cluster) - err := r.removeFinalizerOnInvalidDestination(ctx, applicationSet, &app, clusterList, appLog) + err := r.removeFinalizerOnInvalidDestination(ctx, applicationSet, &app, clusterList, logCtx) if err != nil { - appLog.WithError(err).Error("failed to update Application") + logCtx.WithError(err).Error("failed to update Application") if firstError != nil { firstError = err } @@ -692,14 +828,14 @@ func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, applicat err = r.Client.Delete(ctx, &app) if err != nil { - appLog.WithError(err).Error("failed to delete Application") + logCtx.WithError(err).Error("failed to delete Application") if firstError != nil { firstError = err } continue } r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, "Deleted", "Deleted Application %q", app.Name) - appLog.Log(log.InfoLevel, "Deleted application") + logCtx.Log(log.InfoLevel, "Deleted application") } } return firstError @@ -716,7 +852,7 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte var validDestination bool // Detect if the destination is invalid (name doesn't correspond to a matching cluster) - if err := utils.ValidateDestination(ctx, &app.Spec.Destination, r.KubeClientset, applicationSet.Namespace); err != nil { + if err := utils.ValidateDestination(ctx, &app.Spec.Destination, r.KubeClientset, r.ArgoCDNamespace); err != nil { appLog.Warnf("The destination cluster for %s couldn't be found: %v", app.Name, err) validDestination = false } else { @@ -760,31 +896,59 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte // If the finalizer length changed (due to filtering out an Argo finalizer), update the finalizer list on the app if len(newFinalizers) != len(app.Finalizers) { - app.Finalizers = newFinalizers + updated := app.DeepCopy() + updated.Finalizers = newFinalizers + patch := client.MergeFrom(app) + if log.IsLevelEnabled(log.DebugLevel) { + utils.LogPatch(appLog, patch, updated) + } + if err := r.Client.Patch(ctx, updated, patch); err != nil { + return fmt.Errorf("error updating finalizers: %w", err) + } + r.updateCache(ctx, updated, appLog) + // Application must have updated list of finalizers + updated.DeepCopyInto(app) r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, "Updated", "Updated Application %q finalizer before deletion, because application has an invalid destination", app.Name) appLog.Log(log.InfoLevel, "Updating application finalizer before deletion, because application has an invalid destination") + } + } - err := r.Client.Update(ctx, app, &client.UpdateOptions{}) - if err != nil { - return fmt.Errorf("error updating finalizers: %w", err) - } + return nil +} + +func (r *ApplicationSetReconciler) removeOwnerReferencesOnDeleteAppSet(ctx context.Context, applicationSet argov1alpha1.ApplicationSet) error { + applications, err := r.getCurrentApplications(ctx, applicationSet) + if err != nil { + return err + } + + for _, app := range applications { + app.SetOwnerReferences([]metav1.OwnerReference{}) + err := r.Client.Update(ctx, &app) + if err != nil { + return err } } return nil } -func (r *ApplicationSetReconciler) performProgressiveRollouts(ctx context.Context, appset argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, desiredApplications []argov1alpha1.Application, appMap map[string]argov1alpha1.Application) (map[string]bool, error) { +func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context, logCtx *log.Entry, appset argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, desiredApplications []argov1alpha1.Application, appMap map[string]argov1alpha1.Application) (map[string]bool, error) { - _, err := r.updateApplicationSetApplicationStatus(ctx, &appset, applications) + appDependencyList, appStepMap, err := r.buildAppDependencyList(logCtx, appset, desiredApplications) if err != nil { - return nil, fmt.Errorf("failed to update applicationset app status: %w", err) + return nil, fmt.Errorf("failed to build app dependency list: %w", err) } - appDependencyList, appStepMap, err := r.buildAppDependencyList(ctx, appset, desiredApplications) + _, err = r.updateApplicationSetApplicationStatus(ctx, logCtx, &appset, applications, appStepMap) if err != nil { - return nil, fmt.Errorf("failed to build app dependency list: %w", err) + return nil, fmt.Errorf("failed to update applicationset app status: %w", err) + } + + logCtx.Infof("ApplicationSet %v step list:", appset.Name) + for i, step := range appDependencyList { + logCtx.Infof("step %v: %+v", i+1, step) } appSyncMap, err := r.buildAppSyncMap(ctx, appset, appDependencyList, appMap) @@ -792,9 +956,9 @@ func (r *ApplicationSetReconciler) performProgressiveRollouts(ctx context.Contex return nil, fmt.Errorf("failed to build app sync map: %w", err) } - log.Infof("appSyncMap: %+v", appSyncMap) + logCtx.Infof("Application allowed to sync before maxUpdate?: %+v", appSyncMap) - _, err = r.updateApplicationSetApplicationStatusProgress(ctx, &appset, appSyncMap, appStepMap, appMap) + _, err = r.updateApplicationSetApplicationStatusProgress(ctx, logCtx, &appset, appSyncMap, appStepMap, appMap) if err != nil { return nil, fmt.Errorf("failed to update applicationset application status progress: %w", err) } @@ -808,14 +972,14 @@ func (r *ApplicationSetReconciler) performProgressiveRollouts(ctx context.Contex } // this list tracks which Applications belong to each RollingUpdate step -func (r *ApplicationSetReconciler) buildAppDependencyList(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, applications []argov1alpha1.Application) ([][]string, map[string]int, error) { +func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, applications []argov1alpha1.Application) ([][]string, map[string]int, error) { if applicationSet.Spec.Strategy == nil || applicationSet.Spec.Strategy.Type == "" || applicationSet.Spec.Strategy.Type == "AllAtOnce" { return [][]string{}, map[string]int{}, nil } steps := []argov1alpha1.ApplicationSetRolloutStep{} - if progressiveRolloutStrategyEnabled(&applicationSet, "RollingSync") { + if progressiveSyncsStrategyEnabled(&applicationSet, "RollingSync") { steps = applicationSet.Spec.Strategy.RollingSync.Steps } @@ -832,49 +996,25 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(ctx context.Context, a selected := true // default to true, assuming the current Application is a match for the given step matchExpression - allNotInMatched := true // needed to support correct AND behavior between multiple NotIn MatchExpressions - notInUsed := false // since we default to allNotInMatched == true, track whether a NotIn expression was actually used - for _, matchExpression := range step.MatchExpressions { - if matchExpression.Operator == "In" { - if val, ok := app.Labels[matchExpression.Key]; ok { - valueMatched := labelMatchedExpression(val, matchExpression) + if val, ok := app.Labels[matchExpression.Key]; ok { + valueMatched := labelMatchedExpression(logCtx, val, matchExpression) - if !valueMatched { // none of the matchExpression values was a match with the Application'ss labels - selected = false - break - } - } else { - selected = false // no matching label key with In means this Application will not be included in the current step + if !valueMatched { // none of the matchExpression values was a match with the Application's labels + selected = false break } - } else if matchExpression.Operator == "NotIn" { - notInUsed = true // a NotIn selector was used in this matchExpression - if val, ok := app.Labels[matchExpression.Key]; ok { - valueMatched := labelMatchedExpression(val, matchExpression) - - if !valueMatched { // none of the matchExpression values was a match with the Application's labels - allNotInMatched = false - } - } else { - allNotInMatched = false // no matching label key with NotIn means this Application may still be included in the current step - } - } else { // handle invalid operator selection - log.Warnf("skipping AppSet rollingUpdate step Application selection for %q, invalid matchExpression operator provided: %q ", applicationSet.Name, matchExpression.Operator) - selected = false + } else if matchExpression.Operator == "In" { + selected = false // no matching label key with "In" operator means this Application will not be included in the current step break } } - if notInUsed && allNotInMatched { // check if all NotIn Expressions matched, if so exclude this Application - selected = false - } - if selected { appDependencyList[i] = append(appDependencyList[i], app.Name) if val, ok := appStepMap[app.Name]; ok { - log.Warnf("AppSet '%v' has a invalid matchExpression that selects Application '%v' label twice, in steps %v and %v", applicationSet.Name, app.Name, val+1, i+1) + logCtx.Warnf("AppSet '%v' has a invalid matchExpression that selects Application '%v' label twice, in steps %v and %v", applicationSet.Name, app.Name, val+1, i+1) } else { appStepMap[app.Name] = i } @@ -885,12 +1025,21 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(ctx context.Context, a return appDependencyList, appStepMap, nil } -func labelMatchedExpression(val string, matchExpression argov1alpha1.ApplicationMatchExpression) bool { - valueMatched := false +func labelMatchedExpression(logCtx *log.Entry, val string, matchExpression argov1alpha1.ApplicationMatchExpression) bool { + if matchExpression.Operator != "In" && matchExpression.Operator != "NotIn" { + logCtx.Errorf("skipping AppSet rollingUpdate step Application selection, invalid matchExpression operator provided: %q ", matchExpression.Operator) + return false + } + + // if operator == In, default to false + // if operator == NotIn, default to true + valueMatched := matchExpression.Operator == "NotIn" + for _, value := range matchExpression.Values { if val == value { - valueMatched = true - break + // first "In" match returns true + // first "NotIn" match returns false + return matchExpression.Operator == "In" } } return valueMatched @@ -941,7 +1090,7 @@ func (r *ApplicationSetReconciler) buildAppSyncMap(ctx context.Context, applicat func appSyncEnabledForNextStep(appset *argov1alpha1.ApplicationSet, app argov1alpha1.Application, appStatus argov1alpha1.ApplicationSetApplicationStatus) bool { - if progressiveRolloutStrategyEnabled(appset, "RollingSync") { + if progressiveSyncsStrategyEnabled(appset, "RollingSync") { // we still need to complete the current step if the Application is not yet Healthy or there are still pending Application changes return isApplicationHealthy(app) && appStatus.Status == "Healthy" } @@ -949,7 +1098,7 @@ func appSyncEnabledForNextStep(appset *argov1alpha1.ApplicationSet, app argov1al return true } -func progressiveRolloutStrategyEnabled(appset *argov1alpha1.ApplicationSet, strategyType string) bool { +func progressiveSyncsStrategyEnabled(appset *argov1alpha1.ApplicationSet, strategyType string) bool { if appset.Spec.Strategy == nil || appset.Spec.Strategy.Type != strategyType { return false } @@ -982,7 +1131,7 @@ func statusStrings(app argov1alpha1.Application) (string, string, string) { } // check the status of each Application's status and promote Applications to the next status if needed -func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, applications []argov1alpha1.Application) ([]argov1alpha1.ApplicationSetApplicationStatus, error) { +func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, appStepMap map[string]int) ([]argov1alpha1.ApplicationSetApplicationStatus, error) { now := metav1.Now() appStatuses := make([]argov1alpha1.ApplicationSetApplicationStatus, 0, len(applications)) @@ -993,59 +1142,76 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con idx := findApplicationStatusIndex(applicationSet.Status.ApplicationStatus, app.Name) + currentAppStatus := argov1alpha1.ApplicationSetApplicationStatus{} + if idx == -1 { // AppStatus not found, set default status of "Waiting" - appStatuses = append(appStatuses, argov1alpha1.ApplicationSetApplicationStatus{ + currentAppStatus = argov1alpha1.ApplicationSetApplicationStatus{ Application: app.Name, LastTransitionTime: &now, Message: "No Application status found, defaulting status to Waiting.", Status: "Waiting", - }) - break + Step: fmt.Sprint(appStepMap[app.Name] + 1), + } + } else { + // we have an existing AppStatus + currentAppStatus = applicationSet.Status.ApplicationStatus[idx] } - // we have an existing AppStatus - currentAppStatus := applicationSet.Status.ApplicationStatus[idx] - appOutdated := false - if progressiveRolloutStrategyEnabled(applicationSet, "RollingSync") { + if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") { appOutdated = syncStatusString == "OutOfSync" } if appOutdated && currentAppStatus.Status != "Waiting" && currentAppStatus.Status != "Pending" { - log.Infof("Application %v is outdated, updating its ApplicationSet status to Waiting", app.Name) + logCtx.Infof("Application %v is outdated, updating its ApplicationSet status to Waiting", app.Name) currentAppStatus.LastTransitionTime = &now currentAppStatus.Status = "Waiting" currentAppStatus.Message = "Application has pending changes, setting status to Waiting." + currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1) } if currentAppStatus.Status == "Pending" { - if healthStatusString == "Progressing" || operationPhaseString == "Running" { - log.Infof("Application %v has entered Progressing status, updating its ApplicationSet status to Progressing", app.Name) + // check for successful syncs started less than 10s before the Application transitioned to Pending + // this covers race conditions where syncs initiated by RollingSync miraculously have a sync time before the transition to Pending state occurred (could be a few seconds) + if operationPhaseString == "Succeeded" && app.Status.OperationState.StartedAt.Add(time.Duration(10)*time.Second).After(currentAppStatus.LastTransitionTime.Time) { + if !app.Status.OperationState.StartedAt.After(currentAppStatus.LastTransitionTime.Time) { + logCtx.Warnf("Application %v was synced less than 10s prior to entering Pending status, we'll assume the AppSet controller triggered this sync and update its status to Progressing", app.Name) + } + logCtx.Infof("Application %v has completed a sync successfully, updating its ApplicationSet status to Progressing", app.Name) + currentAppStatus.LastTransitionTime = &now + currentAppStatus.Status = "Progressing" + currentAppStatus.Message = "Application resource completed a sync successfully, updating status from Pending to Progressing." + currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1) + } else if operationPhaseString == "Running" || healthStatusString == "Progressing" { + logCtx.Infof("Application %v has entered Progressing status, updating its ApplicationSet status to Progressing", app.Name) currentAppStatus.LastTransitionTime = &now currentAppStatus.Status = "Progressing" currentAppStatus.Message = "Application resource became Progressing, updating status from Pending to Progressing." + currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1) } } if currentAppStatus.Status == "Waiting" && isApplicationHealthy(app) { - log.Infof("Application %v is already synced and healthy, updating its ApplicationSet status to Healthy", app.Name) + logCtx.Infof("Application %v is already synced and healthy, updating its ApplicationSet status to Healthy", app.Name) currentAppStatus.LastTransitionTime = &now currentAppStatus.Status = healthStatusString currentAppStatus.Message = "Application resource is already Healthy, updating status from Waiting to Healthy." + currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1) } if currentAppStatus.Status == "Progressing" && isApplicationHealthy(app) { - log.Infof("Application %v has completed Progressing status, updating its ApplicationSet status to Healthy", app.Name) + logCtx.Infof("Application %v has completed Progressing status, updating its ApplicationSet status to Healthy", app.Name) currentAppStatus.LastTransitionTime = &now currentAppStatus.Status = healthStatusString currentAppStatus.Message = "Application resource became Healthy, updating status from Progressing to Healthy." + currentAppStatus.Step = fmt.Sprint(appStepMap[currentAppStatus.Application] + 1) } appStatuses = append(appStatuses, currentAppStatus) } - err := r.setAppSetApplicationStatus(ctx, applicationSet, appStatuses) + err := r.setAppSetApplicationStatus(ctx, logCtx, applicationSet, appStatuses) if err != nil { return nil, fmt.Errorf("failed to set AppSet application statuses: %w", err) } @@ -1054,7 +1220,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con } // check Applications that are in Waiting status and promote them to Pending if needed -func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appStepMap map[string]int, appMap map[string]argov1alpha1.Application) ([]argov1alpha1.ApplicationSetApplicationStatus, error) { +func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appStepMap map[string]int, appMap map[string]argov1alpha1.Application) ([]argov1alpha1.ApplicationSetApplicationStatus, error) { now := metav1.Now() appStatuses := make([]argov1alpha1.ApplicationSetApplicationStatus, 0, len(applicationSet.Status.ApplicationStatus)) @@ -1065,7 +1231,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress totalCountMap := []int{} length := 0 - if progressiveRolloutStrategyEnabled(applicationSet, "RollingSync") { + if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") { length = len(applicationSet.Spec.Strategy.RollingSync.Steps) } for s := 0; s < length; s++ { @@ -1077,7 +1243,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress for _, appStatus := range applicationSet.Status.ApplicationStatus { totalCountMap[appStepMap[appStatus.Application]] += 1 - if progressiveRolloutStrategyEnabled(applicationSet, "RollingSync") { + if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") { if appStatus.Status == "Pending" || appStatus.Status == "Progressing" { updateCountMap[appStepMap[appStatus.Application]] += 1 } @@ -1088,7 +1254,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress maxUpdateAllowed := true maxUpdate := &intstr.IntOrString{} - if progressiveRolloutStrategyEnabled(applicationSet, "RollingSync") { + if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") { maxUpdate = applicationSet.Spec.Strategy.RollingSync.Steps[appStepMap[appStatus.Application]].MaxUpdate } @@ -1096,7 +1262,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress if maxUpdate != nil { maxUpdateVal, err := intstr.GetScaledValueFromIntOrPercent(maxUpdate, totalCountMap[appStepMap[appStatus.Application]], false) if err != nil { - log.Warnf("AppSet '%v' has a invalid maxUpdate value '%+v', ignoring maxUpdate logic for this step: %v", applicationSet.Name, maxUpdate, err) + logCtx.Warnf("AppSet '%v' has a invalid maxUpdate value '%+v', ignoring maxUpdate logic for this step: %v", applicationSet.Name, maxUpdate, err) } // ensure that percentage values greater than 0% always result in at least 1 Application being selected @@ -1106,16 +1272,17 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress if updateCountMap[appStepMap[appStatus.Application]] >= maxUpdateVal { maxUpdateAllowed = false - log.Infof("Application %v is not allowed to update yet, %v/%v Applications already updating in step %v in AppSet %v", appStatus.Application, updateCountMap[appStepMap[appStatus.Application]], maxUpdateVal, appStepMap[appStatus.Application]+1, applicationSet.Name) + logCtx.Infof("Application %v is not allowed to update yet, %v/%v Applications already updating in step %v in AppSet %v", appStatus.Application, updateCountMap[appStepMap[appStatus.Application]], maxUpdateVal, appStepMap[appStatus.Application]+1, applicationSet.Name) } } if appStatus.Status == "Waiting" && appSyncMap[appStatus.Application] && maxUpdateAllowed { - log.Infof("Application %v moved to Pending status, watching for the Application to start Progressing", appStatus.Application) + logCtx.Infof("Application %v moved to Pending status, watching for the Application to start Progressing", appStatus.Application) appStatus.LastTransitionTime = &now appStatus.Status = "Pending" appStatus.Message = "Application moved to Pending status, watching for the Application resource to start Progressing." + appStatus.Step = fmt.Sprint(appStepMap[appStatus.Application] + 1) updateCountMap[appStepMap[appStatus.Application]] += 1 } @@ -1124,7 +1291,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress } } - err := r.setAppSetApplicationStatus(ctx, applicationSet, appStatuses) + err := r.setAppSetApplicationStatus(ctx, logCtx, applicationSet, appStatuses) if err != nil { return nil, fmt.Errorf("failed to set AppSet app status: %w", err) } @@ -1186,32 +1353,32 @@ func findApplicationStatusIndex(appStatuses []argov1alpha1.ApplicationSetApplica // setApplicationSetApplicationStatus updates the ApplicatonSet's status field // with any new/changed Application statuses. -func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, applicationStatuses []argov1alpha1.ApplicationSetApplicationStatus) error { +func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, applicationStatuses []argov1alpha1.ApplicationSetApplicationStatus) error { needToUpdateStatus := false - for i := range applicationStatuses { - appStatus := applicationStatuses[i] - idx := findApplicationStatusIndex(applicationSet.Status.ApplicationStatus, appStatus.Application) - if idx == -1 { - needToUpdateStatus = true - break - } - currentStatus := applicationSet.Status.ApplicationStatus[idx] - if currentStatus.Message != appStatus.Message || currentStatus.Status != appStatus.Status { - needToUpdateStatus = true - break + + if len(applicationStatuses) != len(applicationSet.Status.ApplicationStatus) { + needToUpdateStatus = true + } else { + for i := range applicationStatuses { + appStatus := applicationStatuses[i] + idx := findApplicationStatusIndex(applicationSet.Status.ApplicationStatus, appStatus.Application) + if idx == -1 { + needToUpdateStatus = true + break + } + currentStatus := applicationSet.Status.ApplicationStatus[idx] + if currentStatus.Message != appStatus.Message || currentStatus.Status != appStatus.Status || currentStatus.Step != appStatus.Step { + needToUpdateStatus = true + break + } } } if needToUpdateStatus { - // fetch updated Application Set object before updating it namespacedName := types.NamespacedName{Namespace: applicationSet.Namespace, Name: applicationSet.Name} - if err := r.Get(ctx, namespacedName, applicationSet); err != nil { - if client.IgnoreNotFound(err) != nil { - return nil - } - return fmt.Errorf("error fetching updated application set: %v", err) - } + // rebuild ApplicationStatus from scratch, we don't need any previous status history + applicationSet.Status.ApplicationStatus = []argov1alpha1.ApplicationSetApplicationStatus{} for i := range applicationStatuses { applicationSet.Status.SetApplicationStatus(applicationStatuses[i]) } @@ -1220,7 +1387,7 @@ func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Contex err := r.Client.Status().Update(ctx, applicationSet) if err != nil { - log.Errorf("unable to set application set status: %v", err) + logCtx.Errorf("unable to set application set status: %v", err) return fmt.Errorf("unable to set application set status: %v", err) } @@ -1235,7 +1402,7 @@ func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Contex return nil } -func (r *ApplicationSetReconciler) syncValidApplications(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appMap map[string]argov1alpha1.Application, validApps []argov1alpha1.Application) ([]argov1alpha1.Application, error) { +func (r *ApplicationSetReconciler) syncValidApplications(logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appMap map[string]argov1alpha1.Application, validApps []argov1alpha1.Application) ([]argov1alpha1.Application, error) { rolloutApps := []argov1alpha1.Application{} for i := range validApps { pruneEnabled := false @@ -1255,7 +1422,7 @@ func (r *ApplicationSetReconciler) syncValidApplications(ctx context.Context, ap // check appSyncMap to determine which Applications are ready to be updated and which should be skipped if appSyncMap[validApps[i].Name] && appMap[validApps[i].Name].Status.Sync.Status == "OutOfSync" && appSetStatusPending { - log.Infof("triggering sync for application: %v, prune enabled: %v", validApps[i].Name, pruneEnabled) + logCtx.Infof("triggering sync for application: %v, prune enabled: %v", validApps[i].Name, pruneEnabled) validApps[i], _ = syncApplication(validApps[i], pruneEnabled) } rolloutApps = append(rolloutApps, validApps[i]) @@ -1263,7 +1430,7 @@ func (r *ApplicationSetReconciler) syncValidApplications(ctx context.Context, ap return rolloutApps, nil } -// used by the RollingSync Progressive Rollout strategy to trigger a sync of a particular Application resource +// used by the RollingSync Progressive Sync strategy to trigger a sync of a particular Application resource func syncApplication(application argov1alpha1.Application, prune bool) (argov1alpha1.Application, error) { operation := argov1alpha1.Operation{ @@ -1294,4 +1461,95 @@ func syncApplication(application argov1alpha1.Application, prune bool) (argov1al return application, nil } +func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs { + return predicate.Funcs{ + CreateFunc: func(e event.CreateEvent) bool { + // if we are the owner and there is a create event, we most likely created it and do not need to + // re-reconcile + if log.IsLevelEnabled(log.DebugLevel) { + var appName string + app, isApp := e.Object.(*argov1alpha1.Application) + if isApp { + appName = app.QualifiedName() + } + log.WithField("app", appName).Debugln("received create event from owning an application") + } + return false + }, + DeleteFunc: func(e event.DeleteEvent) bool { + if log.IsLevelEnabled(log.DebugLevel) { + var appName string + app, isApp := e.Object.(*argov1alpha1.Application) + if isApp { + appName = app.QualifiedName() + } + log.WithField("app", appName).Debugln("received delete event from owning an application") + } + return true + }, + UpdateFunc: func(e event.UpdateEvent) bool { + appOld, isApp := e.ObjectOld.(*argov1alpha1.Application) + if !isApp { + return false + } + logCtx := log.WithField("app", appOld.QualifiedName()) + logCtx.Debugln("received update event from owning an application") + appNew, isApp := e.ObjectNew.(*argov1alpha1.Application) + if !isApp { + return false + } + requeue := shouldRequeueApplicationSet(appOld, appNew, enableProgressiveSyncs) + logCtx.WithField("requeue", requeue).Debugf("requeue: %t caused by application %s\n", requeue, appNew.Name) + return requeue + }, + GenericFunc: func(e event.GenericEvent) bool { + if log.IsLevelEnabled(log.DebugLevel) { + var appName string + app, isApp := e.Object.(*argov1alpha1.Application) + if isApp { + appName = app.QualifiedName() + } + log.WithField("app", appName).Debugln("received generic event from owning an application") + } + return true + }, + } +} + +// shouldRequeueApplicationSet determines when we want to requeue an ApplicationSet for reconciling based on an owned +// application change +// The applicationset controller owns a subset of the Application CR. +// We do not need to re-reconcile if parts of the application change outside the applicationset's control. +// An example being, Application.ApplicationStatus.ReconciledAt which gets updated by the application controller. +// Additionally, Application.ObjectMeta.ResourceVersion and Application.ObjectMeta.Generation which are set by K8s. +func shouldRequeueApplicationSet(appOld *argov1alpha1.Application, appNew *argov1alpha1.Application, enableProgressiveSyncs bool) bool { + if appOld == nil || appNew == nil { + return false + } + + // the applicationset controller owns the application spec, labels, annotations, and finalizers on the applications + if !reflect.DeepEqual(appOld.Spec, appNew.Spec) || + !reflect.DeepEqual(appOld.ObjectMeta.GetAnnotations(), appNew.ObjectMeta.GetAnnotations()) || + !reflect.DeepEqual(appOld.ObjectMeta.GetLabels(), appNew.ObjectMeta.GetLabels()) || + !reflect.DeepEqual(appOld.ObjectMeta.GetFinalizers(), appNew.ObjectMeta.GetFinalizers()) { + return true + } + + // progressive syncs use the application status for updates. if they differ, requeue to trigger the next progression + if enableProgressiveSyncs { + if appOld.Status.Health.Status != appNew.Status.Health.Status || appOld.Status.Sync.Status != appNew.Status.Sync.Status { + return true + } + + if appOld.Status.OperationState != nil && appNew.Status.OperationState != nil { + if appOld.Status.OperationState.Phase != appNew.Status.OperationState.Phase || + appOld.Status.OperationState.StartedAt != appNew.Status.OperationState.StartedAt { + return true + } + } + } + + return false +} + var _ handler.EventHandler = &clusterSecretEventHandler{} diff --git a/applicationset/controllers/applicationset_controller_test.go b/applicationset/controllers/applicationset_controller_test.go index 38b25e8eac4e6..c3c5f3845bea5 100644 --- a/applicationset/controllers/applicationset_controller_test.go +++ b/applicationset/controllers/applicationset_controller_test.go @@ -19,73 +19,118 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" kubefake "k8s.io/client-go/kubernetes/fake" + k8scache "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" crtclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/event" - "github.com/argoproj/argo-cd/v2/applicationset/generators" - "github.com/argoproj/argo-cd/v2/applicationset/utils" "github.com/argoproj/gitops-engine/pkg/health" "github.com/argoproj/gitops-engine/pkg/sync/common" + "github.com/argoproj/argo-cd/v2/applicationset/generators" + "github.com/argoproj/argo-cd/v2/applicationset/utils" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake" "github.com/argoproj/argo-cd/v2/util/collections" dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) +type fakeStore struct { + k8scache.Store +} + +func (f *fakeStore) Update(obj interface{}) error { + return nil +} + +type fakeInformer struct { + k8scache.SharedInformer +} + +func (f *fakeInformer) AddIndexers(indexers k8scache.Indexers) error { + return nil +} + +func (f *fakeInformer) GetStore() k8scache.Store { + return &fakeStore{} +} + +type fakeCache struct { + cache.Cache +} + +func (f *fakeCache) GetInformer(ctx context.Context, obj crtclient.Object) (cache.Informer, error) { + return &fakeInformer{}, nil +} + type generatorMock struct { mock.Mock } -func (g *generatorMock) GetTemplate(appSetGenerator *argov1alpha1.ApplicationSetGenerator) *argov1alpha1.ApplicationSetTemplate { +func (g *generatorMock) GetTemplate(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate { args := g.Called(appSetGenerator) - return args.Get(0).(*argov1alpha1.ApplicationSetTemplate) + return args.Get(0).(*v1alpha1.ApplicationSetTemplate) } -func (g *generatorMock) GenerateParams(appSetGenerator *argov1alpha1.ApplicationSetGenerator, _ *argov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { +func (g *generatorMock) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, _ *v1alpha1.ApplicationSet) ([]map[string]interface{}, error) { args := g.Called(appSetGenerator) return args.Get(0).([]map[string]interface{}), args.Error(1) } +func (g *generatorMock) Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) { + args := g.Called(tmpl, replaceMap, useGoTemplate, goTemplateOptions) + + return args.Get(0).(string), args.Error(1) +} + type rendererMock struct { mock.Mock } -func (g *generatorMock) GetRequeueAfter(appSetGenerator *argov1alpha1.ApplicationSetGenerator) time.Duration { +func (g *generatorMock) GetRequeueAfter(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration { args := g.Called(appSetGenerator) return args.Get(0).(time.Duration) } -func (r *rendererMock) RenderTemplateParams(tmpl *argov1alpha1.Application, syncPolicy *argov1alpha1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool) (*argov1alpha1.Application, error) { - args := r.Called(tmpl, params, useGoTemplate) +func (r *rendererMock) RenderTemplateParams(tmpl *v1alpha1.Application, syncPolicy *v1alpha1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (*v1alpha1.Application, error) { + args := r.Called(tmpl, params, useGoTemplate, goTemplateOptions) if args.Error(1) != nil { return nil, args.Error(1) } - return args.Get(0).(*argov1alpha1.Application), args.Error(1) + return args.Get(0).(*v1alpha1.Application), args.Error(1) + +} + +func (r *rendererMock) Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) { + args := r.Called(tmpl, replaceMap, useGoTemplate, goTemplateOptions) + return args.Get(0).(string), args.Error(1) } func TestExtractApplications(t *testing.T) { scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) + err := v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - err = argov1alpha1.AddToScheme(scheme) + err = v1alpha1.AddToScheme(scheme) assert.Nil(t, err) for _, c := range []struct { name string params []map[string]interface{} - template argov1alpha1.ApplicationSetTemplate + template v1alpha1.ApplicationSetTemplate generateParamsError error rendererError error expectErr bool @@ -94,13 +139,13 @@ func TestExtractApplications(t *testing.T) { { name: "Generate two applications", params: []map[string]interface{}{{"name": "app1"}, {"name": "app2"}}, - template: argov1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: argov1alpha1.ApplicationSetTemplateMeta{ + template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ Name: "name", Namespace: "namespace", Labels: map[string]string{"label_name": "label_value"}, }, - Spec: argov1alpha1.ApplicationSpec{}, + Spec: v1alpha1.ApplicationSpec{}, }, expectedReason: "", }, @@ -113,13 +158,13 @@ func TestExtractApplications(t *testing.T) { { name: "Handles error from the render", params: []map[string]interface{}{{"name": "app1"}, {"name": "app2"}}, - template: argov1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: argov1alpha1.ApplicationSetTemplateMeta{ + template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ Name: "name", Namespace: "namespace", Labels: map[string]string{"label_name": "label_value"}, }, - Spec: argov1alpha1.ApplicationSpec{}, + Spec: v1alpha1.ApplicationSpec{}, }, rendererError: fmt.Errorf("error"), expectErr: true, @@ -127,7 +172,7 @@ func TestExtractApplications(t *testing.T) { }, } { cc := c - app := argov1alpha1.Application{ + app := v1alpha1.Application{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, @@ -135,7 +180,7 @@ func TestExtractApplications(t *testing.T) { t.Run(cc.name, func(t *testing.T) { - appSet := &argov1alpha1.ApplicationSet{ + appSet := &v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", @@ -145,28 +190,28 @@ func TestExtractApplications(t *testing.T) { client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(appSet).Build() generatorMock := generatorMock{} - generator := argov1alpha1.ApplicationSetGenerator{ - List: &argov1alpha1.ListGenerator{}, + generator := v1alpha1.ApplicationSetGenerator{ + List: &v1alpha1.ListGenerator{}, } generatorMock.On("GenerateParams", &generator). Return(cc.params, cc.generateParamsError) generatorMock.On("GetTemplate", &generator). - Return(&argov1alpha1.ApplicationSetTemplate{}) + Return(&v1alpha1.ApplicationSetTemplate{}) rendererMock := rendererMock{} - var expectedApps []argov1alpha1.Application + var expectedApps []v1alpha1.Application if cc.generateParamsError == nil { for _, p := range cc.params { if cc.rendererError != nil { - rendererMock.On("RenderTemplateParams", getTempApplication(cc.template), p, false). + rendererMock.On("RenderTemplateParams", getTempApplication(cc.template), p, false, []string(nil)). Return(nil, cc.rendererError) } else { - rendererMock.On("RenderTemplateParams", getTempApplication(cc.template), p, false). + rendererMock.On("RenderTemplateParams", getTempApplication(cc.template), p, false, []string(nil)). Return(&app, nil) expectedApps = append(expectedApps, app) } @@ -182,15 +227,16 @@ func TestExtractApplications(t *testing.T) { }, Renderer: &rendererMock, KubeClientset: kubefake.NewSimpleClientset(), + Cache: &fakeCache{}, } - got, reason, err := r.generateApplications(argov1alpha1.ApplicationSet{ + got, reason, err := r.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Generators: []argov1alpha1.ApplicationSetGenerator{generator}, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{generator}, Template: cc.template, }, }) @@ -215,53 +261,53 @@ func TestExtractApplications(t *testing.T) { func TestMergeTemplateApplications(t *testing.T) { scheme := runtime.NewScheme() - _ = argov1alpha1.AddToScheme(scheme) - _ = argov1alpha1.AddToScheme(scheme) + _ = v1alpha1.AddToScheme(scheme) + _ = v1alpha1.AddToScheme(scheme) client := fake.NewClientBuilder().WithScheme(scheme).Build() for _, c := range []struct { name string params []map[string]interface{} - template argov1alpha1.ApplicationSetTemplate - overrideTemplate argov1alpha1.ApplicationSetTemplate - expectedMerged argov1alpha1.ApplicationSetTemplate - expectedApps []argov1alpha1.Application + template v1alpha1.ApplicationSetTemplate + overrideTemplate v1alpha1.ApplicationSetTemplate + expectedMerged v1alpha1.ApplicationSetTemplate + expectedApps []v1alpha1.Application }{ { name: "Generate app", params: []map[string]interface{}{{"name": "app1"}}, - template: argov1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: argov1alpha1.ApplicationSetTemplateMeta{ + template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ Name: "name", Namespace: "namespace", Labels: map[string]string{"label_name": "label_value"}, }, - Spec: argov1alpha1.ApplicationSpec{}, + Spec: v1alpha1.ApplicationSpec{}, }, - overrideTemplate: argov1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: argov1alpha1.ApplicationSetTemplateMeta{ + overrideTemplate: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ Name: "test", Labels: map[string]string{"foo": "bar"}, }, - Spec: argov1alpha1.ApplicationSpec{}, + Spec: v1alpha1.ApplicationSpec{}, }, - expectedMerged: argov1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: argov1alpha1.ApplicationSetTemplateMeta{ + expectedMerged: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ Name: "test", Namespace: "namespace", Labels: map[string]string{"label_name": "label_value", "foo": "bar"}, }, - Spec: argov1alpha1.ApplicationSpec{}, + Spec: v1alpha1.ApplicationSpec{}, }, - expectedApps: []argov1alpha1.Application{ + expectedApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "test", Labels: map[string]string{"foo": "bar"}, }, - Spec: argov1alpha1.ApplicationSpec{}, + Spec: v1alpha1.ApplicationSpec{}, }, }, }, @@ -271,8 +317,8 @@ func TestMergeTemplateApplications(t *testing.T) { t.Run(cc.name, func(t *testing.T) { generatorMock := generatorMock{} - generator := argov1alpha1.ApplicationSetGenerator{ - List: &argov1alpha1.ListGenerator{}, + generator := v1alpha1.ApplicationSetGenerator{ + List: &v1alpha1.ListGenerator{}, } generatorMock.On("GenerateParams", &generator). @@ -283,7 +329,7 @@ func TestMergeTemplateApplications(t *testing.T) { rendererMock := rendererMock{} - rendererMock.On("RenderTemplateParams", getTempApplication(cc.expectedMerged), cc.params[0], false). + rendererMock.On("RenderTemplateParams", getTempApplication(cc.expectedMerged), cc.params[0], false, []string(nil)). Return(&cc.expectedApps[0], nil) r := ApplicationSetReconciler{ @@ -297,13 +343,13 @@ func TestMergeTemplateApplications(t *testing.T) { KubeClientset: kubefake.NewSimpleClientset(), } - got, _, _ := r.generateApplications(argov1alpha1.ApplicationSet{ + got, _, _ := r.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Generators: []argov1alpha1.ApplicationSetGenerator{generator}, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{generator}, Template: cc.template, }, }, @@ -318,44 +364,44 @@ func TestMergeTemplateApplications(t *testing.T) { func TestCreateOrUpdateInCluster(t *testing.T) { scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) + err := v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - err = argov1alpha1.AddToScheme(scheme) + err = v1alpha1.AddToScheme(scheme) assert.Nil(t, err) for _, c := range []struct { // name is human-readable test name name string // appSet is the ApplicationSet we are generating resources for - appSet argov1alpha1.ApplicationSet + appSet v1alpha1.ApplicationSet // existingApps are the apps that already exist on the cluster - existingApps []argov1alpha1.Application + existingApps []v1alpha1.Application // desiredApps are the generated apps to create/update - desiredApps []argov1alpha1.Application + desiredApps []v1alpha1.Application // expected is what we expect the cluster Applications to look like, after createOrUpdateInCluster - expected []argov1alpha1.Application + expected []v1alpha1.Application }{ { name: "Create an app that doesn't exist", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, }, existingApps: nil, - desiredApps: []argov1alpha1.Application{ + desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, }, }, - expected: []argov1alpha1.Application{ + expected: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -363,28 +409,29 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Namespace: "namespace", ResourceVersion: "1", }, + Spec: v1alpha1.ApplicationSpec{Project: "default"}, }, }, }, { name: "Update an existing app with a different project name", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Template: argov1alpha1.ApplicationSetTemplate{ - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, }, - existingApps: []argov1alpha1.Application{ + existingApps: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -392,25 +439,25 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Namespace: "namespace", ResourceVersion: "2", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "test", }, }, }, - desiredApps: []argov1alpha1.Application{ + desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, - expected: []argov1alpha1.Application{ + expected: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -418,7 +465,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Namespace: "namespace", ResourceVersion: "3", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, @@ -426,23 +473,23 @@ func TestCreateOrUpdateInCluster(t *testing.T) { }, { name: "Create a new app and check it doesn't replace the existing app", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Template: argov1alpha1.ApplicationSetTemplate{ - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, }, - existingApps: []argov1alpha1.Application{ + existingApps: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -450,25 +497,25 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Namespace: "namespace", ResourceVersion: "2", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "test", }, }, }, - desiredApps: []argov1alpha1.Application{ + desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app2", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, - expected: []argov1alpha1.Application{ + expected: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -476,7 +523,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Namespace: "namespace", ResourceVersion: "1", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, @@ -484,23 +531,23 @@ func TestCreateOrUpdateInCluster(t *testing.T) { }, { name: "Ensure that labels and annotations are added (via update) into an exiting application", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Template: argov1alpha1.ApplicationSetTemplate{ - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, }, - existingApps: []argov1alpha1.Application{ + existingApps: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -508,27 +555,27 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Namespace: "namespace", ResourceVersion: "2", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, - desiredApps: []argov1alpha1.Application{ + desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", Labels: map[string]string{"label-key": "label-value"}, Annotations: map[string]string{"annot-key": "annot-value"}, }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, - expected: []argov1alpha1.Application{ + expected: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -538,7 +585,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Annotations: map[string]string{"annot-key": "annot-value"}, ResourceVersion: "3", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, @@ -546,23 +593,23 @@ func TestCreateOrUpdateInCluster(t *testing.T) { }, { name: "Ensure that labels and annotations are removed from an existing app", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Template: argov1alpha1.ApplicationSetTemplate{ - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, }, - existingApps: []argov1alpha1.Application{ + existingApps: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -572,25 +619,25 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Labels: map[string]string{"label-key": "label-value"}, Annotations: map[string]string{"annot-key": "annot-value"}, }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, - desiredApps: []argov1alpha1.Application{ + desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, - expected: []argov1alpha1.Application{ + expected: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -598,7 +645,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Namespace: "namespace", ResourceVersion: "3", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, @@ -606,23 +653,23 @@ func TestCreateOrUpdateInCluster(t *testing.T) { }, { name: "Ensure that status and operation fields are not overridden by an update, when removing labels/annotations", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Template: argov1alpha1.ApplicationSetTemplate{ - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, }, - existingApps: []argov1alpha1.Application{ + existingApps: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -632,31 +679,31 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Labels: map[string]string{"label-key": "label-value"}, Annotations: map[string]string{"annot-key": "annot-value"}, }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, - Status: argov1alpha1.ApplicationStatus{ - Resources: []argov1alpha1.ResourceStatus{{Name: "sample-name"}}, + Status: v1alpha1.ApplicationStatus{ + Resources: []v1alpha1.ResourceStatus{{Name: "sample-name"}}, }, - Operation: &argov1alpha1.Operation{ - Sync: &argov1alpha1.SyncOperation{Revision: "sample-revision"}, + Operation: &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{Revision: "sample-revision"}, }, }, }, - desiredApps: []argov1alpha1.Application{ + desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, - expected: []argov1alpha1.Application{ + expected: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -664,39 +711,39 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Namespace: "namespace", ResourceVersion: "3", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, - Status: argov1alpha1.ApplicationStatus{ - Resources: []argov1alpha1.ResourceStatus{{Name: "sample-name"}}, + Status: v1alpha1.ApplicationStatus{ + Resources: []v1alpha1.ResourceStatus{{Name: "sample-name"}}, }, - Operation: &argov1alpha1.Operation{ - Sync: &argov1alpha1.SyncOperation{Revision: "sample-revision"}, + Operation: &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{Revision: "sample-revision"}, }, }, }, }, { name: "Ensure that status and operation fields are not overridden by an update, when removing labels/annotations and adding other fields", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Template: argov1alpha1.ApplicationSetTemplate{ - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", - Source: &argov1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"}, - Destination: argov1alpha1.ApplicationDestination{Server: "server", Namespace: "namespace"}, + Source: &v1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"}, + Destination: v1alpha1.ApplicationDestination{Server: "server", Namespace: "namespace"}, }, }, }, }, - existingApps: []argov1alpha1.Application{ + existingApps: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -704,35 +751,35 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Namespace: "namespace", ResourceVersion: "2", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, - Status: argov1alpha1.ApplicationStatus{ - Resources: []argov1alpha1.ResourceStatus{{Name: "sample-name"}}, + Status: v1alpha1.ApplicationStatus{ + Resources: []v1alpha1.ResourceStatus{{Name: "sample-name"}}, }, - Operation: &argov1alpha1.Operation{ - Sync: &argov1alpha1.SyncOperation{Revision: "sample-revision"}, + Operation: &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{Revision: "sample-revision"}, }, }, }, - desiredApps: []argov1alpha1.Application{ + desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", Labels: map[string]string{"label-key": "label-value"}, Annotations: map[string]string{"annot-key": "annot-value"}, }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", - Source: &argov1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"}, - Destination: argov1alpha1.ApplicationDestination{Server: "server", Namespace: "namespace"}, + Source: &v1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"}, + Destination: v1alpha1.ApplicationDestination{Server: "server", Namespace: "namespace"}, }, }, }, - expected: []argov1alpha1.Application{ + expected: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -742,39 +789,39 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Annotations: map[string]string{"annot-key": "annot-value"}, ResourceVersion: "3", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", - Source: &argov1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"}, - Destination: argov1alpha1.ApplicationDestination{Server: "server", Namespace: "namespace"}, + Source: &v1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"}, + Destination: v1alpha1.ApplicationDestination{Server: "server", Namespace: "namespace"}, }, - Status: argov1alpha1.ApplicationStatus{ - Resources: []argov1alpha1.ResourceStatus{{Name: "sample-name"}}, + Status: v1alpha1.ApplicationStatus{ + Resources: []v1alpha1.ResourceStatus{{Name: "sample-name"}}, }, - Operation: &argov1alpha1.Operation{ - Sync: &argov1alpha1.SyncOperation{Revision: "sample-revision"}, + Operation: &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{Revision: "sample-revision"}, }, }, }, }, { name: "Ensure that argocd notifications state and refresh annotation is preserved from an existing app", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Template: argov1alpha1.ApplicationSetTemplate{ - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, }, - existingApps: []argov1alpha1.Application{ + existingApps: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -783,30 +830,30 @@ func TestCreateOrUpdateInCluster(t *testing.T) { ResourceVersion: "2", Labels: map[string]string{"label-key": "label-value"}, Annotations: map[string]string{ - "annot-key": "annot-value", - NotifiedAnnotationKey: `{"b620d4600c771a6f4cxxxxxxx:on-deployed:[0].y7b5sbwa2Q329JYHxxxxxx-fBs:slack:slack-test":1617144614}`, - argov1alpha1.AnnotationKeyRefresh: string(argov1alpha1.RefreshTypeNormal), + "annot-key": "annot-value", + NotifiedAnnotationKey: `{"b620d4600c771a6f4cxxxxxxx:on-deployed:[0].y7b5sbwa2Q329JYHxxxxxx-fBs:slack:slack-test":1617144614}`, + v1alpha1.AnnotationKeyRefresh: string(v1alpha1.RefreshTypeNormal), }, }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, - desiredApps: []argov1alpha1.Application{ + desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, - expected: []argov1alpha1.Application{ + expected: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -814,368 +861,201 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Namespace: "namespace", ResourceVersion: "3", Annotations: map[string]string{ - NotifiedAnnotationKey: `{"b620d4600c771a6f4cxxxxxxx:on-deployed:[0].y7b5sbwa2Q329JYHxxxxxx-fBs:slack:slack-test":1617144614}`, - argov1alpha1.AnnotationKeyRefresh: string(argov1alpha1.RefreshTypeNormal), + NotifiedAnnotationKey: `{"b620d4600c771a6f4cxxxxxxx:on-deployed:[0].y7b5sbwa2Q329JYHxxxxxx-fBs:slack:slack-test":1617144614}`, + v1alpha1.AnnotationKeyRefresh: string(v1alpha1.RefreshTypeNormal), }, }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, - }, - } { - - t.Run(c.name, func(t *testing.T) { - - initObjs := []crtclient.Object{&c.appSet} - - for _, a := range c.existingApps { - err = controllerutil.SetControllerReference(&c.appSet, &a, scheme) - assert.Nil(t, err) - initObjs = append(initObjs, &a) - } - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build() - - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)), - } - - err = r.createOrUpdateInCluster(context.TODO(), c.appSet, c.desiredApps) - assert.Nil(t, err) - - for _, obj := range c.expected { - got := &argov1alpha1.Application{} - _ = client.Get(context.Background(), crtclient.ObjectKey{ - Namespace: obj.Namespace, - Name: obj.Name, - }, got) - - err = controllerutil.SetControllerReference(&c.appSet, &obj, r.Scheme) - assert.Nil(t, err) - assert.Equal(t, obj, *got) - } - }) - } -} - -func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) { - - scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) - assert.Nil(t, err) - - err = argov1alpha1.AddToScheme(scheme) - assert.Nil(t, err) - - for _, c := range []struct { - // name is human-readable test name - name string - existingFinalizers []string - expectedFinalizers []string - }{ - { - name: "no finalizers", - existingFinalizers: []string{}, - expectedFinalizers: nil, - }, - { - name: "contains only argo finalizer", - existingFinalizers: []string{argov1alpha1.ResourcesFinalizerName}, - expectedFinalizers: nil, - }, - { - name: "contains only non-argo finalizer", - existingFinalizers: []string{"non-argo-finalizer"}, - expectedFinalizers: []string{"non-argo-finalizer"}, - }, - { - name: "contains both argo and non-argo finalizer", - existingFinalizers: []string{"non-argo-finalizer", argov1alpha1.ResourcesFinalizerName}, - expectedFinalizers: []string{"non-argo-finalizer"}, - }, - } { - t.Run(c.name, func(t *testing.T) { - - appSet := argov1alpha1.ApplicationSet{ + }, { + name: "Ensure that configured preserved annotations are preserved from an existing app", + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Template: argov1alpha1.ApplicationSetTemplate{ - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, + PreservedFields: &v1alpha1.ApplicationPreservedFields{ + Annotations: []string{"preserved-annot-key"}, + }, }, - } - - app := argov1alpha1.Application{ + }, + existingApps: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "namespace", + ResourceVersion: "2", + Annotations: map[string]string{ + "annot-key": "annot-value", + "preserved-annot-key": "preserved-annot-value", + }, + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + desiredApps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + expected: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "namespace", + ResourceVersion: "3", + Annotations: map[string]string{ + "preserved-annot-key": "preserved-annot-value", + }, + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + }, { + name: "Ensure that the app spec is normalized before applying", + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Finalizers: c.existingFinalizers, + Name: "name", + Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSpec{ - Project: "project", - Source: &argov1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"}, - // Destination is always invalid, for this test: - Destination: argov1alpha1.ApplicationDestination{Name: "my-cluster", Namespace: "namespace"}, + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{ + Directory: &v1alpha1.ApplicationSourceDirectory{ + Jsonnet: v1alpha1.ApplicationSourceJsonnet{}, + }, + }, + }, + }, }, - } - - initObjs := []crtclient.Object{&app, &appSet} - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build() - secret := &corev1.Secret{ + }, + desiredApps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{ + Directory: &v1alpha1.ApplicationSourceDirectory{ + Jsonnet: v1alpha1.ApplicationSourceJsonnet{}, + }, + }, + }, + }, + }, + expected: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "namespace", + ResourceVersion: "1", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{ + // Directory and jsonnet block are removed + }, + }, + }, + }, + }, { + // For this use case: https://github.com/argoproj/argo-cd/issues/9101#issuecomment-1191138278 + name: "Ensure that ignored targetRevision difference doesn't cause an update, even if another field changes", + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ - Name: "my-secret", + Name: "name", Namespace: "namespace", - Labels: map[string]string{ - generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster, + }, + Spec: v1alpha1.ApplicationSetSpec{ + IgnoreApplicationDifferences: v1alpha1.ApplicationSetIgnoreDifferences{ + {JQPathExpressions: []string{".spec.source.targetRevision"}}, + }, + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{ + RepoURL: "https://git.example.com/test-org/test-repo.git", + TargetRevision: "foo", + }, + }, }, }, - Data: map[string][]byte{ - // Since this test requires the cluster to be an invalid destination, we - // always return a cluster named 'my-cluster2' (different from app 'my-cluster', above) - "name": []byte("mycluster2"), - "server": []byte("https://kubernetes.default.svc"), - "config": []byte("{\"username\":\"foo\",\"password\":\"foo\"}"), - }, - } - - objects := append([]runtime.Object{}, secret) - kubeclientset := kubefake.NewSimpleClientset(objects...) - - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Recorder: record.NewFakeRecorder(10), - KubeClientset: kubeclientset, - } - //settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "namespace") - //argoDB := db.NewDB("namespace", settingsMgr, r.KubeClientset) - //clusterList, err := argoDB.ListClusters(context.Background()) - clusterList, err := utils.ListClusters(context.Background(), kubeclientset, "namespace") - assert.NoError(t, err, "Unexpected error") - - appLog := log.WithFields(log.Fields{"app": app.Name, "appSet": ""}) - - appInputParam := app.DeepCopy() - - err = r.removeFinalizerOnInvalidDestination(context.Background(), appSet, appInputParam, clusterList, appLog) - assert.NoError(t, err, "Unexpected error") - - retrievedApp := argov1alpha1.Application{} - err = client.Get(context.Background(), crtclient.ObjectKeyFromObject(&app), &retrievedApp) - assert.NoError(t, err, "Unexpected error") - - // App on the cluster should have the expected finalizers - assert.ElementsMatch(t, c.expectedFinalizers, retrievedApp.Finalizers) - - // App object passed in as a parameter should have the expected finaliers - assert.ElementsMatch(t, c.expectedFinalizers, appInputParam.Finalizers) - - bytes, _ := json.MarshalIndent(retrievedApp, "", " ") - t.Log("Contents of app after call:", string(bytes)) - - }) - } -} - -func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) { - - scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) - assert.Nil(t, err) - - err = argov1alpha1.AddToScheme(scheme) - assert.Nil(t, err) - - for _, c := range []struct { - // name is human-readable test name - name string - destinationField argov1alpha1.ApplicationDestination - expectFinalizerRemoved bool - }{ - { - name: "invalid cluster: empty destination", - destinationField: argov1alpha1.ApplicationDestination{ - Namespace: "namespace", - }, - expectFinalizerRemoved: true, - }, - { - name: "invalid cluster: invalid server url", - destinationField: argov1alpha1.ApplicationDestination{ - Namespace: "namespace", - Server: "https://1.2.3.4", - }, - expectFinalizerRemoved: true, - }, - { - name: "invalid cluster: invalid cluster name", - destinationField: argov1alpha1.ApplicationDestination{ - Namespace: "namespace", - Name: "invalid-cluster", - }, - expectFinalizerRemoved: true, - }, - { - name: "invalid cluster by both valid", - destinationField: argov1alpha1.ApplicationDestination{ - Namespace: "namespace", - Name: "mycluster2", - Server: "https://kubernetes.default.svc", - }, - expectFinalizerRemoved: true, - }, - { - name: "invalid cluster by both invalid", - destinationField: argov1alpha1.ApplicationDestination{ - Namespace: "namespace", - Name: "mycluster3", - Server: "https://4.5.6.7", - }, - expectFinalizerRemoved: true, - }, - { - name: "valid cluster by name", - destinationField: argov1alpha1.ApplicationDestination{ - Namespace: "namespace", - Name: "mycluster2", - }, - expectFinalizerRemoved: false, - }, - { - name: "valid cluster by server", - destinationField: argov1alpha1.ApplicationDestination{ - Namespace: "namespace", - Server: "https://kubernetes.default.svc", - }, - expectFinalizerRemoved: false, - }, - } { - - t.Run(c.name, func(t *testing.T) { - - appSet := argov1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "namespace", - }, - Spec: argov1alpha1.ApplicationSetSpec{ - Template: argov1alpha1.ApplicationSetTemplate{ - Spec: argov1alpha1.ApplicationSpec{ - Project: "project", - }, - }, - }, - } - - app := argov1alpha1.Application{ - ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Finalizers: []string{argov1alpha1.ResourcesFinalizerName}, - }, - Spec: argov1alpha1.ApplicationSpec{ - Project: "project", - Source: &argov1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"}, - Destination: c.destinationField, - }, - } - - initObjs := []crtclient.Object{&app, &appSet} - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build() - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-secret", - Namespace: "namespace", - Labels: map[string]string{ - generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster, - }, - }, - Data: map[string][]byte{ - // Since this test requires the cluster to be an invalid destination, we - // always return a cluster named 'my-cluster2' (different from app 'my-cluster', above) - "name": []byte("mycluster2"), - "server": []byte("https://kubernetes.default.svc"), - "config": []byte("{\"username\":\"foo\",\"password\":\"foo\"}"), - }, - } - - objects := append([]runtime.Object{}, secret) - kubeclientset := kubefake.NewSimpleClientset(objects...) - - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Recorder: record.NewFakeRecorder(10), - KubeClientset: kubeclientset, - } - // settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "argocd") - // argoDB := db.NewDB("argocd", settingsMgr, r.KubeClientset) - // clusterList, err := argoDB.ListClusters(context.Background()) - clusterList, err := utils.ListClusters(context.Background(), kubeclientset, "namespace") - assert.NoError(t, err, "Unexpected error") - - appLog := log.WithFields(log.Fields{"app": app.Name, "appSet": ""}) - - appInputParam := app.DeepCopy() - - err = r.removeFinalizerOnInvalidDestination(context.Background(), appSet, appInputParam, clusterList, appLog) - assert.NoError(t, err, "Unexpected error") - - retrievedApp := argov1alpha1.Application{} - err = client.Get(context.Background(), crtclient.ObjectKeyFromObject(&app), &retrievedApp) - assert.NoError(t, err, "Unexpected error") - - finalizerRemoved := len(retrievedApp.Finalizers) == 0 - - assert.True(t, c.expectFinalizerRemoved == finalizerRemoved) - - bytes, _ := json.MarshalIndent(retrievedApp, "", " ") - t.Log("Contents of app after call:", string(bytes)) - - }) - } -} - -func TestCreateApplications(t *testing.T) { - - scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) - assert.Nil(t, err) - - err = argov1alpha1.AddToScheme(scheme) - assert.Nil(t, err) - - for _, c := range []struct { - appSet argov1alpha1.ApplicationSet - existsApps []argov1alpha1.Application - apps []argov1alpha1.Application - expected []argov1alpha1.Application - }{ - { - appSet: argov1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "namespace", + }, + existingApps: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "namespace", + ResourceVersion: "2", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{ + RepoURL: "https://git.example.com/test-org/test-repo.git", + TargetRevision: "bar", + }, + }, }, }, - existsApps: nil, - apps: []argov1alpha1.Application{ + desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{ + RepoURL: "https://git.example.com/test-org/test-repo.git", + // The targetRevision is ignored, so this should not be updated. + TargetRevision: "foo", + // This should be updated. + Helm: &v1alpha1.ApplicationSourceHelm{ + Parameters: []v1alpha1.HelmParameter{ + {Name: "hi", Value: "there"}, + }, + }, + }, + }, }, }, - expected: []argov1alpha1.Application{ + expected: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ Kind: "Application", @@ -1184,26 +1064,52 @@ func TestCreateApplications(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app1", Namespace: "namespace", - ResourceVersion: "1", + ResourceVersion: "3", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{ + RepoURL: "https://git.example.com/test-org/test-repo.git", + // This is the existing value from the cluster, which should not be updated because the field is ignored. + TargetRevision: "bar", + // This was missing on the cluster, so it should be added. + Helm: &v1alpha1.ApplicationSourceHelm{ + Parameters: []v1alpha1.HelmParameter{ + {Name: "hi", Value: "there"}, + }, + }, + }, }, }, }, - }, - { - appSet: argov1alpha1.ApplicationSet{ + }, { + // For this use case: https://github.com/argoproj/argo-cd/pull/14743#issuecomment-1761954799 + name: "ignore parameters added to a multi-source app in the cluster", + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Template: argov1alpha1.ApplicationSetTemplate{ - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSetSpec{ + IgnoreApplicationDifferences: v1alpha1.ApplicationSetIgnoreDifferences{ + {JQPathExpressions: []string{`.spec.sources[] | select(.repoURL | contains("test-repo")).helm.parameters`}}, + }, + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", + Sources: []v1alpha1.ApplicationSource{ + { + RepoURL: "https://git.example.com/test-org/test-repo.git", + Helm: &v1alpha1.ApplicationSourceHelm{ + Values: "foo: bar", + }, + }, + }, }, }, }, }, - existsApps: []argov1alpha1.Application{ + existingApps: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ Kind: "Application", @@ -1214,53 +1120,96 @@ func TestCreateApplications(t *testing.T) { Namespace: "namespace", ResourceVersion: "2", }, - Spec: argov1alpha1.ApplicationSpec{ - Project: "test", + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Sources: []v1alpha1.ApplicationSource{ + { + RepoURL: "https://git.example.com/test-org/test-repo.git", + Helm: &v1alpha1.ApplicationSourceHelm{ + Values: "foo: bar", + Parameters: []v1alpha1.HelmParameter{ + {Name: "hi", Value: "there"}, + }, + }, + }, + }, }, }, }, - apps: []argov1alpha1.Application{ + desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", + Sources: []v1alpha1.ApplicationSource{ + { + RepoURL: "https://git.example.com/test-org/test-repo.git", + Helm: &v1alpha1.ApplicationSourceHelm{ + Values: "foo: bar", + }, + }, + }, }, }, }, - expected: []argov1alpha1.Application{ + expected: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ Kind: "Application", APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ - Name: "app1", - Namespace: "namespace", + Name: "app1", + Namespace: "namespace", + // This should not be updated, because reconciliation shouldn't modify the App. ResourceVersion: "2", }, - Spec: argov1alpha1.ApplicationSpec{ - Project: "test", + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Sources: []v1alpha1.ApplicationSource{ + { + RepoURL: "https://git.example.com/test-org/test-repo.git", + Helm: &v1alpha1.ApplicationSourceHelm{ + Values: "foo: bar", + Parameters: []v1alpha1.HelmParameter{ + // This existed only in the cluster, but it shouldn't be removed, because the field is ignored. + {Name: "hi", Value: "there"}, + }, + }, + }, + }, }, }, }, - }, - { - appSet: argov1alpha1.ApplicationSet{ + }, { + name: "Demonstrate limitation of MergePatch", // Maybe we can fix this in Argo CD 3.0: https://github.com/argoproj/argo-cd/issues/15975 + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Template: argov1alpha1.ApplicationSetTemplate{ - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSetSpec{ + IgnoreApplicationDifferences: v1alpha1.ApplicationSetIgnoreDifferences{ + {JQPathExpressions: []string{`.spec.sources[] | select(.repoURL | contains("test-repo")).helm.parameters`}}, + }, + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", + Sources: []v1alpha1.ApplicationSource{ + { + RepoURL: "https://git.example.com/test-org/test-repo.git", + Helm: &v1alpha1.ApplicationSourceHelm{ + Values: "new: values", + }, + }, + }, }, }, }, }, - existsApps: []argov1alpha1.Application{ + existingApps: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ Kind: "Application", @@ -1271,439 +1220,336 @@ func TestCreateApplications(t *testing.T) { Namespace: "namespace", ResourceVersion: "2", }, - Spec: argov1alpha1.ApplicationSpec{ - Project: "test", + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Sources: []v1alpha1.ApplicationSource{ + { + RepoURL: "https://git.example.com/test-org/test-repo.git", + Helm: &v1alpha1.ApplicationSourceHelm{ + Values: "foo: bar", + Parameters: []v1alpha1.HelmParameter{ + {Name: "hi", Value: "there"}, + }, + }, + }, + }, }, }, }, - apps: []argov1alpha1.Application{ + desiredApps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app2", + Name: "app1", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", + Sources: []v1alpha1.ApplicationSource{ + { + RepoURL: "https://git.example.com/test-org/test-repo.git", + Helm: &v1alpha1.ApplicationSourceHelm{ + Values: "new: values", + }, + }, + }, }, }, }, - expected: []argov1alpha1.Application{ + expected: []v1alpha1.Application{ { TypeMeta: metav1.TypeMeta{ Kind: "Application", APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ - Name: "app2", + Name: "app1", Namespace: "namespace", - ResourceVersion: "1", + ResourceVersion: "3", }, - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", + Sources: []v1alpha1.ApplicationSource{ + { + RepoURL: "https://git.example.com/test-org/test-repo.git", + Helm: &v1alpha1.ApplicationSourceHelm{ + Values: "new: values", + // The Parameters field got blown away, because the values field changed. MergePatch + // doesn't merge list items, it replaces the whole list if an item changes. + // If we eventually add a `name` field to Sources, we can use StrategicMergePatch. + }, + }, + }, }, }, }, }, } { - initObjs := []crtclient.Object{&c.appSet} - for _, a := range c.existsApps { - err = controllerutil.SetControllerReference(&c.appSet, &a, scheme) - assert.Nil(t, err) - initObjs = append(initObjs, &a) - } - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build() + t.Run(c.name, func(t *testing.T) { - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)), - } + initObjs := []crtclient.Object{&c.appSet} - err = r.createInCluster(context.TODO(), c.appSet, c.apps) - assert.Nil(t, err) + for _, a := range c.existingApps { + err = controllerutil.SetControllerReference(&c.appSet, &a, scheme) + assert.Nil(t, err) + initObjs = append(initObjs, &a) + } - for _, obj := range c.expected { - got := &argov1alpha1.Application{} - _ = client.Get(context.Background(), crtclient.ObjectKey{ - Namespace: obj.Namespace, - Name: obj.Name, - }, got) + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() - err = controllerutil.SetControllerReference(&c.appSet, &obj, r.Scheme) - assert.Nil(t, err) + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)), + Cache: &fakeCache{}, + } - assert.Equal(t, obj, *got) - } - } + err = r.createOrUpdateInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps) + assert.NoError(t, err) + + for _, obj := range c.expected { + got := &v1alpha1.Application{} + _ = client.Get(context.Background(), crtclient.ObjectKey{ + Namespace: obj.Namespace, + Name: obj.Name, + }, got) + err = controllerutil.SetControllerReference(&c.appSet, &obj, r.Scheme) + assert.Equal(t, obj, *got) + } + }) + } } -func TestDeleteInCluster(t *testing.T) { +func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) { scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) + err := v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - err = argov1alpha1.AddToScheme(scheme) + + err = v1alpha1.AddToScheme(scheme) assert.Nil(t, err) for _, c := range []struct { - // appSet is the application set on which the delete function is called - appSet argov1alpha1.ApplicationSet - // existingApps is the current state of Applications on the cluster - existingApps []argov1alpha1.Application - // desireApps is the apps generated by the generator that we wish to keep alive - desiredApps []argov1alpha1.Application - // expected is the list of applications that we expect to exist after calling delete - expected []argov1alpha1.Application - // notExpected is the list of applications that we expect not to exist after calling delete - notExpected []argov1alpha1.Application + // name is human-readable test name + name string + existingFinalizers []string + expectedFinalizers []string }{ { - appSet: argov1alpha1.ApplicationSet{ + name: "no finalizers", + existingFinalizers: []string{}, + expectedFinalizers: nil, + }, + { + name: "contains only argo finalizer", + existingFinalizers: []string{v1alpha1.ResourcesFinalizerName}, + expectedFinalizers: nil, + }, + { + name: "contains only non-argo finalizer", + existingFinalizers: []string{"non-argo-finalizer"}, + expectedFinalizers: []string{"non-argo-finalizer"}, + }, + { + name: "contains both argo and non-argo finalizer", + existingFinalizers: []string{"non-argo-finalizer", v1alpha1.ResourcesFinalizerName}, + expectedFinalizers: []string{"non-argo-finalizer"}, + }, + } { + t.Run(c.name, func(t *testing.T) { + + appSet := v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Template: argov1alpha1.ApplicationSetTemplate{ - Spec: argov1alpha1.ApplicationSpec{ + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ Project: "project", }, }, }, - }, - existingApps: []argov1alpha1.Application{ - { - TypeMeta: metav1.TypeMeta{ - Kind: "Application", - APIVersion: "argoproj.io/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "delete", - Namespace: "namespace", - ResourceVersion: "2", - }, - Spec: argov1alpha1.ApplicationSpec{ - Project: "project", - }, - }, - { - TypeMeta: metav1.TypeMeta{ - Kind: "Application", - APIVersion: "argoproj.io/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "keep", - Namespace: "namespace", - ResourceVersion: "2", - }, - Spec: argov1alpha1.ApplicationSpec{ - Project: "project", - }, + } + + app := v1alpha1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Finalizers: c.existingFinalizers, }, - }, - desiredApps: []argov1alpha1.Application{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "keep", - }, - Spec: argov1alpha1.ApplicationSpec{ - Project: "project", - }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"}, + // Destination is always invalid, for this test: + Destination: v1alpha1.ApplicationDestination{Name: "my-cluster", Namespace: "namespace"}, }, - }, - expected: []argov1alpha1.Application{ - { - TypeMeta: metav1.TypeMeta{ - Kind: "Application", - APIVersion: "argoproj.io/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "keep", - Namespace: "namespace", - ResourceVersion: "2", - }, - Spec: argov1alpha1.ApplicationSpec{ - Project: "project", + } + + initObjs := []crtclient.Object{&app, &appSet} + + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-secret", + Namespace: "namespace", + Labels: map[string]string{ + generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster, }, }, - }, - notExpected: []argov1alpha1.Application{ - { - TypeMeta: metav1.TypeMeta{ - Kind: "Application", - APIVersion: "argoproj.io/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "delete", - Namespace: "namespace", - ResourceVersion: "1", - }, - Spec: argov1alpha1.ApplicationSpec{ - Project: "project", - }, + Data: map[string][]byte{ + // Since this test requires the cluster to be an invalid destination, we + // always return a cluster named 'my-cluster2' (different from app 'my-cluster', above) + "name": []byte("mycluster2"), + "server": []byte("https://kubernetes.default.svc"), + "config": []byte("{\"username\":\"foo\",\"password\":\"foo\"}"), }, - }, - }, - } { - initObjs := []crtclient.Object{&c.appSet} - for _, a := range c.existingApps { - temp := a - err = controllerutil.SetControllerReference(&c.appSet, &temp, scheme) - assert.Nil(t, err) - initObjs = append(initObjs, &temp) - } - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build() + } - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)), - KubeClientset: kubefake.NewSimpleClientset(), - } + objects := append([]runtime.Object{}, secret) + kubeclientset := kubefake.NewSimpleClientset(objects...) - err = r.deleteInCluster(context.TODO(), c.appSet, c.desiredApps) - assert.Nil(t, err) + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(10), + KubeClientset: kubeclientset, + Cache: &fakeCache{}, + } + //settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "namespace") + //argoDB := db.NewDB("namespace", settingsMgr, r.KubeClientset) + //clusterList, err := argoDB.ListClusters(context.Background()) + clusterList, err := utils.ListClusters(context.Background(), kubeclientset, "namespace") + assert.NoError(t, err, "Unexpected error") - // For each of the expected objects, verify they exist on the cluster - for _, obj := range c.expected { - got := &argov1alpha1.Application{} - _ = client.Get(context.Background(), crtclient.ObjectKey{ - Namespace: obj.Namespace, - Name: obj.Name, - }, got) + appLog := log.WithFields(log.Fields{"app": app.Name, "appSet": ""}) - err = controllerutil.SetControllerReference(&c.appSet, &obj, r.Scheme) - assert.Nil(t, err) + appInputParam := app.DeepCopy() - assert.Equal(t, obj, *got) - } + err = r.removeFinalizerOnInvalidDestination(context.Background(), appSet, appInputParam, clusterList, appLog) + assert.NoError(t, err, "Unexpected error") - // Verify each of the unexpected objs cannot be found - for _, obj := range c.notExpected { - got := &argov1alpha1.Application{} - err := client.Get(context.Background(), crtclient.ObjectKey{ - Namespace: obj.Namespace, - Name: obj.Name, - }, got) + retrievedApp := v1alpha1.Application{} + err = client.Get(context.Background(), crtclient.ObjectKeyFromObject(&app), &retrievedApp) + assert.NoError(t, err, "Unexpected error") - assert.EqualError(t, err, fmt.Sprintf("applications.argoproj.io \"%s\" not found", obj.Name)) - } - } -} + // App on the cluster should have the expected finalizers + assert.ElementsMatch(t, c.expectedFinalizers, retrievedApp.Finalizers) -func TestGetMinRequeueAfter(t *testing.T) { - scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) - assert.Nil(t, err) - err = argov1alpha1.AddToScheme(scheme) - assert.Nil(t, err) + // App object passed in as a parameter should have the expected finaliers + assert.ElementsMatch(t, c.expectedFinalizers, appInputParam.Finalizers) - client := fake.NewClientBuilder().WithScheme(scheme).Build() + bytes, _ := json.MarshalIndent(retrievedApp, "", " ") + t.Log("Contents of app after call:", string(bytes)) - generator := argov1alpha1.ApplicationSetGenerator{ - List: &argov1alpha1.ListGenerator{}, - Git: &argov1alpha1.GitGenerator{}, - Clusters: &argov1alpha1.ClusterGenerator{}, + }) } +} - generatorMock0 := generatorMock{} - generatorMock0.On("GetRequeueAfter", &generator). - Return(generators.NoRequeueAfter) - - generatorMock1 := generatorMock{} - generatorMock1.On("GetRequeueAfter", &generator). - Return(time.Duration(1) * time.Second) +func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) { - generatorMock10 := generatorMock{} - generatorMock10.On("GetRequeueAfter", &generator). - Return(time.Duration(10) * time.Second) + scheme := runtime.NewScheme() + err := v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Recorder: record.NewFakeRecorder(0), - Generators: map[string]generators.Generator{ - "List": &generatorMock10, - "Git": &generatorMock1, - "Clusters": &generatorMock1, - }, - } + err = v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) - got := r.getMinRequeueAfter(&argov1alpha1.ApplicationSet{ - Spec: argov1alpha1.ApplicationSetSpec{ - Generators: []argov1alpha1.ApplicationSetGenerator{generator}, - }, - }) - - assert.Equal(t, time.Duration(1)*time.Second, got) -} - -func TestValidateGeneratedApplications(t *testing.T) { - - scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) - assert.Nil(t, err) - - err = argov1alpha1.AddToScheme(scheme) - assert.Nil(t, err) - - client := fake.NewClientBuilder().WithScheme(scheme).Build() - - // Valid cluster - myCluster := argov1alpha1.Cluster{ - Server: "https://kubernetes.default.svc", - Name: "my-cluster", - } - - // Valid project - myProject := &argov1alpha1.AppProject{ - ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "namespace"}, - Spec: argov1alpha1.AppProjectSpec{ - SourceRepos: []string{"*"}, - Destinations: []argov1alpha1.ApplicationDestination{ - { - Namespace: "*", - Server: "*", - }, + for _, c := range []struct { + // name is human-readable test name + name string + destinationField v1alpha1.ApplicationDestination + expectFinalizerRemoved bool + }{ + { + name: "invalid cluster: empty destination", + destinationField: v1alpha1.ApplicationDestination{ + Namespace: "namespace", }, - ClusterResourceWhitelist: []metav1.GroupKind{ - { - Group: "*", - Kind: "*", - }, + expectFinalizerRemoved: true, + }, + { + name: "invalid cluster: invalid server url", + destinationField: v1alpha1.ApplicationDestination{ + Namespace: "namespace", + Server: "https://1.2.3.4", }, + expectFinalizerRemoved: true, }, - } - - // Test a subset of the validations that 'validateGeneratedApplications' performs - for _, cc := range []struct { - name string - apps []argov1alpha1.Application - expectedErrors []string - validationErrors map[int]error - }{ { - name: "valid app should return true", - apps: []argov1alpha1.Application{ - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{}, - Spec: argov1alpha1.ApplicationSpec{ - Project: "default", - Source: &argov1alpha1.ApplicationSource{ - RepoURL: "https://url", - Path: "/", - TargetRevision: "HEAD", - }, - Destination: argov1alpha1.ApplicationDestination{ - Namespace: "namespace", - Name: "my-cluster", - }, - }, - }, + name: "invalid cluster: invalid cluster name", + destinationField: v1alpha1.ApplicationDestination{ + Namespace: "namespace", + Name: "invalid-cluster", }, - expectedErrors: []string{}, - validationErrors: map[int]error{}, + expectFinalizerRemoved: true, }, { - name: "can't have both name and server defined", - apps: []argov1alpha1.Application{ - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{}, - Spec: argov1alpha1.ApplicationSpec{ - Project: "default", - Source: &argov1alpha1.ApplicationSource{ - RepoURL: "https://url", - Path: "/", - TargetRevision: "HEAD", - }, - Destination: argov1alpha1.ApplicationDestination{ - Namespace: "namespace", - Server: "my-server", - Name: "my-cluster", - }, - }, - }, + name: "invalid cluster by both valid", + destinationField: v1alpha1.ApplicationDestination{ + Namespace: "namespace", + Name: "mycluster2", + Server: "https://kubernetes.default.svc", }, - expectedErrors: []string{"application destination can't have both name and server defined"}, - validationErrors: map[int]error{0: fmt.Errorf("application destination spec is invalid: application destination can't have both name and server defined: my-cluster my-server")}, + expectFinalizerRemoved: true, }, { - name: "project mismatch should return error", - apps: []argov1alpha1.Application{ - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{}, - Spec: argov1alpha1.ApplicationSpec{ - Project: "DOES-NOT-EXIST", - Source: &argov1alpha1.ApplicationSource{ - RepoURL: "https://url", - Path: "/", - TargetRevision: "HEAD", - }, - Destination: argov1alpha1.ApplicationDestination{ - Namespace: "namespace", - Name: "my-cluster", - }, - }, - }, + name: "invalid cluster by both invalid", + destinationField: v1alpha1.ApplicationDestination{ + Namespace: "namespace", + Name: "mycluster3", + Server: "https://4.5.6.7", }, - expectedErrors: []string{"application references project DOES-NOT-EXIST which does not exist"}, - validationErrors: map[int]error{0: fmt.Errorf("application references project DOES-NOT-EXIST which does not exist")}, + expectFinalizerRemoved: true, }, { - name: "valid app should return true", - apps: []argov1alpha1.Application{ - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{}, - Spec: argov1alpha1.ApplicationSpec{ - Project: "default", - Source: &argov1alpha1.ApplicationSource{ - RepoURL: "https://url", - Path: "/", - TargetRevision: "HEAD", - }, - Destination: argov1alpha1.ApplicationDestination{ - Namespace: "namespace", - Name: "my-cluster", - }, - }, - }, + name: "valid cluster by name", + destinationField: v1alpha1.ApplicationDestination{ + Namespace: "namespace", + Name: "mycluster2", }, - expectedErrors: []string{}, - validationErrors: map[int]error{}, + expectFinalizerRemoved: false, }, { - name: "cluster should match", - apps: []argov1alpha1.Application{ - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{}, - Spec: argov1alpha1.ApplicationSpec{ - Project: "default", - Source: &argov1alpha1.ApplicationSource{ - RepoURL: "https://url", - Path: "/", - TargetRevision: "HEAD", - }, - Destination: argov1alpha1.ApplicationDestination{ - Namespace: "namespace", - Name: "nonexistent-cluster", - }, - }, - }, + name: "valid cluster by server", + destinationField: v1alpha1.ApplicationDestination{ + Namespace: "namespace", + Server: "https://kubernetes.default.svc", }, - expectedErrors: []string{"there are no clusters with this name: nonexistent-cluster"}, - validationErrors: map[int]error{0: fmt.Errorf("application destination spec is invalid: unable to find destination server: there are no clusters with this name: nonexistent-cluster")}, + expectFinalizerRemoved: false, }, } { - t.Run(cc.name, func(t *testing.T) { + t.Run(c.name, func(t *testing.T) { + appSet := v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + } + + app := v1alpha1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Finalizers: []string{v1alpha1.ResourcesFinalizerName}, + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"}, + Destination: c.destinationField, + }, + } + + initObjs := []crtclient.Object{&app, &appSet} + + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "my-secret", @@ -1713,110 +1559,1221 @@ func TestValidateGeneratedApplications(t *testing.T) { }, }, Data: map[string][]byte{ - "name": []byte("my-cluster"), + // Since this test requires the cluster to be an invalid destination, we + // always return a cluster named 'my-cluster2' (different from app 'my-cluster', above) + "name": []byte("mycluster2"), "server": []byte("https://kubernetes.default.svc"), "config": []byte("{\"username\":\"foo\",\"password\":\"foo\"}"), }, } - objects := append([]runtime.Object{}, secret) - kubeclientset := kubefake.NewSimpleClientset(objects...) + objects := append([]runtime.Object{}, secret) + kubeclientset := kubefake.NewSimpleClientset(objects...) + + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(10), + KubeClientset: kubeclientset, + Cache: &fakeCache{}, + } + // settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "argocd") + // argoDB := db.NewDB("argocd", settingsMgr, r.KubeClientset) + // clusterList, err := argoDB.ListClusters(context.Background()) + clusterList, err := utils.ListClusters(context.Background(), kubeclientset, "namespace") + assert.NoError(t, err, "Unexpected error") + + appLog := log.WithFields(log.Fields{"app": app.Name, "appSet": ""}) + + appInputParam := app.DeepCopy() + + err = r.removeFinalizerOnInvalidDestination(context.Background(), appSet, appInputParam, clusterList, appLog) + assert.NoError(t, err, "Unexpected error") + + retrievedApp := v1alpha1.Application{} + err = client.Get(context.Background(), crtclient.ObjectKeyFromObject(&app), &retrievedApp) + assert.NoError(t, err, "Unexpected error") + + finalizerRemoved := len(retrievedApp.Finalizers) == 0 + + assert.True(t, c.expectFinalizerRemoved == finalizerRemoved) + + bytes, _ := json.MarshalIndent(retrievedApp, "", " ") + t.Log("Contents of app after call:", string(bytes)) + + }) + } +} + +func TestRemoveOwnerReferencesOnDeleteAppSet(t *testing.T) { + scheme := runtime.NewScheme() + err := v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + err = v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + for _, c := range []struct { + // name is human-readable test name + name string + }{ + { + name: "ownerReferences cleared", + }, + } { + t.Run(c.name, func(t *testing.T) { + appSet := v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + Finalizers: []string{v1alpha1.ResourcesFinalizerName}, + }, + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + } + + app := v1alpha1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "namespace", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"}, + Destination: v1alpha1.ApplicationDestination{ + Namespace: "namespace", + Server: "https://kubernetes.default.svc", + }, + }, + } + + err := controllerutil.SetControllerReference(&appSet, &app, scheme) + assert.NoError(t, err, "Unexpected error") + + initObjs := []crtclient.Object{&app, &appSet} + + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() + + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(10), + KubeClientset: nil, + Cache: &fakeCache{}, + } + + err = r.removeOwnerReferencesOnDeleteAppSet(context.Background(), appSet) + assert.NoError(t, err, "Unexpected error") + + retrievedApp := v1alpha1.Application{} + err = client.Get(context.Background(), crtclient.ObjectKeyFromObject(&app), &retrievedApp) + assert.NoError(t, err, "Unexpected error") + + ownerReferencesRemoved := len(retrievedApp.OwnerReferences) == 0 + assert.True(t, ownerReferencesRemoved) + }) + } +} + +func TestCreateApplications(t *testing.T) { + + scheme := runtime.NewScheme() + err := v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + err = v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + testCases := []struct { + name string + appSet v1alpha1.ApplicationSet + existsApps []v1alpha1.Application + apps []v1alpha1.Application + expected []v1alpha1.Application + }{ + { + name: "no existing apps", + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + }, + }, + existsApps: nil, + apps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + }, + }, + }, + expected: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "namespace", + ResourceVersion: "1", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "default", + }, + }, + }, + }, + { + name: "existing apps", + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + }, + existsApps: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "namespace", + ResourceVersion: "2", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "test", + }, + }, + }, + apps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + expected: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "namespace", + ResourceVersion: "2", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "test", + }, + }, + }, + }, + { + name: "existing apps with different project", + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + }, + existsApps: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "namespace", + ResourceVersion: "2", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "test", + }, + }, + }, + apps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app2", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + expected: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "app2", + Namespace: "namespace", + ResourceVersion: "1", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + initObjs := []crtclient.Object{&c.appSet} + for _, a := range c.existsApps { + err = controllerutil.SetControllerReference(&c.appSet, &a, scheme) + assert.Nil(t, err) + initObjs = append(initObjs, &a) + } + + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() + + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)), + Cache: &fakeCache{}, + } + + err = r.createInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.apps) + assert.Nil(t, err) + + for _, obj := range c.expected { + got := &v1alpha1.Application{} + _ = client.Get(context.Background(), crtclient.ObjectKey{ + Namespace: obj.Namespace, + Name: obj.Name, + }, got) + + err = controllerutil.SetControllerReference(&c.appSet, &obj, r.Scheme) + assert.Nil(t, err) + + assert.Equal(t, obj, *got) + } + }) + } +} + +func TestDeleteInCluster(t *testing.T) { + + scheme := runtime.NewScheme() + err := v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + err = v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + for _, c := range []struct { + // appSet is the application set on which the delete function is called + appSet v1alpha1.ApplicationSet + // existingApps is the current state of Applications on the cluster + existingApps []v1alpha1.Application + // desireApps is the apps generated by the generator that we wish to keep alive + desiredApps []v1alpha1.Application + // expected is the list of applications that we expect to exist after calling delete + expected []v1alpha1.Application + // notExpected is the list of applications that we expect not to exist after calling delete + notExpected []v1alpha1.Application + }{ + { + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + }, + existingApps: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "delete", + Namespace: "namespace", + ResourceVersion: "2", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + { + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "keep", + Namespace: "namespace", + ResourceVersion: "2", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + desiredApps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "keep", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + expected: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "keep", + Namespace: "namespace", + ResourceVersion: "2", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + notExpected: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "delete", + Namespace: "namespace", + ResourceVersion: "1", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + }, + }, + }, + }, + } { + initObjs := []crtclient.Object{&c.appSet} + for _, a := range c.existingApps { + temp := a + err = controllerutil.SetControllerReference(&c.appSet, &temp, scheme) + assert.Nil(t, err) + initObjs = append(initObjs, &temp) + } + + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() + + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)), + KubeClientset: kubefake.NewSimpleClientset(), + } + + err = r.deleteInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps) + assert.Nil(t, err) + + // For each of the expected objects, verify they exist on the cluster + for _, obj := range c.expected { + got := &v1alpha1.Application{} + _ = client.Get(context.Background(), crtclient.ObjectKey{ + Namespace: obj.Namespace, + Name: obj.Name, + }, got) + + err = controllerutil.SetControllerReference(&c.appSet, &obj, r.Scheme) + assert.Nil(t, err) + + assert.Equal(t, obj, *got) + } + + // Verify each of the unexpected objs cannot be found + for _, obj := range c.notExpected { + got := &v1alpha1.Application{} + err := client.Get(context.Background(), crtclient.ObjectKey{ + Namespace: obj.Namespace, + Name: obj.Name, + }, got) + + assert.EqualError(t, err, fmt.Sprintf("applications.argoproj.io \"%s\" not found", obj.Name)) + } + } +} + +func TestGetMinRequeueAfter(t *testing.T) { + scheme := runtime.NewScheme() + err := v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + err = v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + client := fake.NewClientBuilder().WithScheme(scheme).Build() + + generator := v1alpha1.ApplicationSetGenerator{ + List: &v1alpha1.ListGenerator{}, + Git: &v1alpha1.GitGenerator{}, + Clusters: &v1alpha1.ClusterGenerator{}, + } + + generatorMock0 := generatorMock{} + generatorMock0.On("GetRequeueAfter", &generator). + Return(generators.NoRequeueAfter) + + generatorMock1 := generatorMock{} + generatorMock1.On("GetRequeueAfter", &generator). + Return(time.Duration(1) * time.Second) + + generatorMock10 := generatorMock{} + generatorMock10.On("GetRequeueAfter", &generator). + Return(time.Duration(10) * time.Second) + + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(0), + Cache: &fakeCache{}, + Generators: map[string]generators.Generator{ + "List": &generatorMock10, + "Git": &generatorMock1, + "Clusters": &generatorMock1, + }, + } + + got := r.getMinRequeueAfter(&v1alpha1.ApplicationSet{ + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{generator}, + }, + }) + + assert.Equal(t, time.Duration(1)*time.Second, got) +} + +func TestValidateGeneratedApplications(t *testing.T) { + + scheme := runtime.NewScheme() + err := v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + err = v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + client := fake.NewClientBuilder().WithScheme(scheme).Build() + + // Valid cluster + myCluster := v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Name: "my-cluster", + } + + // Valid project + myProject := &v1alpha1.AppProject{ + ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "namespace"}, + Spec: v1alpha1.AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []v1alpha1.ApplicationDestination{ + { + Namespace: "*", + Server: "*", + }, + }, + ClusterResourceWhitelist: []metav1.GroupKind{ + { + Group: "*", + Kind: "*", + }, + }, + }, + } + + // Test a subset of the validations that 'validateGeneratedApplications' performs + for _, cc := range []struct { + name string + apps []v1alpha1.Application + expectedErrors []string + validationErrors map[int]error + }{ + { + name: "valid app should return true", + apps: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: v1alpha1.ApplicationSpec{ + Project: "default", + Source: &v1alpha1.ApplicationSource{ + RepoURL: "https://url", + Path: "/", + TargetRevision: "HEAD", + }, + Destination: v1alpha1.ApplicationDestination{ + Namespace: "namespace", + Name: "my-cluster", + }, + }, + }, + }, + expectedErrors: []string{}, + validationErrors: map[int]error{}, + }, + { + name: "can't have both name and server defined", + apps: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: v1alpha1.ApplicationSpec{ + Project: "default", + Source: &v1alpha1.ApplicationSource{ + RepoURL: "https://url", + Path: "/", + TargetRevision: "HEAD", + }, + Destination: v1alpha1.ApplicationDestination{ + Namespace: "namespace", + Server: "my-server", + Name: "my-cluster", + }, + }, + }, + }, + expectedErrors: []string{"application destination can't have both name and server defined"}, + validationErrors: map[int]error{0: fmt.Errorf("application destination spec is invalid: application destination can't have both name and server defined: my-cluster my-server")}, + }, + { + name: "project mismatch should return error", + apps: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: v1alpha1.ApplicationSpec{ + Project: "DOES-NOT-EXIST", + Source: &v1alpha1.ApplicationSource{ + RepoURL: "https://url", + Path: "/", + TargetRevision: "HEAD", + }, + Destination: v1alpha1.ApplicationDestination{ + Namespace: "namespace", + Name: "my-cluster", + }, + }, + }, + }, + expectedErrors: []string{"application references project DOES-NOT-EXIST which does not exist"}, + validationErrors: map[int]error{0: fmt.Errorf("application references project DOES-NOT-EXIST which does not exist")}, + }, + { + name: "valid app should return true", + apps: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: v1alpha1.ApplicationSpec{ + Project: "default", + Source: &v1alpha1.ApplicationSource{ + RepoURL: "https://url", + Path: "/", + TargetRevision: "HEAD", + }, + Destination: v1alpha1.ApplicationDestination{ + Namespace: "namespace", + Name: "my-cluster", + }, + }, + }, + }, + expectedErrors: []string{}, + validationErrors: map[int]error{}, + }, + { + name: "cluster should match", + apps: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: v1alpha1.ApplicationSpec{ + Project: "default", + Source: &v1alpha1.ApplicationSource{ + RepoURL: "https://url", + Path: "/", + TargetRevision: "HEAD", + }, + Destination: v1alpha1.ApplicationDestination{ + Namespace: "namespace", + Name: "nonexistent-cluster", + }, + }, + }, + }, + expectedErrors: []string{"there are no clusters with this name: nonexistent-cluster"}, + validationErrors: map[int]error{0: fmt.Errorf("application destination spec is invalid: unable to find destination server: there are no clusters with this name: nonexistent-cluster")}, + }, + } { + + t.Run(cc.name, func(t *testing.T) { + + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-secret", + Namespace: "namespace", + Labels: map[string]string{ + generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster, + }, + }, + Data: map[string][]byte{ + "name": []byte("my-cluster"), + "server": []byte("https://kubernetes.default.svc"), + "config": []byte("{\"username\":\"foo\",\"password\":\"foo\"}"), + }, + } + + objects := append([]runtime.Object{}, secret) + kubeclientset := kubefake.NewSimpleClientset(objects...) + + argoDBMock := dbmocks.ArgoDB{} + argoDBMock.On("GetCluster", mock.Anything, "https://kubernetes.default.svc").Return(&myCluster, nil) + argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{ + myCluster, + }}, nil) + + argoObjs := []runtime.Object{myProject} + for _, app := range cc.apps { + argoObjs = append(argoObjs, &app) + } + + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, + Generators: map[string]generators.Generator{}, + ArgoDB: &argoDBMock, + ArgoCDNamespace: "namespace", + ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), + KubeClientset: kubeclientset, + } + + appSetInfo := v1alpha1.ApplicationSet{} + + validationErrors, _ := r.validateGeneratedApplications(context.TODO(), cc.apps, appSetInfo) + var errorMessages []string + for _, v := range validationErrors { + errorMessages = append(errorMessages, v.Error()) + } + + if len(errorMessages) == 0 { + assert.Equal(t, len(cc.expectedErrors), 0, "Expected errors but none were seen") + } else { + // An error was returned: it should be expected + matched := false + for _, expectedErr := range cc.expectedErrors { + foundMatch := strings.Contains(strings.Join(errorMessages, ";"), expectedErr) + assert.True(t, foundMatch, "Unble to locate expected error: %s", cc.expectedErrors) + matched = matched || foundMatch + } + assert.True(t, matched, "An unexpected error occurrred: %v", err) + // validation message was returned: it should be expected + matched = false + foundMatch := reflect.DeepEqual(validationErrors, cc.validationErrors) + var message string + for _, v := range validationErrors { + message = v.Error() + break + } + assert.True(t, foundMatch, "Unble to locate validation message: %s", message) + matched = matched || foundMatch + assert.True(t, matched, "An unexpected error occurrred: %v", err) + } + }) + } +} + +func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) { + + scheme := runtime.NewScheme() + err := v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + err = v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + project := v1alpha1.AppProject{ + ObjectMeta: metav1.ObjectMeta{Name: "good-project", Namespace: "argocd"}, + } + appSet := v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"project": "good-project"}`), + }, { + Raw: []byte(`{"project": "bad-project"}`), + }}, + }, + }, + }, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ + Name: "{{.project}}", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSpec{ + Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"}, + Project: "{{.project}}", + Destination: v1alpha1.ApplicationDestination{Server: "https://kubernetes.default.svc"}, + }, + }, + }, + } + + kubeclientset := kubefake.NewSimpleClientset() + argoDBMock := dbmocks.ArgoDB{} + argoObjs := []runtime.Object{&project} + + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() + goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"} + badCluster := v1alpha1.Cluster{Server: "https://bad-cluster", Name: "bad-cluster"} + argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil) + argoDBMock.On("GetCluster", mock.Anything, "https://bad-cluster").Return(&badCluster, nil) + argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{ + goodCluster, + }}, nil) + + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Renderer: &utils.Render{}, + Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, + Generators: map[string]generators.Generator{ + "List": generators.NewListGenerator(), + }, + ArgoDB: &argoDBMock, + ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), + KubeClientset: kubeclientset, + Policy: v1alpha1.ApplicationsSyncPolicySync, + ArgoCDNamespace: "argocd", + } + + req := ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: "argocd", + Name: "name", + }, + } + + // Verify that on validation error, no error is returned, but the object is requeued + res, err := r.Reconcile(context.Background(), req) + assert.Nil(t, err) + assert.True(t, res.RequeueAfter == ReconcileRequeueOnValidationError) + + var app v1alpha1.Application + + // make sure good app got created + err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "good-project"}, &app) + assert.NoError(t, err) + assert.Equal(t, app.Name, "good-project") + + // make sure bad app was not created + err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "bad-project"}, &app) + assert.Error(t, err) +} + +func TestReconcilerCreateAppsRecoveringRenderError(t *testing.T) { + + scheme := runtime.NewScheme() + err := v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + err = v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + project := v1alpha1.AppProject{ + ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"}, + } + appSet := v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"name": "very-good-app"}`), + }, { + Raw: []byte(`{"name": "bad-app"}`), + }}, + }, + }, + }, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ + Name: "{{ index (splitList \"-\" .name ) 2 }}", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSpec{ + Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"}, + Project: "default", + Destination: v1alpha1.ApplicationDestination{Server: "https://kubernetes.default.svc"}, + }, + }, + }, + } + + kubeclientset := kubefake.NewSimpleClientset() + argoDBMock := dbmocks.ArgoDB{} + argoObjs := []runtime.Object{&project} + + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() + + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Renderer: &utils.Render{}, + Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, + Generators: map[string]generators.Generator{ + "List": generators.NewListGenerator(), + }, + ArgoDB: &argoDBMock, + ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), + KubeClientset: kubeclientset, + Policy: v1alpha1.ApplicationsSyncPolicySync, + ArgoCDNamespace: "argocd", + } + + req := ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: "argocd", + Name: "name", + }, + } + + // Verify that on generatorsError, no error is returned, but the object is requeued + res, err := r.Reconcile(context.Background(), req) + assert.Nil(t, err) + assert.True(t, res.RequeueAfter == ReconcileRequeueOnValidationError) + + var app v1alpha1.Application + + // make sure good app got created + err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "app"}, &app) + assert.NoError(t, err) + assert.Equal(t, app.Name, "app") +} + +func TestSetApplicationSetStatusCondition(t *testing.T) { + scheme := runtime.NewScheme() + err := v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + err = v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + appSet := v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ + {List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), + }}, + }}, + }, + Template: v1alpha1.ApplicationSetTemplate{}, + }, + } + + appCondition := v1alpha1.ApplicationSetCondition{ + Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, + Message: "All applications have been generated successfully", + Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, + Status: v1alpha1.ApplicationSetConditionStatusTrue, + } + + kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...) + argoDBMock := dbmocks.ArgoDB{} + argoObjs := []runtime.Object{} + + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() + + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Renderer: &utils.Render{}, + Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, + Generators: map[string]generators.Generator{ + "List": generators.NewListGenerator(), + }, + ArgoDB: &argoDBMock, + ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), + KubeClientset: kubeclientset, + } + + err = r.setApplicationSetStatusCondition(context.TODO(), &appSet, appCondition, true) + assert.Nil(t, err) + + assert.Len(t, appSet.Status.Conditions, 3) +} + +func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alpha1.ApplicationsSyncPolicy, recordBuffer int, allowPolicyOverride bool) v1alpha1.Application { + + scheme := runtime.NewScheme() + err := v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + err = v1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + + defaultProject := v1alpha1.AppProject{ + ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"}, + Spec: v1alpha1.AppProjectSpec{SourceRepos: []string{"*"}, Destinations: []v1alpha1.ApplicationDestination{{Namespace: "*", Server: "https://good-cluster"}}}, + } + appSet := v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"cluster": "good-cluster","url": "https://good-cluster"}`), + }}, + }, + }, + }, + SyncPolicy: &v1alpha1.ApplicationSetSyncPolicy{ + ApplicationsSync: &applicationsSyncPolicy, + }, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ + Name: "{{cluster}}", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSpec{ + Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"}, + Project: "default", + Destination: v1alpha1.ApplicationDestination{Server: "{{url}}"}, + }, + }, + }, + } + + kubeclientset := kubefake.NewSimpleClientset() + argoDBMock := dbmocks.ArgoDB{} + argoObjs := []runtime.Object{&defaultProject} + + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() + goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"} + argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil) + argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{ + goodCluster, + }}, nil) + + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Renderer: &utils.Render{}, + Recorder: record.NewFakeRecorder(recordBuffer), + Cache: &fakeCache{}, + Generators: map[string]generators.Generator{ + "List": generators.NewListGenerator(), + }, + ArgoDB: &argoDBMock, + ArgoCDNamespace: "argocd", + ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), + KubeClientset: kubeclientset, + Policy: v1alpha1.ApplicationsSyncPolicySync, + EnablePolicyOverride: allowPolicyOverride, + } + + req := ctrl.Request{ + NamespacedName: types.NamespacedName{ + Namespace: "argocd", + Name: "name", + }, + } + + // Verify that on validation error, no error is returned, but the object is requeued + resCreate, err := r.Reconcile(context.Background(), req) + assert.Nil(t, err) + assert.True(t, resCreate.RequeueAfter == 0) + + var app v1alpha1.Application + + // make sure good app got created + err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "good-cluster"}, &app) + assert.Nil(t, err) + assert.Equal(t, app.Name, "good-cluster") + + // Update resource + var retrievedApplicationSet v1alpha1.ApplicationSet + err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "name"}, &retrievedApplicationSet) + assert.Nil(t, err) + + retrievedApplicationSet.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} + retrievedApplicationSet.Spec.Template.Labels = map[string]string{"label-key": "label-value"} + + retrievedApplicationSet.Spec.Template.Spec.Source.Helm = &v1alpha1.ApplicationSourceHelm{ + Values: "global.test: test", + } + + err = r.Client.Update(context.TODO(), &retrievedApplicationSet) + assert.Nil(t, err) + + resUpdate, err := r.Reconcile(context.Background(), req) + assert.Nil(t, err) + + err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "good-cluster"}, &app) + assert.Nil(t, err) + assert.True(t, resUpdate.RequeueAfter == 0) + assert.Equal(t, app.Name, "good-cluster") + + return app +} + +func TestUpdateNotPerformedWithSyncPolicyCreateOnly(t *testing.T) { + + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateOnly + + app := applicationsUpdateSyncPolicyTest(t, applicationsSyncPolicy, 1, true) + + assert.Nil(t, app.Spec.Source.Helm) + assert.Nil(t, app.ObjectMeta.Annotations) +} + +func TestUpdateNotPerformedWithSyncPolicyCreateDelete(t *testing.T) { - argoDBMock := dbmocks.ArgoDB{} - argoDBMock.On("GetCluster", mock.Anything, "https://kubernetes.default.svc").Return(&myCluster, nil) - argoDBMock.On("ListClusters", mock.Anything).Return(&argov1alpha1.ClusterList{Items: []argov1alpha1.Cluster{ - myCluster, - }}, nil) + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateDelete - argoObjs := []runtime.Object{myProject} - for _, app := range cc.apps { - argoObjs = append(argoObjs, &app) - } + app := applicationsUpdateSyncPolicyTest(t, applicationsSyncPolicy, 1, true) - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Recorder: record.NewFakeRecorder(1), - Generators: map[string]generators.Generator{}, - ArgoDB: &argoDBMock, - ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), - KubeClientset: kubeclientset, - } + assert.Nil(t, app.Spec.Source.Helm) + assert.Nil(t, app.ObjectMeta.Annotations) +} - appSetInfo := argov1alpha1.ApplicationSet{} +func TestUpdatePerformedWithSyncPolicyCreateUpdate(t *testing.T) { - validationErrors, _ := r.validateGeneratedApplications(context.TODO(), cc.apps, appSetInfo, "namespace") - var errorMessages []string - for _, v := range validationErrors { - errorMessages = append(errorMessages, v.Error()) - } + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateUpdate - if len(errorMessages) == 0 { - assert.Equal(t, len(cc.expectedErrors), 0, "Expected errors but none were seen") - } else { - // An error was returned: it should be expected - matched := false - for _, expectedErr := range cc.expectedErrors { - foundMatch := strings.Contains(strings.Join(errorMessages, ";"), expectedErr) - assert.True(t, foundMatch, "Unble to locate expected error: %s", cc.expectedErrors) - matched = matched || foundMatch - } - assert.True(t, matched, "An unexpected error occurrred: %v", err) - // validation message was returned: it should be expected - matched = false - foundMatch := reflect.DeepEqual(validationErrors, cc.validationErrors) - var message string - for _, v := range validationErrors { - message = v.Error() - break - } - assert.True(t, foundMatch, "Unble to locate validation message: %s", message) - matched = matched || foundMatch - assert.True(t, matched, "An unexpected error occurrred: %v", err) - } - }) - } + app := applicationsUpdateSyncPolicyTest(t, applicationsSyncPolicy, 2, true) + + assert.Equal(t, "global.test: test", app.Spec.Source.Helm.Values) + assert.Equal(t, map[string]string{"annotation-key": "annotation-value"}, app.ObjectMeta.Annotations) + assert.Equal(t, map[string]string{"label-key": "label-value"}, app.ObjectMeta.Labels) +} + +func TestUpdatePerformedWithSyncPolicySync(t *testing.T) { + + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicySync + + app := applicationsUpdateSyncPolicyTest(t, applicationsSyncPolicy, 2, true) + + assert.Equal(t, "global.test: test", app.Spec.Source.Helm.Values) + assert.Equal(t, map[string]string{"annotation-key": "annotation-value"}, app.ObjectMeta.Annotations) + assert.Equal(t, map[string]string{"label-key": "label-value"}, app.ObjectMeta.Labels) +} + +func TestUpdatePerformedWithSyncPolicyCreateOnlyAndAllowPolicyOverrideFalse(t *testing.T) { + + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateOnly + + app := applicationsUpdateSyncPolicyTest(t, applicationsSyncPolicy, 2, false) + + assert.Equal(t, "global.test: test", app.Spec.Source.Helm.Values) + assert.Equal(t, map[string]string{"annotation-key": "annotation-value"}, app.ObjectMeta.Annotations) + assert.Equal(t, map[string]string{"label-key": "label-value"}, app.ObjectMeta.Labels) } -func TestReconcilerValidationErrorBehaviour(t *testing.T) { +func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alpha1.ApplicationsSyncPolicy, recordBuffer int, allowPolicyOverride bool) v1alpha1.ApplicationList { scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) + err := v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - err = argov1alpha1.AddToScheme(scheme) + err = v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - defaultProject := argov1alpha1.AppProject{ + defaultProject := v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"}, - Spec: argov1alpha1.AppProjectSpec{SourceRepos: []string{"*"}, Destinations: []argov1alpha1.ApplicationDestination{{Namespace: "*", Server: "https://good-cluster"}}}, + Spec: v1alpha1.AppProjectSpec{SourceRepos: []string{"*"}, Destinations: []v1alpha1.ApplicationDestination{{Namespace: "*", Server: "https://good-cluster"}}}, } - appSet := argov1alpha1.ApplicationSet{ + appSet := v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - GoTemplate: true, - Generators: []argov1alpha1.ApplicationSetGenerator{ + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ { - List: &argov1alpha1.ListGenerator{ + List: &v1alpha1.ListGenerator{ Elements: []apiextensionsv1.JSON{{ Raw: []byte(`{"cluster": "good-cluster","url": "https://good-cluster"}`), - }, { - Raw: []byte(`{"cluster": "bad-cluster","url": "https://bad-cluster"}`), }}, }, }, }, - Template: argov1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: argov1alpha1.ApplicationSetTemplateMeta{ - Name: "{{.cluster}}", + SyncPolicy: &v1alpha1.ApplicationSetSyncPolicy{ + ApplicationsSync: &applicationsSyncPolicy, + }, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ + Name: "{{cluster}}", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSpec{ - Source: &argov1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"}, + Spec: v1alpha1.ApplicationSpec{ + Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"}, Project: "default", - Destination: argov1alpha1.ApplicationDestination{Server: "{{.url}}"}, + Destination: v1alpha1.ApplicationDestination{Server: "{{url}}"}, }, }, }, @@ -1826,12 +2783,10 @@ func TestReconcilerValidationErrorBehaviour(t *testing.T) { argoDBMock := dbmocks.ArgoDB{} argoObjs := []runtime.Object{&defaultProject} - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build() - goodCluster := argov1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"} - badCluster := argov1alpha1.Cluster{Server: "https://bad-cluster", Name: "bad-cluster"} + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() + goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"} argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil) - argoDBMock.On("GetCluster", mock.Anything, "https://bad-cluster").Return(&badCluster, nil) - argoDBMock.On("ListClusters", mock.Anything).Return(&argov1alpha1.ClusterList{Items: []argov1alpha1.Cluster{ + argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{ goodCluster, }}, nil) @@ -1839,14 +2794,17 @@ func TestReconcilerValidationErrorBehaviour(t *testing.T) { Client: client, Scheme: scheme, Renderer: &utils.Render{}, - Recorder: record.NewFakeRecorder(1), + Recorder: record.NewFakeRecorder(recordBuffer), + Cache: &fakeCache{}, Generators: map[string]generators.Generator{ "List": generators.NewListGenerator(), }, - ArgoDB: &argoDBMock, - ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), - KubeClientset: kubeclientset, - Policy: &utils.SyncPolicy{}, + ArgoDB: &argoDBMock, + ArgoCDNamespace: "argocd", + ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), + KubeClientset: kubeclientset, + Policy: v1alpha1.ApplicationsSyncPolicySync, + EnablePolicyOverride: allowPolicyOverride, } req := ctrl.Request{ @@ -1857,76 +2815,87 @@ func TestReconcilerValidationErrorBehaviour(t *testing.T) { } // Verify that on validation error, no error is returned, but the object is requeued - res, err := r.Reconcile(context.Background(), req) + resCreate, err := r.Reconcile(context.Background(), req) assert.Nil(t, err) - assert.True(t, res.RequeueAfter == 0) + assert.True(t, resCreate.RequeueAfter == 0) - var app argov1alpha1.Application + var app v1alpha1.Application // make sure good app got created err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "good-cluster"}, &app) - assert.NoError(t, err) + assert.Nil(t, err) assert.Equal(t, app.Name, "good-cluster") - // make sure bad app was not created - err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "bad-cluster"}, &app) - assert.Error(t, err) -} - -func TestSetApplicationSetStatusCondition(t *testing.T) { - scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) - assert.Nil(t, err) - err = argov1alpha1.AddToScheme(scheme) + // Update resource + var retrievedApplicationSet v1alpha1.ApplicationSet + err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "name"}, &retrievedApplicationSet) assert.Nil(t, err) - - appSet := argov1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "argocd", - }, - Spec: argov1alpha1.ApplicationSetSpec{ - Generators: []argov1alpha1.ApplicationSetGenerator{ - {List: &argov1alpha1.ListGenerator{ - Elements: []apiextensionsv1.JSON{{ - Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), - }}, - }}, + retrievedApplicationSet.Spec.Generators = []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{}, }, - Template: argov1alpha1.ApplicationSetTemplate{}, }, } - appCondition := argov1alpha1.ApplicationSetCondition{ - Type: argov1alpha1.ApplicationSetConditionResourcesUpToDate, - Message: "All applications have been generated successfully", - Reason: argov1alpha1.ApplicationSetReasonApplicationSetUpToDate, - Status: argov1alpha1.ApplicationSetConditionStatusTrue, - } - - kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...) - argoDBMock := dbmocks.ArgoDB{} - argoObjs := []runtime.Object{} + err = r.Client.Update(context.TODO(), &retrievedApplicationSet) + assert.Nil(t, err) - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build() + resUpdate, err := r.Reconcile(context.Background(), req) + assert.Nil(t, err) - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Renderer: &utils.Render{}, - Recorder: record.NewFakeRecorder(1), - Generators: map[string]generators.Generator{ - "List": generators.NewListGenerator(), - }, - ArgoDB: &argoDBMock, - ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), - KubeClientset: kubeclientset, - } + var apps v1alpha1.ApplicationList - err = r.setApplicationSetStatusCondition(context.TODO(), &appSet, appCondition, true) + err = r.Client.List(context.TODO(), &apps) assert.Nil(t, err) + assert.True(t, resUpdate.RequeueAfter == 0) - assert.Len(t, appSet.Status.Conditions, 3) + return apps +} + +func TestDeleteNotPerformedWithSyncPolicyCreateOnly(t *testing.T) { + + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateOnly + + apps := applicationsDeleteSyncPolicyTest(t, applicationsSyncPolicy, 1, true) + + assert.Equal(t, "good-cluster", apps.Items[0].Name) +} + +func TestDeleteNotPerformedWithSyncPolicyCreateUpdate(t *testing.T) { + + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateUpdate + + apps := applicationsDeleteSyncPolicyTest(t, applicationsSyncPolicy, 2, true) + + assert.Equal(t, "good-cluster", apps.Items[0].Name) +} + +func TestDeletePerformedWithSyncPolicyCreateDelete(t *testing.T) { + + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateDelete + + apps := applicationsDeleteSyncPolicyTest(t, applicationsSyncPolicy, 3, true) + + assert.Equal(t, 0, len(apps.Items)) +} + +func TestDeletePerformedWithSyncPolicySync(t *testing.T) { + + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicySync + + apps := applicationsDeleteSyncPolicyTest(t, applicationsSyncPolicy, 3, true) + + assert.Equal(t, 0, len(apps.Items)) +} + +func TestDeletePerformedWithSyncPolicyCreateOnlyAndAllowPolicyOverrideFalse(t *testing.T) { + + applicationsSyncPolicy := v1alpha1.ApplicationsSyncPolicyCreateOnly + + apps := applicationsDeleteSyncPolicyTest(t, applicationsSyncPolicy, 3, false) + + assert.Equal(t, 0, len(apps.Items)) } // Test app generation from a go template application set using a pull request generator @@ -1937,42 +2906,52 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) { for _, cases := range []struct { name string params []map[string]interface{} - template argov1alpha1.ApplicationSetTemplate - expectedApp []argov1alpha1.Application + template v1alpha1.ApplicationSetTemplate + expectedApp []v1alpha1.Application }{ { name: "Generate an application from a go template application set manifest using a pull request generator", params: []map[string]interface{}{{ - "number": "1", - "branch": "branch1", - "branch_slug": "branchSlug1", - "head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958", - "head_short_sha": "089d92cb", - "labels": []string{"label1"}}}, - template: argov1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: argov1alpha1.ApplicationSetTemplateMeta{ + "number": "1", + "branch": "branch1", + "branch_slug": "branchSlug1", + "head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958", + "head_short_sha": "089d92cb", + "branch_slugify_default": "feat/a_really+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature", + "branch_slugify_smarttruncate_disabled": "feat/areallylongpullrequestnametotestargoslugificationandbranchnameshorteningfeature", + "branch_slugify_smarttruncate_enabled": "feat/testwithsmarttruncateenabledramdomlonglistofcharacters", + "labels": []string{"label1"}}, + }, + template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ Name: "AppSet-{{.branch}}-{{.number}}", Labels: map[string]string{ - "app1": "{{index .labels 0}}", + "app1": "{{index .labels 0}}", + "branch-test1": "AppSet-{{.branch_slugify_default | slugify }}", + "branch-test2": "AppSet-{{.branch_slugify_smarttruncate_disabled | slugify 49 false }}", + "branch-test3": "AppSet-{{.branch_slugify_smarttruncate_enabled | slugify 50 true }}", }, }, - Spec: argov1alpha1.ApplicationSpec{ - Source: &argov1alpha1.ApplicationSource{ + Spec: v1alpha1.ApplicationSpec{ + Source: &v1alpha1.ApplicationSource{ RepoURL: "https://testurl/testRepo", TargetRevision: "{{.head_short_sha}}", }, - Destination: argov1alpha1.ApplicationDestination{ + Destination: v1alpha1.ApplicationDestination{ Server: "https://kubernetes.default.svc", Namespace: "AppSet-{{.branch_slug}}-{{.head_sha}}", }, }, }, - expectedApp: []argov1alpha1.Application{ + expectedApp: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "AppSet-branch1-1", Labels: map[string]string{ - "app1": "label1", + "app1": "label1", + "branch-test1": "AppSet-feat-a-really-long-pull-request-name-to-test-argo", + "branch-test2": "AppSet-feat-areallylongpullrequestnametotestargoslugific", + "branch-test3": "AppSet-feat", }, }, Spec: v1alpha1.ApplicationSpec{ @@ -1993,8 +2972,8 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) { t.Run(cases.name, func(t *testing.T) { generatorMock := generatorMock{} - generator := argov1alpha1.ApplicationSetGenerator{ - PullRequest: &argov1alpha1.PullRequestGenerator{}, + generator := v1alpha1.ApplicationSetGenerator{ + PullRequest: &v1alpha1.PullRequestGenerator{}, } generatorMock.On("GenerateParams", &generator). @@ -2007,6 +2986,7 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) { Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, Generators: map[string]generators.Generator{ "PullRequest": &generatorMock, }, @@ -2014,11 +2994,11 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) { KubeClientset: kubefake.NewSimpleClientset(), } - gotApp, _, _ := appSetReconciler.generateApplications(argov1alpha1.ApplicationSet{ - Spec: argov1alpha1.ApplicationSetSpec{ + gotApp, _, _ := appSetReconciler.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{ + Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, - Generators: []argov1alpha1.ApplicationSetGenerator{{ - PullRequest: &argov1alpha1.PullRequestGenerator{}, + Generators: []v1alpha1.ApplicationSetGenerator{{ + PullRequest: &v1alpha1.PullRequestGenerator{}, }}, Template: cases.template, }, @@ -2034,17 +3014,17 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) { func TestPolicies(t *testing.T) { scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) + err := v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - err = argov1alpha1.AddToScheme(scheme) + err = v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - defaultProject := argov1alpha1.AppProject{ + defaultProject := v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"}, - Spec: argov1alpha1.AppProjectSpec{SourceRepos: []string{"*"}, Destinations: []argov1alpha1.ApplicationDestination{{Namespace: "*", Server: "https://kubernetes.default.svc"}}}, + Spec: v1alpha1.AppProjectSpec{SourceRepos: []string{"*"}, Destinations: []v1alpha1.ApplicationDestination{{Namespace: "*", Server: "https://kubernetes.default.svc"}}}, } - myCluster := argov1alpha1.Cluster{ + myCluster := v1alpha1.Cluster{ Server: "https://kubernetes.default.svc", Name: "my-cluster", } @@ -2089,16 +3069,16 @@ func TestPolicies(t *testing.T) { policy := utils.Policies[c.policyName] assert.NotNil(t, policy) - appSet := argov1alpha1.ApplicationSet{ + appSet := v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ + Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, - Generators: []argov1alpha1.ApplicationSetGenerator{ + Generators: []v1alpha1.ApplicationSetGenerator{ { - List: &argov1alpha1.ListGenerator{ + List: &v1alpha1.ListGenerator{ Elements: []apiextensionsv1.JSON{ { Raw: []byte(`{"name": "my-app"}`), @@ -2107,34 +3087,36 @@ func TestPolicies(t *testing.T) { }, }, }, - Template: argov1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: argov1alpha1.ApplicationSetTemplateMeta{ + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ Name: "{{.name}}", Namespace: "argocd", Annotations: map[string]string{ "key": "value", }, }, - Spec: argov1alpha1.ApplicationSpec{ - Source: &argov1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"}, + Spec: v1alpha1.ApplicationSpec{ + Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"}, Project: "default", - Destination: argov1alpha1.ApplicationDestination{Server: "https://kubernetes.default.svc"}, + Destination: v1alpha1.ApplicationDestination{Server: "https://kubernetes.default.svc"}, }, }, }, } - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build() + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build() r := ApplicationSetReconciler{ Client: client, Scheme: scheme, Renderer: &utils.Render{}, Recorder: record.NewFakeRecorder(10), + Cache: &fakeCache{}, Generators: map[string]generators.Generator{ "List": generators.NewListGenerator(), }, ArgoDB: &argoDBMock, + ArgoCDNamespace: "argocd", ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), KubeClientset: kubeclientset, Policy: policy, @@ -2152,7 +3134,7 @@ func TestPolicies(t *testing.T) { assert.Nil(t, err) assert.True(t, res.RequeueAfter == 0) - var app argov1alpha1.Application + var app v1alpha1.Application err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app) assert.NoError(t, err) assert.Equal(t, app.Annotations["key"], "value") @@ -2178,8 +3160,8 @@ func TestPolicies(t *testing.T) { // Check if Application is deleted err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "name"}, &appSet) assert.NoError(t, err) - appSet.Spec.Generators[0] = argov1alpha1.ApplicationSetGenerator{ - List: &argov1alpha1.ListGenerator{ + appSet.Spec.Generators[0] = v1alpha1.ApplicationSetGenerator{ + List: &v1alpha1.ListGenerator{ Elements: []apiextensionsv1.JSON{}, }, } @@ -2203,124 +3185,174 @@ func TestPolicies(t *testing.T) { func TestSetApplicationSetApplicationStatus(t *testing.T) { scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) + err := v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - err = argov1alpha1.AddToScheme(scheme) + err = v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - appSet := argov1alpha1.ApplicationSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "name", - Namespace: "argocd", - }, - Spec: argov1alpha1.ApplicationSetSpec{ - Generators: []argov1alpha1.ApplicationSetGenerator{ - {List: &argov1alpha1.ListGenerator{ - Elements: []apiextensionsv1.JSON{{ - Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), - }}, - }}, + kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...) + argoDBMock := dbmocks.ArgoDB{} + argoObjs := []runtime.Object{} + + for _, cc := range []struct { + name string + appSet v1alpha1.ApplicationSet + appStatuses []v1alpha1.ApplicationSetApplicationStatus + expectedAppStatuses []v1alpha1.ApplicationSetApplicationStatus + }{ + { + name: "sets a single appstatus", + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ + {List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), + }}, + }}, + }, + Template: v1alpha1.ApplicationSetTemplate{}, + }, + }, + appStatuses: []v1alpha1.ApplicationSetApplicationStatus{ + { + Application: "app1", + Message: "testing SetApplicationSetApplicationStatus to Healthy", + Status: "Healthy", + }, + }, + expectedAppStatuses: []v1alpha1.ApplicationSetApplicationStatus{ + { + Application: "app1", + Message: "testing SetApplicationSetApplicationStatus to Healthy", + Status: "Healthy", + }, }, - Template: argov1alpha1.ApplicationSetTemplate{}, }, - } - - appStatuses := []argov1alpha1.ApplicationSetApplicationStatus{ { - Application: "my-application", - LastTransitionTime: &metav1.Time{}, - Message: "testing SetApplicationSetApplicationStatus to Healthy", - Status: "Healthy", + name: "removes an appstatus", + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ + {List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), + }}, + }}, + }, + Template: v1alpha1.ApplicationSetTemplate{}, + }, + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ + { + Application: "app1", + Message: "testing SetApplicationSetApplicationStatus to Healthy", + Status: "Healthy", + }, + }, + }, + }, + appStatuses: []v1alpha1.ApplicationSetApplicationStatus{}, + expectedAppStatuses: nil, }, - } + } { - kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...) - argoDBMock := dbmocks.ArgoDB{} - argoObjs := []runtime.Object{} + t.Run(cc.name, func(t *testing.T) { - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build() + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).Build() - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Renderer: &utils.Render{}, - Recorder: record.NewFakeRecorder(1), - Generators: map[string]generators.Generator{ - "List": generators.NewListGenerator(), - }, - ArgoDB: &argoDBMock, - ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), - KubeClientset: kubeclientset, - } + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Renderer: &utils.Render{}, + Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, + Generators: map[string]generators.Generator{ + "List": generators.NewListGenerator(), + }, + ArgoDB: &argoDBMock, + ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), + KubeClientset: kubeclientset, + } - err = r.setAppSetApplicationStatus(context.TODO(), &appSet, appStatuses) - assert.Nil(t, err) + err = r.setAppSetApplicationStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.appStatuses) + assert.Nil(t, err) - assert.Len(t, appSet.Status.ApplicationStatus, 1) + assert.Equal(t, cc.expectedAppStatuses, cc.appSet.Status.ApplicationStatus) + }) + } } func TestBuildAppDependencyList(t *testing.T) { scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) + err := v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - err = argov1alpha1.AddToScheme(scheme) + err = v1alpha1.AddToScheme(scheme) assert.Nil(t, err) client := fake.NewClientBuilder().WithScheme(scheme).Build() for _, cc := range []struct { name string - appSet argov1alpha1.ApplicationSet - apps []argov1alpha1.Application + appSet v1alpha1.ApplicationSet + apps []v1alpha1.Application expectedList [][]string expectedStepMap map[string]int }{ { name: "handles an empty set of applications and no strategy", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{}, + Spec: v1alpha1.ApplicationSetSpec{}, }, - apps: []argov1alpha1.Application{}, + apps: []v1alpha1.Application{}, expectedList: [][]string{}, expectedStepMap: map[string]int{}, }, { name: "handles an empty set of applications and ignores AllAtOnce strategy", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "AllAtOnce", }, }, }, - apps: []argov1alpha1.Application{}, + apps: []v1alpha1.Application{}, expectedList: [][]string{}, expectedStepMap: map[string]int{}, }, { name: "handles an empty set of applications with good 'In' selectors", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ { Key: "env", Operator: "In", @@ -2335,7 +3367,7 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, }, - apps: []argov1alpha1.Application{}, + apps: []v1alpha1.Application{}, expectedList: [][]string{ {}, }, @@ -2343,18 +3375,18 @@ func TestBuildAppDependencyList(t *testing.T) { }, { name: "handles selecting 1 application with 1 'In' selector", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ { Key: "env", Operator: "In", @@ -2369,7 +3401,7 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app-dev", @@ -2388,18 +3420,18 @@ func TestBuildAppDependencyList(t *testing.T) { }, { name: "handles 'In' selectors that select no applications", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ { Key: "env", Operator: "In", @@ -2410,7 +3442,7 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ { Key: "env", Operator: "In", @@ -2421,7 +3453,7 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ { Key: "env", Operator: "In", @@ -2436,7 +3468,7 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app-qa", @@ -2466,18 +3498,18 @@ func TestBuildAppDependencyList(t *testing.T) { }, { name: "multiple 'In' selectors in the same matchExpression only select Applications that match all selectors", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ { Key: "region", Operator: "In", @@ -2499,7 +3531,7 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app-qa1", @@ -2527,18 +3559,18 @@ func TestBuildAppDependencyList(t *testing.T) { }, { name: "multiple values in the same 'In' matchExpression can match on any value", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ { Key: "env", Operator: "In", @@ -2554,7 +3586,7 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app-dev", @@ -2591,18 +3623,18 @@ func TestBuildAppDependencyList(t *testing.T) { }, { name: "handles an empty set of applications with good 'NotIn' selectors", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ { Key: "env", Operator: "In", @@ -2617,31 +3649,76 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, }, - apps: []argov1alpha1.Application{}, + apps: []v1alpha1.Application{}, expectedList: [][]string{ {}, }, - expectedStepMap: map[string]int{}, + expectedStepMap: map[string]int{}, + }, + { + name: "selects 1 application with 1 'NotIn' selector", + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ + Type: "RollingSync", + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ + { + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ + { + Key: "env", + Operator: "NotIn", + Values: []string{ + "qa", + }, + }, + }, + }, + }, + }, + }, + }, + }, + apps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app-dev", + Labels: map[string]string{ + "env": "dev", + }, + }, + }, + }, + expectedList: [][]string{ + {"app-dev"}, + }, + expectedStepMap: map[string]int{ + "app-dev": 0, + }, }, { - name: "selects 1 application with 1 'NotIn' selector", - appSet: argov1alpha1.ApplicationSet{ + name: "'NotIn' selectors that select no applications", + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ { Key: "env", Operator: "NotIn", Values: []string{ - "qa", + "dev", }, }, }, @@ -2651,42 +3728,58 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app-dev", + Name: "app-qa", Labels: map[string]string{ - "env": "dev", + "env": "qa", + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app-prod", + Labels: map[string]string{ + "env": "prod", }, }, }, }, expectedList: [][]string{ - {"app-dev"}, + {"app-qa", "app-prod"}, }, expectedStepMap: map[string]int{ - "app-dev": 0, + "app-qa": 0, + "app-prod": 0, }, }, { - name: "'NotIn' selectors that select no applications", - appSet: argov1alpha1.ApplicationSet{ + name: "multiple 'NotIn' selectors remove Applications with mising labels on any match", + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ + { + Key: "region", + Operator: "NotIn", + Values: []string{ + "us-east-2", + }, + }, { Key: "env", Operator: "NotIn", Values: []string{ - "dev", + "qa", }, }, }, @@ -2696,10 +3789,10 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ - Name: "app-qa", + Name: "app-qa1", Labels: map[string]string{ "env": "qa", }, @@ -2707,35 +3800,33 @@ func TestBuildAppDependencyList(t *testing.T) { }, { ObjectMeta: metav1.ObjectMeta{ - Name: "app-prod", + Name: "app-qa2", Labels: map[string]string{ - "env": "prod", + "env": "qa", + "region": "us-east-2", }, }, }, }, expectedList: [][]string{ - {"app-qa", "app-prod"}, - }, - expectedStepMap: map[string]int{ - "app-qa": 0, - "app-prod": 0, + {}, }, + expectedStepMap: map[string]int{}, }, { - name: "multiple 'NotIn' selectors only match Applications with all labels", - appSet: argov1alpha1.ApplicationSet{ + name: "multiple 'NotIn' selectors filter all matching Applications", + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ { Key: "region", Operator: "NotIn", @@ -2757,12 +3848,13 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app-qa1", Labels: map[string]string{ - "env": "qa", + "env": "qa", + "region": "us-east-1", }, }, }, @@ -2775,28 +3867,46 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app-prod1", + Labels: map[string]string{ + "env": "prod", + "region": "us-east-1", + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app-prod2", + Labels: map[string]string{ + "env": "prod", + "region": "us-east-2", + }, + }, + }, }, expectedList: [][]string{ - {"app-qa1"}, + {"app-prod1"}, }, expectedStepMap: map[string]int{ - "app-qa1": 0, + "app-prod1": 0, }, }, { name: "multiple values in the same 'NotIn' matchExpression exclude a match from any value", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ { Key: "env", Operator: "NotIn", @@ -2812,7 +3922,7 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app-dev", @@ -2848,18 +3958,18 @@ func TestBuildAppDependencyList(t *testing.T) { }, { name: "in a mix of 'In' and 'NotIn' selectors, 'NotIn' takes precedence", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{ + MatchExpressions: []v1alpha1.ApplicationMatchExpression{ { Key: "env", Operator: "In", @@ -2882,7 +3992,7 @@ func TestBuildAppDependencyList(t *testing.T) { }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app-dev", @@ -2929,13 +4039,14 @@ func TestBuildAppDependencyList(t *testing.T) { Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, Generators: map[string]generators.Generator{}, ArgoDB: &argoDBMock, ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), KubeClientset: kubeclientset, } - appDependencyList, appStepMap, err := r.buildAppDependencyList(context.TODO(), cc.appSet, cc.apps) + appDependencyList, appStepMap, err := r.buildAppDependencyList(log.NewEntry(log.StandardLogger()), cc.appSet, cc.apps) assert.Equal(t, err, nil, "expected no errors, but errors occured") assert.Equal(t, cc.expectedList, appDependencyList, "expected appDependencyList did not match actual") assert.Equal(t, cc.expectedStepMap, appStepMap, "expected appStepMap did not match actual") @@ -2946,32 +4057,32 @@ func TestBuildAppDependencyList(t *testing.T) { func TestBuildAppSyncMap(t *testing.T) { scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) + err := v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - err = argov1alpha1.AddToScheme(scheme) + err = v1alpha1.AddToScheme(scheme) assert.Nil(t, err) client := fake.NewClientBuilder().WithScheme(scheme).Build() for _, cc := range []struct { name string - appSet argov1alpha1.ApplicationSet - appMap map[string]argov1alpha1.Application + appSet v1alpha1.ApplicationSet + appMap map[string]v1alpha1.Application appDependencyList [][]string expectedMap map[string]bool }{ { name: "handles an empty app dependency list", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, }, @@ -2980,15 +4091,15 @@ func TestBuildAppSyncMap(t *testing.T) { }, { name: "handles two applications with no statuses", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, }, @@ -3003,15 +4114,15 @@ func TestBuildAppSyncMap(t *testing.T) { }, { name: "handles applications after an empty selection", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, }, @@ -3026,19 +4137,19 @@ func TestBuildAppSyncMap(t *testing.T) { }, { name: "handles RollingSync applications that are healthy and have no changes", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Status: "Healthy", @@ -3050,20 +4161,20 @@ func TestBuildAppSyncMap(t *testing.T) { }, }, }, - appMap: map[string]argov1alpha1.Application{ + appMap: map[string]v1alpha1.Application{ "app1": { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3071,15 +4182,15 @@ func TestBuildAppSyncMap(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app2", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3095,19 +4206,19 @@ func TestBuildAppSyncMap(t *testing.T) { }, { name: "blocks RollingSync applications that are healthy and have no changes, but are still pending", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Status: "Pending", @@ -3119,20 +4230,20 @@ func TestBuildAppSyncMap(t *testing.T) { }, }, }, - appMap: map[string]argov1alpha1.Application{ + appMap: map[string]v1alpha1.Application{ "app1": { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3140,15 +4251,15 @@ func TestBuildAppSyncMap(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app2", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3164,19 +4275,19 @@ func TestBuildAppSyncMap(t *testing.T) { }, { name: "handles RollingSync applications that are up to date and healthy, but still syncing", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Status: "Progressing", @@ -3188,20 +4299,20 @@ func TestBuildAppSyncMap(t *testing.T) { }, }, }, - appMap: map[string]argov1alpha1.Application{ + appMap: map[string]v1alpha1.Application{ "app1": { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationRunning, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3209,15 +4320,15 @@ func TestBuildAppSyncMap(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app2", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationRunning, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3233,19 +4344,19 @@ func TestBuildAppSyncMap(t *testing.T) { }, { name: "handles RollingSync applications that are up to date and synced, but degraded", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Status: "Progressing", @@ -3257,20 +4368,20 @@ func TestBuildAppSyncMap(t *testing.T) { }, }, }, - appMap: map[string]argov1alpha1.Application{ + appMap: map[string]v1alpha1.Application{ "app1": { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusDegraded, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationRunning, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3278,15 +4389,15 @@ func TestBuildAppSyncMap(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app2", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusDegraded, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationRunning, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3302,19 +4413,19 @@ func TestBuildAppSyncMap(t *testing.T) { }, { name: "handles RollingSync applications that are OutOfSync and healthy", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Status: "Healthy", @@ -3330,20 +4441,20 @@ func TestBuildAppSyncMap(t *testing.T) { {"app1"}, {"app2"}, }, - appMap: map[string]argov1alpha1.Application{ + appMap: map[string]v1alpha1.Application{ "app1": { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeOutOfSync, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, }, }, }, @@ -3351,15 +4462,15 @@ func TestBuildAppSyncMap(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app2", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeOutOfSync, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, }, }, }, @@ -3371,19 +4482,19 @@ func TestBuildAppSyncMap(t *testing.T) { }, { name: "handles a lot of applications", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Status: "Healthy", @@ -3411,20 +4522,20 @@ func TestBuildAppSyncMap(t *testing.T) { }, }, }, - appMap: map[string]argov1alpha1.Application{ + appMap: map[string]v1alpha1.Application{ "app1": { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3432,15 +4543,15 @@ func TestBuildAppSyncMap(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app2", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3448,15 +4559,15 @@ func TestBuildAppSyncMap(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app3", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3464,15 +4575,15 @@ func TestBuildAppSyncMap(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app5", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3480,15 +4591,15 @@ func TestBuildAppSyncMap(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app6", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusDegraded, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, @@ -3522,6 +4633,7 @@ func TestBuildAppSyncMap(t *testing.T) { Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, Generators: map[string]generators.Generator{}, ArgoDB: &argoDBMock, ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), @@ -3538,346 +4650,632 @@ func TestBuildAppSyncMap(t *testing.T) { func TestUpdateApplicationSetApplicationStatus(t *testing.T) { scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) + err := v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - err = argov1alpha1.AddToScheme(scheme) + err = v1alpha1.AddToScheme(scheme) assert.Nil(t, err) for _, cc := range []struct { name string - appSet argov1alpha1.ApplicationSet - apps []argov1alpha1.Application - expectedAppStatus []argov1alpha1.ApplicationSetApplicationStatus + appSet v1alpha1.ApplicationSet + apps []v1alpha1.Application + appStepMap map[string]int + expectedAppStatus []v1alpha1.ApplicationSetApplicationStatus }{ { name: "handles a nil list of statuses and no applications", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, }, - apps: []argov1alpha1.Application{}, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{}, + apps: []v1alpha1.Application{}, + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{}, }, { name: "handles a nil list of statuses with a healthy application", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", - Message: "No Application status found, defaulting status to Waiting.", - Status: "Waiting", + Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", + Status: "Healthy", + Step: "1", }, }, }, { name: "handles an empty list of statuses with a healthy application", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, - Status: argov1alpha1.ApplicationSetStatus{}, + Status: v1alpha1.ApplicationSetStatus{}, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", - Message: "No Application status found, defaulting status to Waiting.", - Status: "Waiting", + Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", + Status: "Healthy", + Step: "1", }, }, }, { name: "progresses an OutOfSync RollingSync application to waiting", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "", Status: "Healthy", + Step: "1", }, }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeOutOfSync, + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, }, }, }, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application has pending changes, setting status to Waiting.", Status: "Waiting", + Step: "1", }, }, }, { name: "progresses a pending progressing application to progressing", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "", Status: "Pending", + Step: "1", }, }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusProgressing, }, }, }, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application resource became Progressing, updating status from Pending to Progressing.", Status: "Progressing", + Step: "1", }, }, }, { name: "progresses a pending syncing application to progressing", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "", Status: "Pending", + Step: "1", }, }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationRunning, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application resource became Progressing, updating status from Pending to Progressing.", Status: "Progressing", + Step: "1", }, }, }, { name: "progresses a progressing application to healthy", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "", Status: "Progressing", + Step: "1", }, }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application resource became Healthy, updating status from Progressing to Healthy.", Status: "Healthy", + Step: "1", }, }, }, { name: "progresses a waiting healthy application to healthy", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{}, + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "", Status: "Waiting", + Step: "1", + }, + }, + }, + }, + apps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + }, + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ + Status: health.HealthStatusHealthy, + }, + OperationState: &v1alpha1.OperationState{ + Phase: common.OperationSucceeded, + }, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, + }, + }, + }, + }, + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ + { + Application: "app1", + Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", + Status: "Healthy", + Step: "1", + }, + }, + }, + { + name: "progresses a new outofsync application in a later step to waiting", + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ + Type: "RollingSync", + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, + }, + }, + }, + apps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + }, + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ + Status: health.HealthStatusHealthy, + }, + OperationState: &v1alpha1.OperationState{ + Phase: common.OperationSucceeded, + }, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, + }, + }, + }, + }, + appStepMap: map[string]int{ + "app1": 1, + "app2": 0, + }, + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ + { + Application: "app1", + Message: "No Application status found, defaulting status to Waiting.", + Status: "Waiting", + Step: "2", + }, + }, + }, + { + name: "progresses a pending application with a successful sync to progressing", + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ + Type: "RollingSync", + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, + }, + }, + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ + { + Application: "app1", + LastTransitionTime: &metav1.Time{ + Time: time.Now().Add(time.Duration(-1) * time.Minute), + }, + Message: "", + Status: "Pending", + Step: "1", + }, + }, + }, + }, + apps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + }, + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ + Status: health.HealthStatusDegraded, + }, + OperationState: &v1alpha1.OperationState{ + Phase: common.OperationSucceeded, + StartedAt: metav1.Time{ + Time: time.Now(), + }, + }, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, + }, + }, + }, + }, + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ + { + Application: "app1", + Message: "Application resource completed a sync successfully, updating status from Pending to Progressing.", + Status: "Progressing", + Step: "1", + }, + }, + }, + { + name: "progresses a pending application with a successful sync <1s ago to progressing", + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ + Type: "RollingSync", + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, + }, + }, + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ + { + Application: "app1", + LastTransitionTime: &metav1.Time{ + Time: time.Now(), + }, + Message: "", + Status: "Pending", + Step: "1", + }, + }, + }, + }, + apps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + }, + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ + Status: health.HealthStatusDegraded, + }, + OperationState: &v1alpha1.OperationState{ + Phase: common.OperationSucceeded, + StartedAt: metav1.Time{ + Time: time.Now().Add(time.Duration(-1) * time.Second), + }, + }, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, + }, + }, + }, + }, + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ + { + Application: "app1", + Message: "Application resource completed a sync successfully, updating status from Pending to Progressing.", + Status: "Progressing", + Step: "1", + }, + }, + }, + { + name: "does not progresses a pending application with an old successful sync to progressing", + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ + Type: "RollingSync", + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, + }, + }, + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ + { + Application: "app1", + LastTransitionTime: &metav1.Time{ + Time: time.Now(), + }, + Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", + Status: "Pending", + Step: "1", + }, + }, + }, + }, + apps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + }, + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ + Status: health.HealthStatusDegraded, + }, + OperationState: &v1alpha1.OperationState{ + Phase: common.OperationSucceeded, + StartedAt: metav1.Time{ + Time: time.Now().Add(time.Duration(-11) * time.Second), + }, + }, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, + }, + }, + }, + }, + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ + { + Application: "app1", + Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", + Status: "Pending", + Step: "1", + }, + }, + }, + { + name: "removes the appStatus for applications that no longer exist", + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ + Type: "RollingSync", + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{}, + }, + }, + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ + { + Application: "app1", + Message: "Application has pending changes, setting status to Waiting.", + Status: "Waiting", + Step: "1", + }, + { + Application: "app2", + Message: "Application has pending changes, setting status to Waiting.", + Status: "Waiting", + Step: "1", }, }, }, }, - apps: []argov1alpha1.Application{ + apps: []v1alpha1.Application{ { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Health: argov1alpha1.HealthStatus{ + Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ Status: health.HealthStatusHealthy, }, - OperationState: &argov1alpha1.OperationState{ + OperationState: &v1alpha1.OperationState{ Phase: common.OperationSucceeded, }, - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeSynced, + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, }, }, }, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application resource is already Healthy, updating status from Waiting to Healthy.", Status: "Healthy", + Step: "1", }, }, }, @@ -3895,13 +5293,14 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, Generators: map[string]generators.Generator{}, ArgoDB: &argoDBMock, ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), KubeClientset: kubeclientset, } - appStatuses, err := r.updateApplicationSetApplicationStatus(context.TODO(), &cc.appSet, cc.apps) + appStatuses, err := r.updateApplicationSetApplicationStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps, cc.appStepMap) // opt out of testing the LastTransitionTime is accurate for i := range appStatuses { @@ -3917,93 +5316,93 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) { func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { scheme := runtime.NewScheme() - err := argov1alpha1.AddToScheme(scheme) + err := v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - err = argov1alpha1.AddToScheme(scheme) + err = v1alpha1.AddToScheme(scheme) assert.Nil(t, err) for _, cc := range []struct { name string - appSet argov1alpha1.ApplicationSet + appSet v1alpha1.ApplicationSet appSyncMap map[string]bool appStepMap map[string]int - appMap map[string]argov1alpha1.Application - expectedAppStatus []argov1alpha1.ApplicationSetApplicationStatus + appMap map[string]v1alpha1.Application + expectedAppStatus []v1alpha1.ApplicationSetApplicationStatus }{ { name: "handles an empty appSync and appStepMap", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, }, }, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{}, + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{}, }, }, appSyncMap: map[string]bool{}, appStepMap: map[string]int{}, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{}, + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{}, }, { name: "handles an empty strategy", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{}, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{}, + Spec: v1alpha1.ApplicationSetSpec{}, + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{}, }, }, appSyncMap: map[string]bool{}, appStepMap: map[string]int{}, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{}, + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{}, }, { name: "handles an empty applicationset strategy", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{}, + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{}, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{}, + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{}, }, }, appSyncMap: map[string]bool{}, appStepMap: map[string]int{}, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{}, + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{}, }, { name: "handles an appSyncMap with no existing statuses", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{}, + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{}, }, }, appSyncMap: map[string]bool{ @@ -4014,32 +5413,32 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { "app1": 0, "app2": 1, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{}, + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{}, }, { name: "handles updating a RollingSync status from Waiting to Pending", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, }, }, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", @@ -4054,43 +5453,45 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { appStepMap: map[string]int{ "app1": 0, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", LastTransitionTime: nil, Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", Status: "Pending", + Step: "1", }, }, }, { name: "does not update a RollingSync status if appSyncMap is false", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, }, }, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, }, }, @@ -4101,43 +5502,45 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { appStepMap: map[string]int{ "app1": 0, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", LastTransitionTime: nil, Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, }, }, { name: "does not update a status if status is not pending", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, }, }, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application Pending status timed out while waiting to become Progressing, reset status to Healthy.", Status: "Healthy", + Step: "1", }, }, }, @@ -4148,62 +5551,67 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { appStepMap: map[string]int{ "app1": 0, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", LastTransitionTime: nil, Message: "Application Pending status timed out while waiting to become Progressing, reset status to Healthy.", Status: "Healthy", + Step: "1", }, }, }, { name: "does not update a status if maxUpdate has already been reached with RollingSync", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, MaxUpdate: &intstr.IntOrString{ Type: intstr.Int, IntVal: 3, }, }, { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, }, }, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application resource became Progressing, updating status from Pending to Progressing.", Status: "Progressing", + Step: "1", }, { Application: "app2", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app3", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app4", Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", Status: "Pending", + Step: "1", }, }, }, @@ -4220,14 +5628,14 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { "app3": 0, "app4": 0, }, - appMap: map[string]argov1alpha1.Application{ + appMap: map[string]v1alpha1.Application{ "app1": { ObjectMeta: metav1.ObjectMeta{ Name: "app1", }, - Status: argov1alpha1.ApplicationStatus{ - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeOutOfSync, + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, }, }, }, @@ -4235,9 +5643,9 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app2", }, - Status: argov1alpha1.ApplicationStatus{ - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeOutOfSync, + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, }, }, }, @@ -4245,9 +5653,9 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app3", }, - Status: argov1alpha1.ApplicationStatus{ - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeOutOfSync, + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, }, }, }, @@ -4255,82 +5663,89 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "app4", }, - Status: argov1alpha1.ApplicationStatus{ - Sync: argov1alpha1.SyncStatus{ - Status: argov1alpha1.SyncStatusCodeOutOfSync, + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, }, }, }, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", LastTransitionTime: nil, Message: "Application resource became Progressing, updating status from Pending to Progressing.", Status: "Progressing", + Step: "1", }, { Application: "app2", LastTransitionTime: nil, Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", Status: "Pending", + Step: "1", }, { Application: "app3", LastTransitionTime: nil, Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app4", LastTransitionTime: nil, Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", Status: "Pending", + Step: "1", }, }, }, { name: "rounds down for maxUpdate set to percentage string", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, MaxUpdate: &intstr.IntOrString{ Type: intstr.String, StrVal: "50%", }, }, { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, }, }, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app2", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app3", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, }, }, @@ -4345,69 +5760,75 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { "app2": 0, "app3": 0, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", LastTransitionTime: nil, Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", Status: "Pending", + Step: "1", }, { Application: "app2", LastTransitionTime: nil, Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app3", LastTransitionTime: nil, Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, }, }, { name: "does not update any applications with maxUpdate set to 0", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, MaxUpdate: &intstr.IntOrString{ Type: intstr.Int, IntVal: 0, }, }, { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, }, }, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app2", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app3", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, }, }, @@ -4422,69 +5843,75 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { "app2": 0, "app3": 0, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", LastTransitionTime: nil, Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app2", LastTransitionTime: nil, Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app3", LastTransitionTime: nil, Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, }, }, { name: "updates all applications with maxUpdate set to 100%", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, MaxUpdate: &intstr.IntOrString{ Type: intstr.String, StrVal: "100%", }, }, { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, }, }, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app2", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app3", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, }, }, @@ -4499,69 +5926,75 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { "app2": 0, "app3": 0, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", LastTransitionTime: nil, Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", Status: "Pending", + Step: "1", }, { Application: "app2", LastTransitionTime: nil, Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", Status: "Pending", + Step: "1", }, { Application: "app3", LastTransitionTime: nil, Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", Status: "Pending", + Step: "1", }, }, }, { name: "updates at least 1 application with maxUpdate >0%", - appSet: argov1alpha1.ApplicationSet{ + appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "argocd", }, - Spec: argov1alpha1.ApplicationSetSpec{ - Strategy: &argov1alpha1.ApplicationSetStrategy{ + Spec: v1alpha1.ApplicationSetSpec{ + Strategy: &v1alpha1.ApplicationSetStrategy{ Type: "RollingSync", - RollingSync: &argov1alpha1.ApplicationSetRolloutStrategy{ - Steps: []argov1alpha1.ApplicationSetRolloutStep{ + RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{ + Steps: []v1alpha1.ApplicationSetRolloutStep{ { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, MaxUpdate: &intstr.IntOrString{ Type: intstr.String, StrVal: "1%", }, }, { - MatchExpressions: []argov1alpha1.ApplicationMatchExpression{}, + MatchExpressions: []v1alpha1.ApplicationMatchExpression{}, }, }, }, }, }, - Status: argov1alpha1.ApplicationSetStatus{ - ApplicationStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + Status: v1alpha1.ApplicationSetStatus{ + ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app2", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app3", Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, }, }, @@ -4576,24 +6009,27 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { "app2": 0, "app3": 0, }, - expectedAppStatus: []argov1alpha1.ApplicationSetApplicationStatus{ + expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{ { Application: "app1", LastTransitionTime: nil, Message: "Application moved to Pending status, watching for the Application resource to start Progressing.", Status: "Pending", + Step: "1", }, { Application: "app2", LastTransitionTime: nil, Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, { Application: "app3", LastTransitionTime: nil, Message: "Application is out of date with the current AppSet generation, setting status to Waiting.", Status: "Waiting", + Step: "1", }, }, }, @@ -4611,13 +6047,14 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { Client: client, Scheme: scheme, Recorder: record.NewFakeRecorder(1), + Cache: &fakeCache{}, Generators: map[string]generators.Generator{}, ArgoDB: &argoDBMock, ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...), KubeClientset: kubeclientset, } - appStatuses, err := r.updateApplicationSetApplicationStatusProgress(context.TODO(), &cc.appSet, cc.appSyncMap, cc.appStepMap, cc.appMap) + appStatuses, err := r.updateApplicationSetApplicationStatusProgress(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.appSyncMap, cc.appStepMap, cc.appMap) // opt out of testing the LastTransitionTime is accurate for i := range appStatuses { @@ -4629,3 +6066,133 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) { }) } } + +func TestOwnsHandler(t *testing.T) { + // progressive syncs do not affect create, delete, or generic + ownsHandler := getOwnsHandlerPredicates(true) + assert.False(t, ownsHandler.CreateFunc(event.CreateEvent{})) + assert.True(t, ownsHandler.DeleteFunc(event.DeleteEvent{})) + assert.True(t, ownsHandler.GenericFunc(event.GenericEvent{})) + ownsHandler = getOwnsHandlerPredicates(false) + assert.False(t, ownsHandler.CreateFunc(event.CreateEvent{})) + assert.True(t, ownsHandler.DeleteFunc(event.DeleteEvent{})) + assert.True(t, ownsHandler.GenericFunc(event.GenericEvent{})) + + now := metav1.Now() + type args struct { + e event.UpdateEvent + enableProgressiveSyncs bool + } + tests := []struct { + name string + args args + want bool + }{ + {name: "SameApplicationReconciledAtDiff", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.Application{Status: v1alpha1.ApplicationStatus{ReconciledAt: &now}}, + ObjectNew: &v1alpha1.Application{Status: v1alpha1.ApplicationStatus{ReconciledAt: &now}}, + }}, want: false}, + {name: "SameApplicationResourceVersionDiff", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }}, + ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "bar", + }}, + }}, want: false}, + {name: "ApplicationHealthStatusDiff", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.Application{Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ + Status: "Unknown", + }, + }}, + ObjectNew: &v1alpha1.Application{Status: v1alpha1.ApplicationStatus{ + Health: v1alpha1.HealthStatus{ + Status: "Healthy", + }, + }}, + }, + enableProgressiveSyncs: true, + }, want: true}, + {name: "ApplicationSyncStatusDiff", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.Application{Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Status: "OutOfSync", + }, + }}, + ObjectNew: &v1alpha1.Application{Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Status: "Synced", + }, + }}, + }, + enableProgressiveSyncs: true, + }, want: true}, + {name: "ApplicationOperationStateDiff", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.Application{Status: v1alpha1.ApplicationStatus{ + OperationState: &v1alpha1.OperationState{ + Phase: "foo", + }, + }}, + ObjectNew: &v1alpha1.Application{Status: v1alpha1.ApplicationStatus{ + OperationState: &v1alpha1.OperationState{ + Phase: "bar", + }, + }}, + }, + enableProgressiveSyncs: true, + }, want: true}, + {name: "ApplicationOperationStartedAtDiff", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.Application{Status: v1alpha1.ApplicationStatus{ + OperationState: &v1alpha1.OperationState{ + StartedAt: now, + }, + }}, + ObjectNew: &v1alpha1.Application{Status: v1alpha1.ApplicationStatus{ + OperationState: &v1alpha1.OperationState{ + StartedAt: metav1.NewTime(now.Add(time.Minute * 1)), + }, + }}, + }, + enableProgressiveSyncs: true, + }, want: true}, + {name: "SameApplicationGeneration", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{ + Generation: 1, + }}, + ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{ + Generation: 2, + }}, + }}, want: false}, + {name: "DifferentApplicationSpec", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.Application{Spec: v1alpha1.ApplicationSpec{Project: "default"}}, + ObjectNew: &v1alpha1.Application{Spec: v1alpha1.ApplicationSpec{Project: "not-default"}}, + }}, want: true}, + {name: "DifferentApplicationLabels", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}}}, + ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"bar": "foo"}}}, + }}, want: true}, + {name: "DifferentApplicationAnnotations", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"foo": "bar"}}}, + ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"bar": "foo"}}}, + }}, want: true}, + {name: "DifferentApplicationFinalizers", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"argo"}}}, + ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"none"}}}, + }}, want: true}, + {name: "NotAnAppOld", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.AppProject{}, + ObjectNew: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"bar": "foo"}}}, + }}, want: false}, + {name: "NotAnAppNew", args: args{e: event.UpdateEvent{ + ObjectOld: &v1alpha1.Application{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}}}, + ObjectNew: &v1alpha1.AppProject{}, + }}, want: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ownsHandler = getOwnsHandlerPredicates(tt.args.enableProgressiveSyncs) + assert.Equalf(t, tt.want, ownsHandler.UpdateFunc(tt.args.e), "UpdateFunc(%v)", tt.args.e) + }) + } +} diff --git a/applicationset/controllers/clustereventhandler.go b/applicationset/controllers/clustereventhandler.go index eb487b7f20076..951da0cb6bc44 100644 --- a/applicationset/controllers/clustereventhandler.go +++ b/applicationset/controllers/clustereventhandler.go @@ -2,6 +2,7 @@ package controllers import ( "context" + "fmt" log "github.com/sirupsen/logrus" @@ -46,7 +47,6 @@ type addRateLimitingInterface interface { } func (h *clusterSecretEventHandler) queueRelatedAppGenerators(q addRateLimitingInterface, object client.Object) { - // Check for label, lookup all ApplicationSets that might match the cluster, queue them all if object.GetLabels()[generators.ArgoCDSecretTypeLabel] != generators.ArgoCDSecretTypeCluster { return @@ -73,6 +73,40 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(q addRateLimitingI foundClusterGenerator = true break } + + if generator.Matrix != nil { + ok, err := nestedGeneratorsHaveClusterGenerator(generator.Matrix.Generators) + if err != nil { + h.Log. + WithFields(log.Fields{ + "namespace": appSet.GetNamespace(), + "name": appSet.GetName(), + }). + WithError(err). + Error("Unable to check if ApplicationSet matrix generators have cluster generator") + } + if ok { + foundClusterGenerator = true + break + } + } + + if generator.Merge != nil { + ok, err := nestedGeneratorsHaveClusterGenerator(generator.Merge.Generators) + if err != nil { + h.Log. + WithFields(log.Fields{ + "namespace": appSet.GetNamespace(), + "name": appSet.GetName(), + }). + WithError(err). + Error("Unable to check if ApplicationSet merge generators have cluster generator") + } + if ok { + foundClusterGenerator = true + break + } + } } if foundClusterGenerator { @@ -82,3 +116,50 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(q addRateLimitingI } } } + +// nestedGeneratorsHaveClusterGenerator iterate over provided nested generators to check if they have a cluster generator. +func nestedGeneratorsHaveClusterGenerator(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator) (bool, error) { + for _, generator := range generators { + if ok, err := nestedGeneratorHasClusterGenerator(generator); ok || err != nil { + return ok, err + } + } + return false, nil +} + +// nestedGeneratorHasClusterGenerator checks if the provided generator has a cluster generator. +func nestedGeneratorHasClusterGenerator(nested argoprojiov1alpha1.ApplicationSetNestedGenerator) (bool, error) { + if nested.Clusters != nil { + return true, nil + } + + if nested.Matrix != nil { + nestedMatrix, err := argoprojiov1alpha1.ToNestedMatrixGenerator(nested.Matrix) + if err != nil { + return false, fmt.Errorf("unable to get nested matrix generator: %w", err) + } + if nestedMatrix != nil { + hasClusterGenerator, err := nestedGeneratorsHaveClusterGenerator(nestedMatrix.ToMatrixGenerator().Generators) + if err != nil { + return false, fmt.Errorf("error evaluating nested matrix generator: %w", err) + } + return hasClusterGenerator, nil + } + } + + if nested.Merge != nil { + nestedMerge, err := argoprojiov1alpha1.ToNestedMergeGenerator(nested.Merge) + if err != nil { + return false, fmt.Errorf("unable to get nested merge generator: %w", err) + } + if nestedMerge != nil { + hasClusterGenerator, err := nestedGeneratorsHaveClusterGenerator(nestedMerge.ToMergeGenerator().Generators) + if err != nil { + return false, fmt.Errorf("error evaluating nested merge generator: %w", err) + } + return hasClusterGenerator, nil + } + } + + return false, nil +} diff --git a/applicationset/controllers/clustereventhandler_test.go b/applicationset/controllers/clustereventhandler_test.go index 56fdd4b1974b8..7e850fc44c66d 100644 --- a/applicationset/controllers/clustereventhandler_test.go +++ b/applicationset/controllers/clustereventhandler_test.go @@ -6,6 +6,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -163,7 +164,6 @@ func TestClusterEventHandler(t *testing.T) { {NamespacedName: types.NamespacedName{Namespace: "another-namespace", Name: "my-app-set"}}, }, }, - { name: "non-argo cd secret should not match", items: []argov1alpha1.ApplicationSet{ @@ -189,6 +189,348 @@ func TestClusterEventHandler(t *testing.T) { }, expectedRequests: []reconcile.Request{}, }, + { + name: "a matrix generator with a cluster generator should produce a request", + items: []argov1alpha1.ApplicationSet{ + { + ObjectMeta: v1.ObjectMeta{ + Name: "my-app-set", + Namespace: "argocd", + }, + Spec: argov1alpha1.ApplicationSetSpec{ + Generators: []argov1alpha1.ApplicationSetGenerator{ + { + Matrix: &argov1alpha1.MatrixGenerator{ + Generators: []argov1alpha1.ApplicationSetNestedGenerator{ + { + Clusters: &argov1alpha1.ClusterGenerator{}, + }, + }, + }, + }, + }, + }, + }, + }, + secret: corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "argocd", + Name: "my-secret", + Labels: map[string]string{ + generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster, + }, + }, + }, + expectedRequests: []reconcile.Request{{ + NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"}, + }}, + }, + { + name: "a matrix generator with non cluster generator should not match", + items: []argov1alpha1.ApplicationSet{ + { + ObjectMeta: v1.ObjectMeta{ + Name: "my-app-set", + Namespace: "argocd", + }, + Spec: argov1alpha1.ApplicationSetSpec{ + Generators: []argov1alpha1.ApplicationSetGenerator{ + { + Matrix: &argov1alpha1.MatrixGenerator{ + Generators: []argov1alpha1.ApplicationSetNestedGenerator{ + { + List: &argov1alpha1.ListGenerator{}, + }, + }, + }, + }, + }, + }, + }, + }, + secret: corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "argocd", + Name: "my-secret", + Labels: map[string]string{ + generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster, + }, + }, + }, + expectedRequests: []reconcile.Request{}, + }, + { + name: "a matrix generator with a nested matrix generator containing a cluster generator should produce a request", + items: []argov1alpha1.ApplicationSet{ + { + ObjectMeta: v1.ObjectMeta{ + Name: "my-app-set", + Namespace: "argocd", + }, + Spec: argov1alpha1.ApplicationSetSpec{ + Generators: []argov1alpha1.ApplicationSetGenerator{ + { + Matrix: &argov1alpha1.MatrixGenerator{ + Generators: []argov1alpha1.ApplicationSetNestedGenerator{ + { + Matrix: &apiextensionsv1.JSON{ + Raw: []byte( + `{ + "generators": [ + { + "clusters": { + "selector": { + "matchLabels": { + "argocd.argoproj.io/secret-type": "cluster" + } + } + } + } + ] + }`, + ), + }, + }, + }, + }, + }, + }, + }, + }, + }, + secret: corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "argocd", + Name: "my-secret", + Labels: map[string]string{ + generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster, + }, + }, + }, + expectedRequests: []reconcile.Request{{ + NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"}, + }}, + }, + { + name: "a matrix generator with a nested matrix generator containing non cluster generator should not match", + items: []argov1alpha1.ApplicationSet{ + { + ObjectMeta: v1.ObjectMeta{ + Name: "my-app-set", + Namespace: "argocd", + }, + Spec: argov1alpha1.ApplicationSetSpec{ + Generators: []argov1alpha1.ApplicationSetGenerator{ + { + Matrix: &argov1alpha1.MatrixGenerator{ + Generators: []argov1alpha1.ApplicationSetNestedGenerator{ + { + Matrix: &apiextensionsv1.JSON{ + Raw: []byte( + `{ + "generators": [ + { + "list": { + "elements": [ + "a", + "b" + ] + } + } + ] + }`, + ), + }, + }, + }, + }, + }, + }, + }, + }, + }, + secret: corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "argocd", + Name: "my-secret", + Labels: map[string]string{ + generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster, + }, + }, + }, + expectedRequests: []reconcile.Request{}, + }, + { + name: "a merge generator with a cluster generator should produce a request", + items: []argov1alpha1.ApplicationSet{ + { + ObjectMeta: v1.ObjectMeta{ + Name: "my-app-set", + Namespace: "argocd", + }, + Spec: argov1alpha1.ApplicationSetSpec{ + Generators: []argov1alpha1.ApplicationSetGenerator{ + { + Merge: &argov1alpha1.MergeGenerator{ + Generators: []argov1alpha1.ApplicationSetNestedGenerator{ + { + Clusters: &argov1alpha1.ClusterGenerator{}, + }, + }, + }, + }, + }, + }, + }, + }, + secret: corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "argocd", + Name: "my-secret", + Labels: map[string]string{ + generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster, + }, + }, + }, + expectedRequests: []reconcile.Request{{ + NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"}, + }}, + }, + { + name: "a matrix generator with non cluster generator should not match", + items: []argov1alpha1.ApplicationSet{ + { + ObjectMeta: v1.ObjectMeta{ + Name: "my-app-set", + Namespace: "argocd", + }, + Spec: argov1alpha1.ApplicationSetSpec{ + Generators: []argov1alpha1.ApplicationSetGenerator{ + { + Merge: &argov1alpha1.MergeGenerator{ + Generators: []argov1alpha1.ApplicationSetNestedGenerator{ + { + List: &argov1alpha1.ListGenerator{}, + }, + }, + }, + }, + }, + }, + }, + }, + secret: corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "argocd", + Name: "my-secret", + Labels: map[string]string{ + generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster, + }, + }, + }, + expectedRequests: []reconcile.Request{}, + }, + { + name: "a merge generator with a nested merge generator containing a cluster generator should produce a request", + items: []argov1alpha1.ApplicationSet{ + { + ObjectMeta: v1.ObjectMeta{ + Name: "my-app-set", + Namespace: "argocd", + }, + Spec: argov1alpha1.ApplicationSetSpec{ + Generators: []argov1alpha1.ApplicationSetGenerator{ + { + Merge: &argov1alpha1.MergeGenerator{ + Generators: []argov1alpha1.ApplicationSetNestedGenerator{ + { + Merge: &apiextensionsv1.JSON{ + Raw: []byte( + `{ + "generators": [ + { + "clusters": { + "selector": { + "matchLabels": { + "argocd.argoproj.io/secret-type": "cluster" + } + } + } + } + ] + }`, + ), + }, + }, + }, + }, + }, + }, + }, + }, + }, + secret: corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "argocd", + Name: "my-secret", + Labels: map[string]string{ + generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster, + }, + }, + }, + expectedRequests: []reconcile.Request{{ + NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"}, + }}, + }, + { + name: "a merge generator with a nested merge generator containing non cluster generator should not match", + items: []argov1alpha1.ApplicationSet{ + { + ObjectMeta: v1.ObjectMeta{ + Name: "my-app-set", + Namespace: "argocd", + }, + Spec: argov1alpha1.ApplicationSetSpec{ + Generators: []argov1alpha1.ApplicationSetGenerator{ + { + Merge: &argov1alpha1.MergeGenerator{ + Generators: []argov1alpha1.ApplicationSetNestedGenerator{ + { + Merge: &apiextensionsv1.JSON{ + Raw: []byte( + `{ + "generators": [ + { + "list": { + "elements": [ + "a", + "b" + ] + } + } + ] + }`, + ), + }, + }, + }, + }, + }, + }, + }, + }, + }, + secret: corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "argocd", + Name: "my-secret", + Labels: map[string]string{ + generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster, + }, + }, + }, + expectedRequests: []reconcile.Request{}, + }, } for _, test := range tests { @@ -231,3 +573,68 @@ type mockAddRateLimitingInterface struct { errorOccurred bool addedItems []ctrl.Request } + +func TestNestedGeneratorHasClusterGenerator_NestedClusterGenerator(t *testing.T) { + nested := argov1alpha1.ApplicationSetNestedGenerator{ + Clusters: &argov1alpha1.ClusterGenerator{}, + } + + hasClusterGenerator, err := nestedGeneratorHasClusterGenerator(nested) + + assert.Nil(t, err) + assert.True(t, hasClusterGenerator) +} + +func TestNestedGeneratorHasClusterGenerator_NestedMergeGenerator(t *testing.T) { + nested := argov1alpha1.ApplicationSetNestedGenerator{ + Merge: &apiextensionsv1.JSON{ + Raw: []byte( + `{ + "generators": [ + { + "clusters": { + "selector": { + "matchLabels": { + "argocd.argoproj.io/secret-type": "cluster" + } + } + } + } + ] + }`, + ), + }, + } + + hasClusterGenerator, err := nestedGeneratorHasClusterGenerator(nested) + + assert.Nil(t, err) + assert.True(t, hasClusterGenerator) +} + +func TestNestedGeneratorHasClusterGenerator_NestedMergeGeneratorWithInvalidJSON(t *testing.T) { + nested := argov1alpha1.ApplicationSetNestedGenerator{ + Merge: &apiextensionsv1.JSON{ + Raw: []byte( + `{ + "generators": [ + { + "clusters": { + "selector": { + "matchLabels": { + "argocd.argoproj.io/secret-type": "cluster" + } + } + } + } + ] + `, + ), + }, + } + + hasClusterGenerator, err := nestedGeneratorHasClusterGenerator(nested) + + assert.NotNil(t, err) + assert.False(t, hasClusterGenerator) +} diff --git a/applicationset/controllers/requeue_after_test.go b/applicationset/controllers/requeue_after_test.go new file mode 100644 index 0000000000000..6db6145af5348 --- /dev/null +++ b/applicationset/controllers/requeue_after_test.go @@ -0,0 +1,153 @@ +package controllers + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + dynfake "k8s.io/client-go/dynamic/fake" + kubefake "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/argoproj/argo-cd/v2/applicationset/generators" + "github.com/argoproj/argo-cd/v2/applicationset/services/mocks" + argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +func TestRequeueAfter(t *testing.T) { + mockServer := &mocks.Repos{} + ctx := context.Background() + scheme := runtime.NewScheme() + err := argov1alpha1.AddToScheme(scheme) + assert.Nil(t, err) + gvrToListKind := map[schema.GroupVersionResource]string{{ + Group: "mallard.io", + Version: "v1", + Resource: "ducks", + }: "DuckList"} + appClientset := kubefake.NewSimpleClientset() + k8sClient := fake.NewClientBuilder().Build() + duckType := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v2quack", + "kind": "Duck", + "metadata": map[string]interface{}{ + "name": "mightyduck", + "namespace": "namespace", + "labels": map[string]interface{}{"duck": "all-species"}, + }, + "status": map[string]interface{}{ + "decisions": []interface{}{ + map[string]interface{}{ + "clusterName": "staging-01", + }, + map[string]interface{}{ + "clusterName": "production-01", + }, + }, + }, + }, + } + fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, duckType) + + terminalGenerators := map[string]generators.Generator{ + "List": generators.NewListGenerator(), + "Clusters": generators.NewClusterGenerator(k8sClient, ctx, appClientset, "argocd"), + "Git": generators.NewGitGenerator(mockServer), + "SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), generators.SCMAuthProviders{}, "", []string{""}, true), + "ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"), + "PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, "", []string{""}, true), + } + + nestedGenerators := map[string]generators.Generator{ + "List": terminalGenerators["List"], + "Clusters": terminalGenerators["Clusters"], + "Git": terminalGenerators["Git"], + "SCMProvider": terminalGenerators["SCMProvider"], + "ClusterDecisionResource": terminalGenerators["ClusterDecisionResource"], + "PullRequest": terminalGenerators["PullRequest"], + "Matrix": generators.NewMatrixGenerator(terminalGenerators), + "Merge": generators.NewMergeGenerator(terminalGenerators), + } + + topLevelGenerators := map[string]generators.Generator{ + "List": terminalGenerators["List"], + "Clusters": terminalGenerators["Clusters"], + "Git": terminalGenerators["Git"], + "SCMProvider": terminalGenerators["SCMProvider"], + "ClusterDecisionResource": terminalGenerators["ClusterDecisionResource"], + "PullRequest": terminalGenerators["PullRequest"], + "Matrix": generators.NewMatrixGenerator(nestedGenerators), + "Merge": generators.NewMergeGenerator(nestedGenerators), + } + + client := fake.NewClientBuilder().WithScheme(scheme).Build() + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(0), + Generators: topLevelGenerators, + } + + type args struct { + appset *argov1alpha1.ApplicationSet + } + tests := []struct { + name string + args args + want time.Duration + wantErr assert.ErrorAssertionFunc + }{ + {name: "Cluster", args: args{appset: &argov1alpha1.ApplicationSet{ + Spec: argov1alpha1.ApplicationSetSpec{ + Generators: []argov1alpha1.ApplicationSetGenerator{{Clusters: &argov1alpha1.ClusterGenerator{}}}, + }, + }}, want: generators.NoRequeueAfter, wantErr: assert.NoError}, + {name: "ClusterMergeNested", args: args{&argov1alpha1.ApplicationSet{ + Spec: argov1alpha1.ApplicationSetSpec{ + Generators: []argov1alpha1.ApplicationSetGenerator{ + {Clusters: &argov1alpha1.ClusterGenerator{}}, + {Merge: &argov1alpha1.MergeGenerator{ + Generators: []argov1alpha1.ApplicationSetNestedGenerator{ + { + Clusters: &argov1alpha1.ClusterGenerator{}, + Git: &argov1alpha1.GitGenerator{}, + }, + }, + }}, + }, + }, + }}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError}, + {name: "ClusterMatrixNested", args: args{&argov1alpha1.ApplicationSet{ + Spec: argov1alpha1.ApplicationSetSpec{ + Generators: []argov1alpha1.ApplicationSetGenerator{ + {Clusters: &argov1alpha1.ClusterGenerator{}}, + {Matrix: &argov1alpha1.MatrixGenerator{ + Generators: []argov1alpha1.ApplicationSetNestedGenerator{ + { + Clusters: &argov1alpha1.ClusterGenerator{}, + Git: &argov1alpha1.GitGenerator{}, + }, + }, + }}, + }, + }, + }}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError}, + {name: "ListGenerator", args: args{appset: &argov1alpha1.ApplicationSet{ + Spec: argov1alpha1.ApplicationSetSpec{ + Generators: []argov1alpha1.ApplicationSetGenerator{{List: &argov1alpha1.ListGenerator{}}}, + }, + }}, want: generators.NoRequeueAfter, wantErr: assert.NoError}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, r.getMinRequeueAfter(tt.args.appset), "getMinRequeueAfter(%v)", tt.args.appset) + }) + } +} diff --git a/applicationset/controllers/templatePatch.go b/applicationset/controllers/templatePatch.go new file mode 100644 index 0000000000000..f8efd9f376996 --- /dev/null +++ b/applicationset/controllers/templatePatch.go @@ -0,0 +1,46 @@ +package controllers + +import ( + "encoding/json" + "fmt" + + "k8s.io/apimachinery/pkg/util/strategicpatch" + + "github.com/argoproj/argo-cd/v2/applicationset/utils" + appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +func applyTemplatePatch(app *appv1.Application, templatePatch string) (*appv1.Application, error) { + + appString, err := json.Marshal(app) + if err != nil { + return nil, fmt.Errorf("error while marhsalling Application %w", err) + } + + convertedTemplatePatch, err := utils.ConvertYAMLToJSON(templatePatch) + + if err != nil { + return nil, fmt.Errorf("error while converting template to json %q: %w", convertedTemplatePatch, err) + } + + if err := json.Unmarshal([]byte(convertedTemplatePatch), &appv1.Application{}); err != nil { + return nil, fmt.Errorf("invalid templatePatch %q: %w", convertedTemplatePatch, err) + } + + data, err := strategicpatch.StrategicMergePatch(appString, []byte(convertedTemplatePatch), appv1.Application{}) + + if err != nil { + return nil, fmt.Errorf("error while applying templatePatch template to json %q: %w", convertedTemplatePatch, err) + } + + finalApp := appv1.Application{} + err = json.Unmarshal(data, &finalApp) + if err != nil { + return nil, fmt.Errorf("error while unmarhsalling patched application: %w", err) + } + + // Prevent changes to the `project` field. This helps prevent malicious template patches + finalApp.Spec.Project = app.Spec.Project + + return &finalApp, nil +} diff --git a/applicationset/controllers/templatePatch_test.go b/applicationset/controllers/templatePatch_test.go new file mode 100644 index 0000000000000..c1a794077c8ee --- /dev/null +++ b/applicationset/controllers/templatePatch_test.go @@ -0,0 +1,249 @@ +package controllers + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +func Test_ApplyTemplatePatch(t *testing.T) { + testCases := []struct { + name string + appTemplate *appv1.Application + templatePatch string + expectedApp *appv1.Application + }{ + { + name: "patch with JSON", + appTemplate: &appv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: "namespace", + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: appv1.ApplicationSpec{ + Project: "default", + Source: &appv1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: appv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + }, + templatePatch: `{ + "metadata": { + "annotations": { + "annotation-some-key": "annotation-some-value" + } + }, + "spec": { + "source": { + "helm": { + "valueFiles": [ + "values.test.yaml", + "values.big.yaml" + ] + } + }, + "syncPolicy": { + "automated": { + "prune": true + } + } + } + }`, + expectedApp: &appv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: "namespace", + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + Annotations: map[string]string{ + "annotation-some-key": "annotation-some-value", + }, + }, + Spec: appv1.ApplicationSpec{ + Project: "default", + Source: &appv1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + Helm: &appv1.ApplicationSourceHelm{ + ValueFiles: []string{ + "values.test.yaml", + "values.big.yaml", + }, + }, + }, + Destination: appv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + SyncPolicy: &appv1.SyncPolicy{ + Automated: &appv1.SyncPolicyAutomated{ + Prune: true, + }, + }, + }, + }, + }, + { + name: "patch with YAML", + appTemplate: &appv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: "namespace", + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: appv1.ApplicationSpec{ + Project: "default", + Source: &appv1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: appv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + }, + templatePatch: ` +metadata: + annotations: + annotation-some-key: annotation-some-value +spec: + source: + helm: + valueFiles: + - values.test.yaml + - values.big.yaml + syncPolicy: + automated: + prune: true`, + expectedApp: &appv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: "namespace", + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + Annotations: map[string]string{ + "annotation-some-key": "annotation-some-value", + }, + }, + Spec: appv1.ApplicationSpec{ + Project: "default", + Source: &appv1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + Helm: &appv1.ApplicationSourceHelm{ + ValueFiles: []string{ + "values.test.yaml", + "values.big.yaml", + }, + }, + }, + Destination: appv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + SyncPolicy: &appv1.SyncPolicy{ + Automated: &appv1.SyncPolicyAutomated{ + Prune: true, + }, + }, + }, + }, + }, + { + name: "project field isn't overwritten", + appTemplate: &appv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: "namespace", + }, + Spec: appv1.ApplicationSpec{ + Project: "default", + Source: &appv1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: appv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + }, + templatePatch: ` +spec: + project: my-project`, + expectedApp: &appv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: "namespace", + }, + Spec: appv1.ApplicationSpec{ + Project: "default", + Source: &appv1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: appv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + }, + }, + } + + for _, tc := range testCases { + tcc := tc + t.Run(tcc.name, func(t *testing.T) { + result, err := applyTemplatePatch(tcc.appTemplate, tcc.templatePatch) + require.NoError(t, err) + assert.Equal(t, *tcc.expectedApp, *result) + }) + } +} + +func TestError(t *testing.T) { + app := &appv1.Application{} + + result, err := applyTemplatePatch(app, "hello world") + require.Error(t, err) + require.Nil(t, result) +} diff --git a/applicationset/examples/applications-sync-policies/create-only.yaml b/applicationset/examples/applications-sync-policies/create-only.yaml new file mode 100644 index 0000000000000..7758a70b45765 --- /dev/null +++ b/applicationset/examples/applications-sync-policies/create-only.yaml @@ -0,0 +1,35 @@ +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: guestbook +spec: + goTemplate: true + generators: + - list: + elements: + - cluster: engineering-dev + url: https://kubernetes.default.svc + foo: bar + # Update foo value with foo: bar + # Application engineering-prod-guestbook labels will still be baz + # Delete this element + # Application engineering-prod-guestbook will be kept + - cluster: engineering-prod + url: https://kubernetes.default.svc + foo: baz + template: + metadata: + name: '{{.cluster}}-guestbook' + labels: + foo: '{{.foo}}' + spec: + project: default + source: + repoURL: https://github.com/argoproj/argo-cd.git + targetRevision: HEAD + path: applicationset/examples/list-generator/guestbook/{{.cluster}} + destination: + server: '{{.url}}' + namespace: guestbook + syncPolicy: + applicationsSync: create-only diff --git a/applicationset/examples/applications-sync-policies/create-update.yaml b/applicationset/examples/applications-sync-policies/create-update.yaml new file mode 100644 index 0000000000000..277e8d6e18884 --- /dev/null +++ b/applicationset/examples/applications-sync-policies/create-update.yaml @@ -0,0 +1,35 @@ +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: guestbook +spec: + goTemplate: true + generators: + - list: + elements: + - cluster: engineering-dev + url: https://kubernetes.default.svc + foo: bar + # Update foo value with foo: bar + # Application engineering-prod-guestbook labels will change to foo: bar + # Delete this element + # Application engineering-prod-guestbook will be kept + - cluster: engineering-prod + url: https://kubernetes.default.svc + foo: baz + template: + metadata: + name: '{{.cluster}}-guestbook' + labels: + foo: '{{.foo}}' + spec: + project: default + source: + repoURL: https://github.com/argoproj/argo-cd.git + targetRevision: HEAD + path: applicationset/examples/list-generator/guestbook/{{.cluster}} + destination: + server: '{{.url}}' + namespace: guestbook + syncPolicy: + applicationsSync: create-update diff --git a/applicationset/examples/applications-sync-policies/guestbook/engineering-dev/guestbook-ui-deployment.yaml b/applicationset/examples/applications-sync-policies/guestbook/engineering-dev/guestbook-ui-deployment.yaml new file mode 100644 index 0000000000000..8a0975e363539 --- /dev/null +++ b/applicationset/examples/applications-sync-policies/guestbook/engineering-dev/guestbook-ui-deployment.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: guestbook-ui +spec: + replicas: 1 + revisionHistoryLimit: 3 + selector: + matchLabels: + app: guestbook-ui + template: + metadata: + labels: + app: guestbook-ui + spec: + containers: + - image: gcr.io/heptio-images/ks-guestbook-demo:0.2 + name: guestbook-ui + ports: + - containerPort: 80 diff --git a/applicationset/examples/applications-sync-policies/guestbook/engineering-dev/guestbook-ui-svc.yaml b/applicationset/examples/applications-sync-policies/guestbook/engineering-dev/guestbook-ui-svc.yaml new file mode 100644 index 0000000000000..e8a4a27fbae40 --- /dev/null +++ b/applicationset/examples/applications-sync-policies/guestbook/engineering-dev/guestbook-ui-svc.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: guestbook-ui +spec: + ports: + - port: 80 + targetPort: 80 + selector: + app: guestbook-ui diff --git a/applicationset/examples/applications-sync-policies/guestbook/engineering-prod/guestbook-ui-deployment.yaml b/applicationset/examples/applications-sync-policies/guestbook/engineering-prod/guestbook-ui-deployment.yaml new file mode 100644 index 0000000000000..8a0975e363539 --- /dev/null +++ b/applicationset/examples/applications-sync-policies/guestbook/engineering-prod/guestbook-ui-deployment.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: guestbook-ui +spec: + replicas: 1 + revisionHistoryLimit: 3 + selector: + matchLabels: + app: guestbook-ui + template: + metadata: + labels: + app: guestbook-ui + spec: + containers: + - image: gcr.io/heptio-images/ks-guestbook-demo:0.2 + name: guestbook-ui + ports: + - containerPort: 80 diff --git a/applicationset/examples/applications-sync-policies/guestbook/engineering-prod/guestbook-ui-svc.yaml b/applicationset/examples/applications-sync-policies/guestbook/engineering-prod/guestbook-ui-svc.yaml new file mode 100644 index 0000000000000..e8a4a27fbae40 --- /dev/null +++ b/applicationset/examples/applications-sync-policies/guestbook/engineering-prod/guestbook-ui-svc.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: guestbook-ui +spec: + ports: + - port: 80 + targetPort: 80 + selector: + app: guestbook-ui diff --git a/applicationset/examples/cluster/cluster-example.yaml b/applicationset/examples/cluster/cluster-example.yaml index 9714ce1952e9c..a8e54212595e8 100644 --- a/applicationset/examples/cluster/cluster-example.yaml +++ b/applicationset/examples/cluster/cluster-example.yaml @@ -4,6 +4,7 @@ metadata: name: guestbook spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - clusters: {} template: diff --git a/applicationset/examples/clusterDecisionResource/ducktype-example.yaml b/applicationset/examples/clusterDecisionResource/ducktype-example.yaml index c6058e870bbf6..cf633483a8f68 100644 --- a/applicationset/examples/clusterDecisionResource/ducktype-example.yaml +++ b/applicationset/examples/clusterDecisionResource/ducktype-example.yaml @@ -4,6 +4,7 @@ metadata: name: book-import spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - clusterDecisionResource: configMapRef: ocm-placement diff --git a/applicationset/examples/design-doc/applicationset.yaml b/applicationset/examples/design-doc/applicationset.yaml index b1e49bd814d15..7ab4e824596a3 100644 --- a/applicationset/examples/design-doc/applicationset.yaml +++ b/applicationset/examples/design-doc/applicationset.yaml @@ -8,6 +8,7 @@ metadata: name: guestbook spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - clusters: {} template: diff --git a/applicationset/examples/design-doc/git-directory-discovery.yaml b/applicationset/examples/design-doc/git-directory-discovery.yaml index 2f62e33cd6ca6..a158d034d9043 100644 --- a/applicationset/examples/design-doc/git-directory-discovery.yaml +++ b/applicationset/examples/design-doc/git-directory-discovery.yaml @@ -27,6 +27,7 @@ metadata: name: cluster-addons spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - git: repoURL: https://github.com/infra-team/cluster-deployments.git diff --git a/applicationset/examples/design-doc/git-files-discovery.yaml b/applicationset/examples/design-doc/git-files-discovery.yaml index 3a4167886de69..367e318ac2d5a 100644 --- a/applicationset/examples/design-doc/git-files-discovery.yaml +++ b/applicationset/examples/design-doc/git-files-discovery.yaml @@ -38,6 +38,7 @@ metadata: name: guestbook spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - git: repoURL: https://github.com/infra-team/cluster-deployments.git diff --git a/applicationset/examples/design-doc/git-files-literal.yaml b/applicationset/examples/design-doc/git-files-literal.yaml index 5cb9bd9553446..9dbace36e4c56 100644 --- a/applicationset/examples/design-doc/git-files-literal.yaml +++ b/applicationset/examples/design-doc/git-files-literal.yaml @@ -51,6 +51,7 @@ metadata: name: guestbook spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - git: repoURL: https://github.com/infra-team/cluster-deployments.git diff --git a/applicationset/examples/design-doc/list.yaml b/applicationset/examples/design-doc/list.yaml index 3f76526b17df5..b1bcd593eac7f 100644 --- a/applicationset/examples/design-doc/list.yaml +++ b/applicationset/examples/design-doc/list.yaml @@ -5,6 +5,7 @@ metadata: name: guestbook spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - list: elements: diff --git a/applicationset/examples/design-doc/template-override.yaml b/applicationset/examples/design-doc/template-override.yaml index be55e739e15a2..970c7c395a820 100644 --- a/applicationset/examples/design-doc/template-override.yaml +++ b/applicationset/examples/design-doc/template-override.yaml @@ -8,6 +8,7 @@ metadata: name: guestbook spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - list: elements: diff --git a/applicationset/examples/git-generator-directory/excludes/git-directories-exclude-example.yaml b/applicationset/examples/git-generator-directory/excludes/git-directories-exclude-example.yaml index 786d30a536419..a021a3d0c66d3 100644 --- a/applicationset/examples/git-generator-directory/excludes/git-directories-exclude-example.yaml +++ b/applicationset/examples/git-generator-directory/excludes/git-directories-exclude-example.yaml @@ -5,6 +5,7 @@ metadata: namespace: argocd spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - git: repoURL: https://github.com/argoproj/argo-cd.git diff --git a/applicationset/examples/git-generator-directory/git-directories-example.yaml b/applicationset/examples/git-generator-directory/git-directories-example.yaml index 4ac79a34dd43c..6fc16b4d39384 100644 --- a/applicationset/examples/git-generator-directory/git-directories-example.yaml +++ b/applicationset/examples/git-generator-directory/git-directories-example.yaml @@ -5,6 +5,7 @@ metadata: namespace: argocd spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - git: repoURL: https://github.com/argoproj/argo-cd.git diff --git a/applicationset/examples/git-generator-files-discovery/git-generator-files.yaml b/applicationset/examples/git-generator-files-discovery/git-generator-files.yaml index 7ccd68f6c6b88..78a0136655498 100644 --- a/applicationset/examples/git-generator-files-discovery/git-generator-files.yaml +++ b/applicationset/examples/git-generator-files-discovery/git-generator-files.yaml @@ -4,6 +4,7 @@ metadata: name: guestbook spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - git: repoURL: https://github.com/argoproj/argo-cd.git diff --git a/applicationset/examples/list-generator/list-elementsYaml-example.yaml b/applicationset/examples/list-generator/list-elementsYaml-example.yaml new file mode 100644 index 0000000000000..f3aa3f34dd57d --- /dev/null +++ b/applicationset/examples/list-generator/list-elementsYaml-example.yaml @@ -0,0 +1,14 @@ +key: + components: + - name: component1 + chart: podinfo + version: "6.3.2" + releaseName: component1 + repoUrl: "https://stefanprodan.github.io/podinfo" + namespace: component1 + - name: component2 + chart: podinfo + version: "6.3.3" + releaseName: component2 + repoUrl: "ghcr.io/stefanprodan/charts" + namespace: component2 diff --git a/applicationset/examples/list-generator/list-example.yaml b/applicationset/examples/list-generator/list-example.yaml index a54fa0cfd92e1..03e33130bad84 100644 --- a/applicationset/examples/list-generator/list-example.yaml +++ b/applicationset/examples/list-generator/list-example.yaml @@ -4,6 +4,7 @@ metadata: name: guestbook spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - list: elements: diff --git a/applicationset/examples/matrix/cluster-and-git.yaml b/applicationset/examples/matrix/cluster-and-git.yaml index a42568db821f3..d58d2fa5f83f6 100644 --- a/applicationset/examples/matrix/cluster-and-git.yaml +++ b/applicationset/examples/matrix/cluster-and-git.yaml @@ -8,6 +8,7 @@ metadata: name: cluster-git spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - matrix: generators: diff --git a/applicationset/examples/matrix/list-and-git.yaml b/applicationset/examples/matrix/list-and-git.yaml index d1a2979daedfe..9ba04345476b4 100644 --- a/applicationset/examples/matrix/list-and-git.yaml +++ b/applicationset/examples/matrix/list-and-git.yaml @@ -8,6 +8,7 @@ metadata: name: list-git spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - matrix: generators: diff --git a/applicationset/examples/matrix/list-and-list.yaml b/applicationset/examples/matrix/list-and-list.yaml index fe5606a4b4b53..f88189ba5ec01 100644 --- a/applicationset/examples/matrix/list-and-list.yaml +++ b/applicationset/examples/matrix/list-and-list.yaml @@ -5,6 +5,7 @@ metadata: namespace: argocd spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - matrix: generators: diff --git a/applicationset/examples/matrix/matrix-and-union-in-matrix.yaml b/applicationset/examples/matrix/matrix-and-union-in-matrix.yaml index 783b4c94b5c3a..e4fed589764a8 100644 --- a/applicationset/examples/matrix/matrix-and-union-in-matrix.yaml +++ b/applicationset/examples/matrix/matrix-and-union-in-matrix.yaml @@ -13,6 +13,7 @@ metadata: name: matrix-and-union-in-matrix spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - matrix: generators: diff --git a/applicationset/examples/merge/merge-clusters-and-list.yaml b/applicationset/examples/merge/merge-clusters-and-list.yaml index 48b35b0251ed4..c91f4fea47d7b 100644 --- a/applicationset/examples/merge/merge-clusters-and-list.yaml +++ b/applicationset/examples/merge/merge-clusters-and-list.yaml @@ -4,6 +4,7 @@ metadata: name: merge-clusters-and-list spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - merge: mergeKeys: diff --git a/applicationset/examples/merge/merge-two-matrixes.yaml b/applicationset/examples/merge/merge-two-matrixes.yaml index f7590fb685d9f..f864ac6948b2d 100644 --- a/applicationset/examples/merge/merge-two-matrixes.yaml +++ b/applicationset/examples/merge/merge-two-matrixes.yaml @@ -4,6 +4,7 @@ metadata: name: merge-two-matrixes spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - merge: mergeKeys: diff --git a/applicationset/examples/pull-request-generator/pull-request-example.yaml b/applicationset/examples/pull-request-generator/pull-request-example.yaml index 98f66ae095e6d..d8ad8502b9b13 100644 --- a/applicationset/examples/pull-request-generator/pull-request-example.yaml +++ b/applicationset/examples/pull-request-generator/pull-request-example.yaml @@ -4,6 +4,7 @@ metadata: name: myapp spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - pullRequest: github: diff --git a/applicationset/examples/scm-provider-generator/scm-provider-example-fasttemplate-gitlab.yaml b/applicationset/examples/scm-provider-generator/scm-provider-example-fasttemplate-gitlab.yaml new file mode 100644 index 0000000000000..c62c151122d1f --- /dev/null +++ b/applicationset/examples/scm-provider-generator/scm-provider-example-fasttemplate-gitlab.yaml @@ -0,0 +1,26 @@ +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: guestbook +spec: + generators: + - scmProvider: + gitlab: + api: https://gitlab.com + group: test-argocd-proton + includeSubgroups: true + cloneProtocol: https + filters: + - repositoryMatch: test-app + template: + metadata: + name: '{{ repository }}-guestbook' + spec: + project: "default" + source: + repoURL: '{{ url }}' + targetRevision: '{{ branch }}' + path: guestbook + destination: + server: https://kubernetes.default.svc + namespace: guestbook diff --git a/applicationset/examples/scm-provider-generator/scm-provider-example.yaml b/applicationset/examples/scm-provider-generator/scm-provider-example.yaml index 8e310d45ccda5..c3ca2e5b3e5a9 100644 --- a/applicationset/examples/scm-provider-generator/scm-provider-example.yaml +++ b/applicationset/examples/scm-provider-generator/scm-provider-example.yaml @@ -4,6 +4,7 @@ metadata: name: guestbook spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - scmProvider: github: diff --git a/applicationset/examples/template-override/template-overrides-example.yaml b/applicationset/examples/template-override/template-overrides-example.yaml index dbc19418b4716..48cbf703fcd70 100644 --- a/applicationset/examples/template-override/template-overrides-example.yaml +++ b/applicationset/examples/template-override/template-overrides-example.yaml @@ -8,6 +8,7 @@ metadata: name: guestbook spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - list: elements: diff --git a/applicationset/generators/cluster.go b/applicationset/generators/cluster.go index d91ee3126279a..d8647d78d3a5c 100644 --- a/applicationset/generators/cluster.go +++ b/applicationset/generators/cluster.go @@ -51,6 +51,8 @@ func NewClusterGenerator(c client.Client, ctx context.Context, clientset kuberne return g } +// GetRequeueAfter never requeue the cluster generator because the `clusterSecretEventHandler` will requeue the appsets +// when the cluster secrets change func (g *ClusterGenerator) GetRequeueAfter(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) time.Duration { return NoRequeueAfter } @@ -59,8 +61,7 @@ func (g *ClusterGenerator) GetTemplate(appSetGenerator *argoappsetv1alpha1.Appli return &appSetGenerator.Clusters.Template } -func (g *ClusterGenerator) GenerateParams( - appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet) ([]map[string]interface{}, error) { +func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet) ([]map[string]interface{}, error) { if appSetGenerator == nil { return nil, EmptyAppSetGeneratorError @@ -77,7 +78,7 @@ func (g *ClusterGenerator) GenerateParams( // ListCluster from Argo CD's util/db package will include the local cluster in the list of clusters clustersFromArgoCD, err := utils.ListClusters(g.ctx, g.clientset, g.namespace) if err != nil { - return nil, err + return nil, fmt.Errorf("error listing clusters: %w", err) } if clustersFromArgoCD == nil { @@ -107,7 +108,7 @@ func (g *ClusterGenerator) GenerateParams( params["nameNormalized"] = cluster.Name params["server"] = cluster.Server - err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet) + err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) if err != nil { return nil, err } @@ -147,7 +148,7 @@ func (g *ClusterGenerator) GenerateParams( } } - err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet) + err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) if err != nil { return nil, err } @@ -160,44 +161,6 @@ func (g *ClusterGenerator) GenerateParams( return res, nil } -func appendTemplatedValues(clusterValues map[string]string, params map[string]interface{}, appSet *argoappsetv1alpha1.ApplicationSet) error { - // We create a local map to ensure that we do not fall victim to a billion-laughs attack. We iterate through the - // cluster values map and only replace values in said map if it has already been whitelisted in the params map. - // Once we iterate through all the cluster values we can then safely merge the `tmp` map into the main params map. - tmp := map[string]interface{}{} - - for key, value := range clusterValues { - result, err := replaceTemplatedString(value, params, appSet) - - if err != nil { - return fmt.Errorf("error replacing templated String: %w", err) - } - - if appSet.Spec.GoTemplate { - if tmp["values"] == nil { - tmp["values"] = map[string]string{} - } - tmp["values"].(map[string]string)[key] = result - } else { - tmp[fmt.Sprintf("values.%s", key)] = result - } - } - - for key, value := range tmp { - params[key] = value - } - - return nil -} - -func replaceTemplatedString(value string, params map[string]interface{}, appSet *argoappsetv1alpha1.ApplicationSet) (string, error) { - replacedTmplStr, err := render.Replace(value, params, appSet.Spec.GoTemplate) - if err != nil { - return "", err - } - return replacedTmplStr, nil -} - func (g *ClusterGenerator) getSecretsByClusterName(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) (map[string]corev1.Secret, error) { // List all Clusters: clusterSecretList := &corev1.SecretList{} diff --git a/applicationset/generators/duck_type.go b/applicationset/generators/duck_type.go index cdd13e8aeaf7a..f98afd0e01381 100644 --- a/applicationset/generators/duck_type.go +++ b/applicationset/generators/duck_type.go @@ -74,7 +74,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A // ListCluster from Argo CD's util/db package will include the local cluster in the list of clusters clustersFromArgoCD, err := utils.ListClusters(g.ctx, g.clientset, g.namespace) if err != nil { - return nil, err + return nil, fmt.Errorf("error listing clusters: %w", err) } if clustersFromArgoCD == nil { @@ -85,7 +85,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A cm, err := g.clientset.CoreV1().ConfigMaps(g.namespace).Get(g.ctx, appSetGenerator.ClusterDecisionResource.ConfigMapRef, metav1.GetOptions{}) if err != nil { - return nil, err + return nil, fmt.Errorf("error reading configMapRef: %w", err) } // Extract GVK data for the dynamic client to use diff --git a/applicationset/generators/duck_type_test.go b/applicationset/generators/duck_type_test.go index 21882e86575ed..788457b27559c 100644 --- a/applicationset/generators/duck_type_test.go +++ b/applicationset/generators/duck_type_test.go @@ -3,6 +3,7 @@ package generators import ( "context" "fmt" + "testing" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" @@ -15,8 +16,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - - "testing" ) const resourceApiVersion = "mallard.io/v1" diff --git a/applicationset/generators/generator_spec_processor.go b/applicationset/generators/generator_spec_processor.go index 4e08816e3e0c0..494b2e8d9a37d 100644 --- a/applicationset/generators/generator_spec_processor.go +++ b/applicationset/generators/generator_spec_processor.go @@ -2,12 +2,12 @@ package generators import ( "fmt" - "encoding/json" "reflect" + "github.com/jeremywohl/flatten" + "github.com/argoproj/argo-cd/v2/applicationset/utils" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -25,9 +25,12 @@ type TransformResult struct { Template argoprojiov1alpha1.ApplicationSetTemplate } -//Transform a spec generator to list of paramSets and a template +// Transform a spec generator to list of paramSets and a template func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, allGenerators map[string]Generator, baseTemplate argoprojiov1alpha1.ApplicationSetTemplate, appSet *argoprojiov1alpha1.ApplicationSet, genParams map[string]interface{}) ([]TransformResult, error) { - selector, err := metav1.LabelSelectorAsSelector(requestedGenerator.Selector) + // This is a custom version of the `LabelSelectorAsSelector` that is in k8s.io/apimachinery. This has been copied + // verbatim from that package, with the difference that we do not have any restrictions on label values. This is done + // so that, among other things, we can match on cluster urls. + selector, err := utils.LabelSelectorAsSelector(requestedGenerator.Selector) if err != nil { return nil, fmt.Errorf("error parsing label selector: %w", err) } @@ -50,7 +53,7 @@ func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, al } var params []map[string]interface{} if len(genParams) != 0 { - tempInterpolatedGenerator, err := InterpolateGenerator(&requestedGenerator, genParams, appSet.Spec.GoTemplate) + tempInterpolatedGenerator, err := InterpolateGenerator(&requestedGenerator, genParams, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) interpolatedGenerator = &tempInterpolatedGenerator if err != nil { log.WithError(err).WithField("genParams", genParams). @@ -72,8 +75,17 @@ func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, al } var filterParams []map[string]interface{} for _, param := range params { + flatParam, err := flattenParameters(param) + if err != nil { + log.WithError(err).WithField("generator", g). + Error("error flattening params") + if firstError == nil { + firstError = err + } + continue + } - if requestedGenerator.Selector != nil && !selector.Matches(labels.Set(keepOnlyStringValues(param))) { + if requestedGenerator.Selector != nil && !selector.Matches(labels.Set(flatParam)) { continue } filterParams = append(filterParams, param) @@ -88,18 +100,6 @@ func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, al return res, firstError } -func keepOnlyStringValues(in map[string]interface{}) map[string]string { - var out map[string]string = map[string]string{} - - for key, value := range in { - if _, ok := value.(string); ok { - out[key] = value.(string) - } - } - - return out -} - func GetRelevantGenerators(requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, generators map[string]Generator) []Generator { var res []Generator @@ -122,6 +122,20 @@ func GetRelevantGenerators(requestedGenerator *argoprojiov1alpha1.ApplicationSet return res } +func flattenParameters(in map[string]interface{}) (map[string]string, error) { + flat, err := flatten.Flatten(in, "", flatten.DotStyle) + if err != nil { + return nil, fmt.Errorf("error flatenning parameters: %w", err) + } + + out := make(map[string]string, len(flat)) + for k, v := range flat { + out[k] = fmt.Sprintf("%v", v) + } + + return out, nil +} + func mergeGeneratorTemplate(g Generator, requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetTemplate argoprojiov1alpha1.ApplicationSetTemplate) (argoprojiov1alpha1.ApplicationSetTemplate, error) { // Make a copy of the value from `GetTemplate()` before merge, rather than copying directly into // the provided parameter (which will touch the original resource object returned by client-go) @@ -132,27 +146,28 @@ func mergeGeneratorTemplate(g Generator, requestedGenerator *argoprojiov1alpha1. return *dest, err } -// Currently for Matrix Generator. Allows interpolating the matrix's 2nd child generator with values from the 1st child generator +// InterpolateGenerator allows interpolating the matrix's 2nd child generator with values from the 1st child generator // "params" parameter is an array, where each index corresponds to a generator. Each index contains a map w/ that generator's parameters. -func InterpolateGenerator(requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, params map[string]interface{}, useGoTemplate bool) (argoprojiov1alpha1.ApplicationSetGenerator, error) { - interpolatedGenerator := requestedGenerator.DeepCopy() - tmplBytes, err := json.Marshal(interpolatedGenerator) - if err != nil { - log.WithError(err).WithField("requestedGenerator", interpolatedGenerator).Error("error marshalling requested generator for interpolation") - return *interpolatedGenerator, err - } - +func InterpolateGenerator(requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (argoprojiov1alpha1.ApplicationSetGenerator, error) { render := utils.Render{} - replacedTmplStr, err := render.Replace(string(tmplBytes), params, useGoTemplate) + interpolatedGenerator, err := render.RenderGeneratorParams(requestedGenerator, params, useGoTemplate, goTemplateOptions) if err != nil { - log.WithError(err).WithField("interpolatedGeneratorString", replacedTmplStr).Error("error interpolating generator with other generator's parameter") - return *interpolatedGenerator, err + log.WithError(err).WithField("interpolatedGenerator", interpolatedGenerator).Error("error interpolating generator with other generator's parameter") + return argoprojiov1alpha1.ApplicationSetGenerator{}, err } - err = json.Unmarshal([]byte(replacedTmplStr), interpolatedGenerator) - if err != nil { - log.WithError(err).WithField("requestedGenerator", interpolatedGenerator).Error("error unmarshalling requested generator for interpolation") - return *interpolatedGenerator, err - } return *interpolatedGenerator, nil } + +// Fixes https://github.com/argoproj/argo-cd/issues/11982 while ensuring backwards compatibility. +// This is only a short-term solution and should be removed in a future major version. +func dropDisabledNestedSelectors(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator) bool { + var foundSelector bool + for i := range generators { + if generators[i].Selector != nil { + foundSelector = true + generators[i].Selector = nil + } + } + return foundSelector +} diff --git a/applicationset/generators/generator_spec_processor_test.go b/applicationset/generators/generator_spec_processor_test.go index 8dc125cac0d35..b5838e7af7cbe 100644 --- a/applicationset/generators/generator_spec_processor_test.go +++ b/applicationset/generators/generator_spec_processor_test.go @@ -6,9 +6,12 @@ import ( log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/argoproj/argo-cd/v2/applicationset/services/mocks" + argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/stretchr/testify/mock" @@ -17,8 +20,6 @@ import ( kubefake "k8s.io/client-go/kubernetes/fake" crtclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" - - argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) func TestMatchValues(t *testing.T) { @@ -69,16 +70,18 @@ func TestMatchValues(t *testing.T) { "List": listGenerator, } - applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ + applicationSetInfo := argov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "set", }, - Spec: argoprojiov1alpha1.ApplicationSetSpec{}, + Spec: argov1alpha1.ApplicationSetSpec{ + GoTemplate: false, + }, } - results, err := Transform(argoprojiov1alpha1.ApplicationSetGenerator{ + results, err := Transform(argov1alpha1.ApplicationSetGenerator{ Selector: testCase.selector, - List: &argoprojiov1alpha1.ListGenerator{ + List: &argov1alpha1.ListGenerator{ Elements: testCase.elements, Template: emptyTemplate(), }}, @@ -92,8 +95,160 @@ func TestMatchValues(t *testing.T) { } } -func emptyTemplate() argoprojiov1alpha1.ApplicationSetTemplate { - return argoprojiov1alpha1.ApplicationSetTemplate{ +func TestMatchValuesGoTemplate(t *testing.T) { + testCases := []struct { + name string + elements []apiextensionsv1.JSON + selector *metav1.LabelSelector + expected []map[string]interface{} + }{ + { + name: "no filter", + elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}}, + selector: &metav1.LabelSelector{}, + expected: []map[string]interface{}{{"cluster": "cluster", "url": "url"}}, + }, + { + name: "nil", + elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}}, + selector: nil, + expected: []map[string]interface{}{{"cluster": "cluster", "url": "url"}}, + }, + { + name: "values.foo should be foo but is ignore element", + elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}}, + selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "values.foo": "foo", + }, + }, + expected: []map[string]interface{}{}, + }, + { + name: "values.foo should be bar", + elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}}, + selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "values.foo": "bar", + }, + }, + expected: []map[string]interface{}{{"cluster": "cluster", "url": "url", "values": map[string]interface{}{"foo": "bar"}}}, + }, + { + name: "values.0 should be bar", + elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":["bar"]}`)}}, + selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "values.0": "bar", + }, + }, + expected: []map[string]interface{}{{"cluster": "cluster", "url": "url", "values": []interface{}{"bar"}}}, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var listGenerator = NewListGenerator() + var data = map[string]Generator{ + "List": listGenerator, + } + + applicationSetInfo := argov1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "set", + }, + Spec: argov1alpha1.ApplicationSetSpec{ + GoTemplate: true, + }, + } + + results, err := Transform(argov1alpha1.ApplicationSetGenerator{ + Selector: testCase.selector, + List: &argov1alpha1.ListGenerator{ + Elements: testCase.elements, + Template: emptyTemplate(), + }}, + data, + emptyTemplate(), + &applicationSetInfo, nil) + + assert.NoError(t, err) + assert.ElementsMatch(t, testCase.expected, results[0].Params) + }) + } +} + +func TestTransForm(t *testing.T) { + testCases := []struct { + name string + selector *metav1.LabelSelector + expected []map[string]interface{} + }{ + { + name: "server filter", + selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"server": "https://production-01.example.com"}, + }, + expected: []map[string]interface{}{{ + "metadata.annotations.foo.argoproj.io": "production", + "metadata.labels.argocd.argoproj.io/secret-type": "cluster", + "metadata.labels.environment": "production", + "metadata.labels.org": "bar", + "name": "production_01/west", + "nameNormalized": "production-01-west", + "server": "https://production-01.example.com", + }}, + }, + { + name: "server filter with long url", + selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"server": "https://some-really-long-url-that-will-exceed-63-characters.com"}, + }, + expected: []map[string]interface{}{{ + "metadata.annotations.foo.argoproj.io": "production", + "metadata.labels.argocd.argoproj.io/secret-type": "cluster", + "metadata.labels.environment": "production", + "metadata.labels.org": "bar", + "name": "some-really-long-server-url", + "nameNormalized": "some-really-long-server-url", + "server": "https://some-really-long-url-that-will-exceed-63-characters.com", + }}, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + testGenerators := map[string]Generator{ + "Clusters": getMockClusterGenerator(), + } + + applicationSetInfo := argov1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "set", + }, + Spec: argov1alpha1.ApplicationSetSpec{}, + } + + results, err := Transform( + argov1alpha1.ApplicationSetGenerator{ + Selector: testCase.selector, + Clusters: &argov1alpha1.ClusterGenerator{ + Selector: metav1.LabelSelector{}, + Template: argov1alpha1.ApplicationSetTemplate{}, + Values: nil, + }}, + testGenerators, + emptyTemplate(), + &applicationSetInfo, nil) + + assert.NoError(t, err) + assert.ElementsMatch(t, testCase.expected, results[0].Params) + }) + } +} + +func emptyTemplate() argov1alpha1.ApplicationSetTemplate { + return argov1alpha1.ApplicationSetTemplate{ Spec: argov1alpha1.ApplicationSpec{ Project: "project", }, @@ -150,8 +305,35 @@ func getMockClusterGenerator() Generator { }, Type: corev1.SecretType("Opaque"), }, + &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "some-really-long-server-url", + Namespace: "namespace", + Labels: map[string]string{ + "argocd.argoproj.io/secret-type": "cluster", + "environment": "production", + "org": "bar", + }, + Annotations: map[string]string{ + "foo.argoproj.io": "production", + }, + }, + Data: map[string][]byte{ + "config": []byte("{}"), + "name": []byte("some-really-long-server-url"), + "server": []byte("https://some-really-long-url-that-will-exceed-63-characters.com"), + }, + Type: corev1.SecretType("Opaque"), + }, } runtimeClusters := []runtime.Object{} + for _, clientCluster := range clusters { + runtimeClusters = append(runtimeClusters, clientCluster) + } appClientset := kubefake.NewSimpleClientset(runtimeClusters...) fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build() @@ -159,9 +341,9 @@ func getMockClusterGenerator() Generator { } func getMockGitGenerator() Generator { - argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}} - argoCDServiceMock.mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil) - var gitGenerator = NewGitGenerator(argoCDServiceMock) + argoCDServiceMock := mocks.Repos{} + argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil) + var gitGenerator = NewGitGenerator(&argoCDServiceMock) return gitGenerator } @@ -176,8 +358,8 @@ func TestGetRelevantGenerators(t *testing.T) { testGenerators["Merge"] = NewMergeGenerator(testGenerators) testGenerators["List"] = NewListGenerator() - requestedGenerator := &argoprojiov1alpha1.ApplicationSetGenerator{ - List: &argoprojiov1alpha1.ListGenerator{ + requestedGenerator := &argov1alpha1.ApplicationSetGenerator{ + List: &argov1alpha1.ListGenerator{ Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}}, }} @@ -185,10 +367,10 @@ func TestGetRelevantGenerators(t *testing.T) { assert.Len(t, relevantGenerators, 1) assert.IsType(t, &ListGenerator{}, relevantGenerators[0]) - requestedGenerator = &argoprojiov1alpha1.ApplicationSetGenerator{ - Clusters: &argoprojiov1alpha1.ClusterGenerator{ + requestedGenerator = &argov1alpha1.ApplicationSetGenerator{ + Clusters: &argov1alpha1.ClusterGenerator{ Selector: metav1.LabelSelector{}, - Template: argoprojiov1alpha1.ApplicationSetTemplate{}, + Template: argov1alpha1.ApplicationSetTemplate{}, Values: nil, }, } @@ -197,14 +379,14 @@ func TestGetRelevantGenerators(t *testing.T) { assert.Len(t, relevantGenerators, 1) assert.IsType(t, &ClusterGenerator{}, relevantGenerators[0]) - requestedGenerator = &argoprojiov1alpha1.ApplicationSetGenerator{ - Git: &argoprojiov1alpha1.GitGenerator{ + requestedGenerator = &argov1alpha1.ApplicationSetGenerator{ + Git: &argov1alpha1.GitGenerator{ RepoURL: "", Directories: nil, Files: nil, Revision: "", RequeueAfterSeconds: nil, - Template: argoprojiov1alpha1.ApplicationSetTemplate{}, + Template: argov1alpha1.ApplicationSetTemplate{}, }, } @@ -214,8 +396,8 @@ func TestGetRelevantGenerators(t *testing.T) { } func TestInterpolateGenerator(t *testing.T) { - requestedGenerator := &argoprojiov1alpha1.ApplicationSetGenerator{ - Clusters: &argoprojiov1alpha1.ClusterGenerator{ + requestedGenerator := &argov1alpha1.ApplicationSetGenerator{ + Clusters: &argov1alpha1.ClusterGenerator{ Selector: metav1.LabelSelector{ MatchLabels: map[string]string{ "argocd.argoproj.io/secret-type": "cluster", @@ -232,7 +414,7 @@ func TestInterpolateGenerator(t *testing.T) { "path[1]": "p2", "path.basenameNormalized": "app3", } - interpolatedGenerator, err := InterpolateGenerator(requestedGenerator, gitGeneratorParams, false) + interpolatedGenerator, err := InterpolateGenerator(requestedGenerator, gitGeneratorParams, false, nil) if err != nil { log.WithError(err).WithField("requestedGenerator", requestedGenerator).Error("error interpolating Generator") return @@ -241,23 +423,23 @@ func TestInterpolateGenerator(t *testing.T) { assert.Equal(t, "p1", interpolatedGenerator.Clusters.Selector.MatchLabels["path-zero"]) assert.Equal(t, "p1/p2/app3", interpolatedGenerator.Clusters.Selector.MatchLabels["path-full"]) - fileNamePath := argoprojiov1alpha1.GitFileGeneratorItem{ + fileNamePath := argov1alpha1.GitFileGeneratorItem{ Path: "{{name}}", } - fileServerPath := argoprojiov1alpha1.GitFileGeneratorItem{ + fileServerPath := argov1alpha1.GitFileGeneratorItem{ Path: "{{server}}", } - requestedGenerator = &argoprojiov1alpha1.ApplicationSetGenerator{ - Git: &argoprojiov1alpha1.GitGenerator{ - Files: append([]argoprojiov1alpha1.GitFileGeneratorItem{}, fileNamePath, fileServerPath), - Template: argoprojiov1alpha1.ApplicationSetTemplate{}, + requestedGenerator = &argov1alpha1.ApplicationSetGenerator{ + Git: &argov1alpha1.GitGenerator{ + Files: append([]argov1alpha1.GitFileGeneratorItem{}, fileNamePath, fileServerPath), + Template: argov1alpha1.ApplicationSetTemplate{}, }, } clusterGeneratorParams := map[string]interface{}{ "name": "production_01/west", "server": "https://production-01.example.com", } - interpolatedGenerator, err = InterpolateGenerator(requestedGenerator, clusterGeneratorParams, true) + interpolatedGenerator, err = InterpolateGenerator(requestedGenerator, clusterGeneratorParams, false, nil) if err != nil { log.WithError(err).WithField("requestedGenerator", requestedGenerator).Error("error interpolating Generator") return @@ -265,3 +447,114 @@ func TestInterpolateGenerator(t *testing.T) { assert.Equal(t, "production_01/west", interpolatedGenerator.Git.Files[0].Path) assert.Equal(t, "https://production-01.example.com", interpolatedGenerator.Git.Files[1].Path) } + +func TestInterpolateGenerator_go(t *testing.T) { + requestedGenerator := &argov1alpha1.ApplicationSetGenerator{ + Clusters: &argov1alpha1.ClusterGenerator{ + Selector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "argocd.argoproj.io/secret-type": "cluster", + "path-basename": "{{base .path.path}}", + "path-zero": "{{index .path.segments 0}}", + "path-full": "{{.path.path}}", + "kubernetes.io/environment": `{{default "foo" .my_label}}`, + }}, + }, + } + gitGeneratorParams := map[string]interface{}{ + "path": map[string]interface{}{ + "path": "p1/p2/app3", + "segments": []string{"p1", "p2", "app3"}, + }, + } + interpolatedGenerator, err := InterpolateGenerator(requestedGenerator, gitGeneratorParams, true, nil) + require.NoError(t, err) + if err != nil { + log.WithError(err).WithField("requestedGenerator", requestedGenerator).Error("error interpolating Generator") + return + } + assert.Equal(t, "app3", interpolatedGenerator.Clusters.Selector.MatchLabels["path-basename"]) + assert.Equal(t, "p1", interpolatedGenerator.Clusters.Selector.MatchLabels["path-zero"]) + assert.Equal(t, "p1/p2/app3", interpolatedGenerator.Clusters.Selector.MatchLabels["path-full"]) + + fileNamePath := argov1alpha1.GitFileGeneratorItem{ + Path: "{{.name}}", + } + fileServerPath := argov1alpha1.GitFileGeneratorItem{ + Path: "{{.server}}", + } + + requestedGenerator = &argov1alpha1.ApplicationSetGenerator{ + Git: &argov1alpha1.GitGenerator{ + Files: append([]argov1alpha1.GitFileGeneratorItem{}, fileNamePath, fileServerPath), + Template: argov1alpha1.ApplicationSetTemplate{}, + }, + } + clusterGeneratorParams := map[string]interface{}{ + "name": "production_01/west", "server": "https://production-01.example.com", + } + interpolatedGenerator, err = InterpolateGenerator(requestedGenerator, clusterGeneratorParams, true, nil) + if err != nil { + log.WithError(err).WithField("requestedGenerator", requestedGenerator).Error("error interpolating Generator") + return + } + assert.Equal(t, "production_01/west", interpolatedGenerator.Git.Files[0].Path) + assert.Equal(t, "https://production-01.example.com", interpolatedGenerator.Git.Files[1].Path) +} + +func TestInterpolateGeneratorError(t *testing.T) { + type args struct { + requestedGenerator *argov1alpha1.ApplicationSetGenerator + params map[string]interface{} + useGoTemplate bool + goTemplateOptions []string + } + tests := []struct { + name string + args args + want argov1alpha1.ApplicationSetGenerator + expectedErrStr string + }{ + {name: "Empty Gen", args: args{ + requestedGenerator: nil, + params: nil, + useGoTemplate: false, + goTemplateOptions: nil, + }, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: "generator is empty"}, + {name: "No Params", args: args{ + requestedGenerator: &argov1alpha1.ApplicationSetGenerator{}, + params: map[string]interface{}{}, + useGoTemplate: false, + goTemplateOptions: nil, + }, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: ""}, + {name: "Error templating", args: args{ + requestedGenerator: &argov1alpha1.ApplicationSetGenerator{Git: &argov1alpha1.GitGenerator{ + RepoURL: "foo", + Files: []argov1alpha1.GitFileGeneratorItem{{Path: "bar/"}}, + Revision: "main", + Values: map[string]string{ + "git_test": "{{ toPrettyJson . }}", + "selection": "{{ default .override .test }}", + "resolved": "{{ index .rmap (default .override .test) }}", + }, + }}, + params: map[string]interface{}{ + "name": "in-cluster", + "override": "foo", + }, + useGoTemplate: true, + goTemplateOptions: []string{}, + }, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: "failed to replace parameters in generator: failed to execute go template {{ index .rmap (default .override .test) }}: template: :1:3: executing \"\" at : error calling index: index of untyped nil"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := InterpolateGenerator(tt.args.requestedGenerator, tt.args.params, tt.args.useGoTemplate, tt.args.goTemplateOptions) + if tt.expectedErrStr != "" { + assert.EqualError(t, err, tt.expectedErrStr) + } else { + require.NoError(t, err) + } + assert.Equalf(t, tt.want, got, "InterpolateGenerator(%v, %v, %v, %v)", tt.args.requestedGenerator, tt.args.params, tt.args.useGoTemplate, tt.args.goTemplateOptions) + }) + } +} diff --git a/applicationset/generators/git.go b/applicationset/generators/git.go index 7fd6d75fd6912..57fe2835b8df0 100644 --- a/applicationset/generators/git.go +++ b/applicationset/generators/git.go @@ -56,51 +56,56 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic return nil, EmptyAppSetGeneratorError } + noRevisionCache := appSet.RefreshRequired() + var err error var res []map[string]interface{} - if appSetGenerator.Git.Directories != nil { - res, err = g.generateParamsForGitDirectories(appSetGenerator, appSet.Spec.GoTemplate) - } else if appSetGenerator.Git.Files != nil { - res, err = g.generateParamsForGitFiles(appSetGenerator, appSet.Spec.GoTemplate) + if len(appSetGenerator.Git.Directories) != 0 { + res, err = g.generateParamsForGitDirectories(appSetGenerator, noRevisionCache, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) + } else if len(appSetGenerator.Git.Files) != 0 { + res, err = g.generateParamsForGitFiles(appSetGenerator, noRevisionCache, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) } else { return nil, EmptyAppSetGeneratorError } if err != nil { - return nil, err + return nil, fmt.Errorf("error generating params from git: %w", err) } return res, nil } -func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool) ([]map[string]interface{}, error) { +func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache bool, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) { // Directories, not files - allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision) + allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, noRevisionCache) if err != nil { - return nil, err + return nil, fmt.Errorf("error getting directories from repo: %w", err) } log.WithFields(log.Fields{ - "allPaths": allPaths, - "total": len(allPaths), - "repoURL": appSetGenerator.Git.RepoURL, - "revision": appSetGenerator.Git.Revision, + "allPaths": allPaths, + "total": len(allPaths), + "repoURL": appSetGenerator.Git.RepoURL, + "revision": appSetGenerator.Git.Revision, "pathParamPrefix": appSetGenerator.Git.PathParamPrefix, }).Info("applications result from the repo service") requestedApps := g.filterApps(appSetGenerator.Git.Directories, allPaths) - res := g.generateParamsFromApps(requestedApps, appSetGenerator, useGoTemplate) + res, err := g.generateParamsFromApps(requestedApps, appSetGenerator, useGoTemplate, goTemplateOptions) + if err != nil { + return nil, fmt.Errorf("error generating params from apps: %w", err) + } return res, nil } -func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool) ([]map[string]interface{}, error) { +func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache bool, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) { // Get all files that match the requested path string, removing duplicates allFiles := make(map[string][]byte) for _, requestedPath := range appSetGenerator.Git.Files { - files, err := g.repos.GetFiles(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, requestedPath.Path) + files, err := g.repos.GetFiles(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, requestedPath.Path, noRevisionCache) if err != nil { return nil, err } @@ -122,19 +127,17 @@ func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1al for _, path := range allPaths { // A JSON / YAML file path can contain multiple sets of parameters (ie it is an array) - paramsArray, err := g.generateParamsFromGitFile(path, allFiles[path], useGoTemplate, appSetGenerator.Git.PathParamPrefix) + paramsArray, err := g.generateParamsFromGitFile(path, allFiles[path], appSetGenerator.Git.Values, useGoTemplate, goTemplateOptions, appSetGenerator.Git.PathParamPrefix) if err != nil { return nil, fmt.Errorf("unable to process file '%s': %v", path, err) } - for index := range paramsArray { - res = append(res, paramsArray[index]) - } + res = append(res, paramsArray...) } return res, nil } -func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []byte, useGoTemplate bool, pathParamPrefix string) ([]map[string]interface{}, error) { +func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []byte, values map[string]string, useGoTemplate bool, goTemplateOptions []string, pathParamPrefix string) ([]map[string]interface{}, error) { objectsFound := []map[string]interface{}{} // First, we attempt to parse as an array @@ -147,6 +150,9 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent [] return nil, fmt.Errorf("unable to parse file: %v", err) } objectsFound = append(objectsFound, singleObj) + } else if len(objectsFound) == 0 { + // If file is valid but empty, add a default empty item + objectsFound = append(objectsFound, map[string]interface{}{}) } res := []map[string]interface{}{} @@ -176,14 +182,14 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent [] } else { flat, err := flatten.Flatten(objectFound, "", flatten.DotStyle) if err != nil { - return nil, err + return nil, fmt.Errorf("error flattening object: %w", err) } for k, v := range flat { params[k] = fmt.Sprintf("%v", v) } pathParamName := "path" if pathParamPrefix != "" { - pathParamName = pathParamPrefix+"."+pathParamName + pathParamName = pathParamPrefix + "." + pathParamName } params[pathParamName] = path.Dir(filePath) params[pathParamName+".basename"] = path.Base(params[pathParamName].(string)) @@ -197,6 +203,11 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent [] } } + err := appendTemplatedValues(values, params, useGoTemplate, goTemplateOptions) + if err != nil { + return nil, fmt.Errorf("failed to append templated values: %w", err) + } + res = append(res, params) } @@ -231,7 +242,7 @@ func (g *GitGenerator) filterApps(Directories []argoprojiov1alpha1.GitDirectoryG return res } -func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool) []map[string]interface{} { +func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) { res := make([]map[string]interface{}, len(requestedApps)) for i, a := range requestedApps { @@ -251,7 +262,7 @@ func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGene } else { pathParamName := "path" if appSetGenerator.Git.PathParamPrefix != "" { - pathParamName = appSetGenerator.Git.PathParamPrefix+"."+pathParamName + pathParamName = appSetGenerator.Git.PathParamPrefix + "." + pathParamName } params[pathParamName] = a params[pathParamName+".basename"] = path.Base(a) @@ -263,8 +274,13 @@ func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGene } } + err := appendTemplatedValues(appSetGenerator.Git.Values, params, useGoTemplate, goTemplateOptions) + if err != nil { + return nil, fmt.Errorf("failed to append templated values: %w", err) + } + res[i] = params } - return res + return res, nil } diff --git a/applicationset/generators/git_test.go b/applicationset/generators/git_test.go index 96cfe08404d81..d3fd4965057f8 100644 --- a/applicationset/generators/git_test.go +++ b/applicationset/generators/git_test.go @@ -1,152 +1,176 @@ package generators import ( - "context" "fmt" "testing" + "github.com/argoproj/argo-cd/v2/applicationset/services/mocks" + argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) -// type clientSet struct { -// RepoServerServiceClient apiclient.RepoServerServiceClient -// } - -// func (c *clientSet) NewRepoServerClient() (io.Closer, apiclient.RepoServerServiceClient, error) { -// return io.NewCloser(func() error { return nil }), c.RepoServerServiceClient, nil -// } - -type argoCDServiceMock struct { - mock *mock.Mock -} - -func (a argoCDServiceMock) GetApps(ctx context.Context, repoURL string, revision string) ([]string, error) { - args := a.mock.Called(ctx, repoURL, revision) - - return args.Get(0).([]string), args.Error(1) -} - -func (a argoCDServiceMock) GetFiles(ctx context.Context, repoURL string, revision string, pattern string) (map[string][]byte, error) { - args := a.mock.Called(ctx, repoURL, revision, pattern) - - return args.Get(0).(map[string][]byte), args.Error(1) -} - -func (a argoCDServiceMock) GetFileContent(ctx context.Context, repoURL string, revision string, path string) ([]byte, error) { - args := a.mock.Called(ctx, repoURL, revision, path) - - return args.Get(0).([]byte), args.Error(1) -} - -func (a argoCDServiceMock) GetDirectories(ctx context.Context, repoURL string, revision string) ([]string, error) { - args := a.mock.Called(ctx, repoURL, revision) - return args.Get(0).([]string), args.Error(1) -} - func Test_generateParamsFromGitFile(t *testing.T) { - params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(` + defaultContent := []byte(` foo: bar: baz -`), false, "") - if err != nil { - t.Fatal(err) +`) + type args struct { + filePath string + fileContent []byte + values map[string]string + useGoTemplate bool + goTemplateOptions []string + pathParamPrefix string } - assert.Equal(t, []map[string]interface{}{ + tests := []struct { + name string + args args + want []map[string]interface{} + wantErr bool + }{ { - "foo.bar": "baz", - "path": "path/dir", - "path.basename": "dir", - "path.filename": "file_name.yaml", - "path.basenameNormalized": "dir", - "path.filenameNormalized": "file-name.yaml", - "path[0]": "path", - "path[1]": "dir", + name: "empty file returns path parameters", + args: args{ + filePath: "path/dir/file_name.yaml", + fileContent: []byte(""), + values: map[string]string{}, + useGoTemplate: false, + }, + want: []map[string]interface{}{ + { + "path": "path/dir", + "path.basename": "dir", + "path.filename": "file_name.yaml", + "path.basenameNormalized": "dir", + "path.filenameNormalized": "file-name.yaml", + "path[0]": "path", + "path[1]": "dir", + }, + }, }, - }, params) -} - -func Test_generatePrefixedParamsFromGitFile(t *testing.T) { - params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(` -foo: - bar: baz -`), false, "myRepo") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, []map[string]interface{}{ { - "foo.bar": "baz", - "myRepo.path": "path/dir", - "myRepo.path.basename": "dir", - "myRepo.path.filename": "file_name.yaml", - "myRepo.path.basenameNormalized": "dir", - "myRepo.path.filenameNormalized": "file-name.yaml", - "myRepo.path[0]": "path", - "myRepo.path[1]": "dir", + name: "invalid json/yaml file returns error", + args: args{ + filePath: "path/dir/file_name.yaml", + fileContent: []byte("this is not json or yaml"), + values: map[string]string{}, + useGoTemplate: false, + }, + wantErr: true, + }, + { + name: "file parameters are added to params", + args: args{ + filePath: "path/dir/file_name.yaml", + fileContent: defaultContent, + values: map[string]string{}, + useGoTemplate: false, + }, + want: []map[string]interface{}{ + { + "foo.bar": "baz", + "path": "path/dir", + "path.basename": "dir", + "path.filename": "file_name.yaml", + "path.basenameNormalized": "dir", + "path.filenameNormalized": "file-name.yaml", + "path[0]": "path", + "path[1]": "dir", + }, + }, }, - }, params) -} - -func Test_generateParamsFromGitFileGoTemplate(t *testing.T) { - params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(` -foo: - bar: baz -`), true, "") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, []map[string]interface{}{ { - "foo": map[string]interface{}{ - "bar": "baz", + name: "path parameter are prefixed", + args: args{ + filePath: "path/dir/file_name.yaml", + fileContent: defaultContent, + values: map[string]string{}, + useGoTemplate: false, + pathParamPrefix: "myRepo", }, - "path": map[string]interface{}{ - "path": "path/dir", - "basename": "dir", - "filename": "file_name.yaml", - "basenameNormalized": "dir", - "filenameNormalized": "file-name.yaml", - "segments": []string{ - "path", - "dir", + want: []map[string]interface{}{ + { + "foo.bar": "baz", + "myRepo.path": "path/dir", + "myRepo.path.basename": "dir", + "myRepo.path.filename": "file_name.yaml", + "myRepo.path.basenameNormalized": "dir", + "myRepo.path.filenameNormalized": "file-name.yaml", + "myRepo.path[0]": "path", + "myRepo.path[1]": "dir", }, }, }, - }, params) -} - -func Test_generatePrefixedParamsFromGitFileGoTemplate(t *testing.T) { - params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(` -foo: - bar: baz -`), true, "myRepo") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, []map[string]interface{}{ { - "foo": map[string]interface{}{ - "bar": "baz", + name: "file parameters are added to params with go template", + args: args{ + filePath: "path/dir/file_name.yaml", + fileContent: defaultContent, + values: map[string]string{}, + useGoTemplate: true, }, - "myRepo": map[string]interface{}{ - "path": map[string]interface{}{ - "path": "path/dir", - "basename": "dir", - "filename": "file_name.yaml", - "basenameNormalized": "dir", - "filenameNormalized": "file-name.yaml", - "segments": []string{ - "path", - "dir", + want: []map[string]interface{}{ + { + "foo": map[string]interface{}{ + "bar": "baz", + }, + "path": map[string]interface{}{ + "path": "path/dir", + "basename": "dir", + "filename": "file_name.yaml", + "basenameNormalized": "dir", + "filenameNormalized": "file-name.yaml", + "segments": []string{ + "path", + "dir", + }, }, }, }, }, - }, params) + { + name: "path parameter are prefixed with go template", + args: args{ + filePath: "path/dir/file_name.yaml", + fileContent: defaultContent, + values: map[string]string{}, + useGoTemplate: true, + pathParamPrefix: "myRepo", + }, + want: []map[string]interface{}{ + { + "foo": map[string]interface{}{ + "bar": "baz", + }, + "myRepo": map[string]interface{}{ + "path": map[string]interface{}{ + "path": "path/dir", + "basename": "dir", + "filename": "file_name.yaml", + "basenameNormalized": "dir", + "filenameNormalized": "file-name.yaml", + "segments": []string{ + "path", + "dir", + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + params, err := (*GitGenerator)(nil).generateParamsFromGitFile(tt.args.filePath, tt.args.fileContent, tt.args.values, tt.args.useGoTemplate, tt.args.goTemplateOptions, tt.args.pathParamPrefix) + if (err != nil) != tt.wantErr { + t.Errorf("GitGenerator.generateParamsFromGitFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equal(t, tt.want, params) + }) + } } func TestGitGenerateParamsFromDirectories(t *testing.T) { @@ -157,6 +181,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) { pathParamPrefix string repoApps []string repoError error + values map[string]string expected []map[string]interface{} expectedError error }{ @@ -247,6 +272,25 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) { }, expectedError: nil, }, + { + name: "Value variable interpolation", + directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}, {Path: "*/*"}}, + repoApps: []string{ + "app1", + "p1/app2", + }, + repoError: nil, + values: map[string]string{ + "foo": "bar", + "aaa": "{{ path[0] }}", + "no-op": "{{ this-does-not-exist }}", + }, + expected: []map[string]interface{}{ + {"values.foo": "bar", "values.no-op": "{{ this-does-not-exist }}", "values.aaa": "app1", "path": "app1", "path.basename": "app1", "path[0]": "app1", "path.basenameNormalized": "app1"}, + {"values.foo": "bar", "values.no-op": "{{ this-does-not-exist }}", "values.aaa": "p1", "path": "p1/app2", "path.basename": "app2", "path[0]": "p1", "path[1]": "app2", "path.basenameNormalized": "app2"}, + }, + expectedError: nil, + }, { name: "handles empty response from repo server", directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}}, @@ -261,7 +305,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) { repoApps: []string{}, repoError: fmt.Errorf("error"), expected: []map[string]interface{}{}, - expectedError: fmt.Errorf("error"), + expectedError: fmt.Errorf("error generating params from git: error getting directories from repo: error"), }, } @@ -271,11 +315,11 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) { t.Run(testCaseCopy.name, func(t *testing.T) { t.Parallel() - argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}} + argoCDServiceMock := mocks.Repos{} - argoCDServiceMock.mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError) + argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError) - var gitGenerator = NewGitGenerator(argoCDServiceMock) + var gitGenerator = NewGitGenerator(&argoCDServiceMock) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "set", @@ -287,6 +331,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) { Revision: "Revision", Directories: testCaseCopy.directories, PathParamPrefix: testCaseCopy.pathParamPrefix, + Values: testCaseCopy.values, }, }}, }, @@ -301,7 +346,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) { assert.Equal(t, testCaseCopy.expected, got) } - argoCDServiceMock.mock.AssertExpectations(t) + argoCDServiceMock.AssertExpectations(t) }) } } @@ -556,7 +601,7 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) { repoApps: []string{}, repoError: fmt.Errorf("error"), expected: []map[string]interface{}{}, - expectedError: fmt.Errorf("error"), + expectedError: fmt.Errorf("error generating params from git: error getting directories from repo: error"), }, } @@ -566,11 +611,11 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) { t.Run(testCaseCopy.name, func(t *testing.T) { t.Parallel() - argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}} + argoCDServiceMock := mocks.Repos{} - argoCDServiceMock.mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError) + argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError) - var gitGenerator = NewGitGenerator(argoCDServiceMock) + var gitGenerator = NewGitGenerator(&argoCDServiceMock) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "set", @@ -597,7 +642,7 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) { assert.Equal(t, testCaseCopy.expected, got) } - argoCDServiceMock.mock.AssertExpectations(t) + argoCDServiceMock.AssertExpectations(t) }) } @@ -613,6 +658,7 @@ func TestGitGenerateParamsFromFiles(t *testing.T) { repoFileContents map[string][]byte // if repoPathsError is non-nil, the call to GetPaths(...) will return this error value repoPathsError error + values map[string]string expected []map[string]interface{} expectedError error }{ @@ -676,13 +722,81 @@ func TestGitGenerateParamsFromFiles(t *testing.T) { }, expectedError: nil, }, + { + name: "Value variable interpolation", + files: []argoprojiov1alpha1.GitFileGeneratorItem{{Path: "**/config.json"}}, + repoFileContents: map[string][]byte{ + "cluster-config/production/config.json": []byte(`{ + "cluster": { + "owner": "john.doe@example.com", + "name": "production", + "address": "https://kubernetes.default.svc" + }, + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123 +}`), + "cluster-config/staging/config.json": []byte(`{ + "cluster": { + "owner": "foo.bar@example.com", + "name": "staging", + "address": "https://kubernetes.default.svc" + } +}`), + }, + repoPathsError: nil, + values: map[string]string{ + "aaa": "{{ cluster.owner }}", + "no-op": "{{ this-does-not-exist }}", + }, + expected: []map[string]interface{}{ + { + "cluster.owner": "john.doe@example.com", + "cluster.name": "production", + "cluster.address": "https://kubernetes.default.svc", + "key1": "val1", + "key2.key2_1": "val2_1", + "key2.key2_2.key2_2_1": "val2_2_1", + "key3": "123", + "path": "cluster-config/production", + "path.basename": "production", + "path[0]": "cluster-config", + "path[1]": "production", + "path.basenameNormalized": "production", + "path.filename": "config.json", + "path.filenameNormalized": "config.json", + "values.aaa": "john.doe@example.com", + "values.no-op": "{{ this-does-not-exist }}", + }, + { + "cluster.owner": "foo.bar@example.com", + "cluster.name": "staging", + "cluster.address": "https://kubernetes.default.svc", + "path": "cluster-config/staging", + "path.basename": "staging", + "path[0]": "cluster-config", + "path[1]": "staging", + "path.basenameNormalized": "staging", + "path.filename": "config.json", + "path.filenameNormalized": "config.json", + "values.aaa": "foo.bar@example.com", + "values.no-op": "{{ this-does-not-exist }}", + }, + }, + expectedError: nil, + }, { name: "handles error during getting repo paths", files: []argoprojiov1alpha1.GitFileGeneratorItem{{Path: "**/config.json"}}, repoFileContents: map[string][]byte{}, repoPathsError: fmt.Errorf("paths error"), expected: []map[string]interface{}{}, - expectedError: fmt.Errorf("paths error"), + expectedError: fmt.Errorf("error generating params from git: paths error"), }, { name: "test invalid JSON file returns error", @@ -692,7 +806,7 @@ func TestGitGenerateParamsFromFiles(t *testing.T) { }, repoPathsError: nil, expected: []map[string]interface{}{}, - expectedError: fmt.Errorf("unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"), + expectedError: fmt.Errorf("error generating params from git: unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"), }, { name: "test JSON array", @@ -857,11 +971,11 @@ cluster: t.Run(testCaseCopy.name, func(t *testing.T) { t.Parallel() - argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}} - argoCDServiceMock.mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + argoCDServiceMock := mocks.Repos{} + argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError) - var gitGenerator = NewGitGenerator(argoCDServiceMock) + var gitGenerator = NewGitGenerator(&argoCDServiceMock) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "set", @@ -872,6 +986,7 @@ cluster: RepoURL: "RepoURL", Revision: "Revision", Files: testCaseCopy.files, + Values: testCaseCopy.values, }, }}, }, @@ -887,7 +1002,7 @@ cluster: assert.ElementsMatch(t, testCaseCopy.expected, got) } - argoCDServiceMock.mock.AssertExpectations(t) + argoCDServiceMock.AssertExpectations(t) }) } } @@ -987,7 +1102,7 @@ func TestGitGenerateParamsFromFilesGoTemplate(t *testing.T) { repoFileContents: map[string][]byte{}, repoPathsError: fmt.Errorf("paths error"), expected: []map[string]interface{}{}, - expectedError: fmt.Errorf("paths error"), + expectedError: fmt.Errorf("error generating params from git: paths error"), }, { name: "test invalid JSON file returns error", @@ -997,7 +1112,7 @@ func TestGitGenerateParamsFromFilesGoTemplate(t *testing.T) { }, repoPathsError: nil, expected: []map[string]interface{}{}, - expectedError: fmt.Errorf("unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"), + expectedError: fmt.Errorf("error generating params from git: unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"), }, { name: "test JSON array", @@ -1206,11 +1321,11 @@ cluster: t.Run(testCaseCopy.name, func(t *testing.T) { t.Parallel() - argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}} - argoCDServiceMock.mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + argoCDServiceMock := mocks.Repos{} + argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError) - var gitGenerator = NewGitGenerator(argoCDServiceMock) + var gitGenerator = NewGitGenerator(&argoCDServiceMock) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "set", @@ -1237,7 +1352,7 @@ cluster: assert.ElementsMatch(t, testCaseCopy.expected, got) } - argoCDServiceMock.mock.AssertExpectations(t) + argoCDServiceMock.AssertExpectations(t) }) } } diff --git a/applicationset/generators/list.go b/applicationset/generators/list.go index cff4c67f161dd..b3afabe6dac7d 100644 --- a/applicationset/generators/list.go +++ b/applicationset/generators/list.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + "sigs.k8s.io/yaml" + argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -73,5 +75,16 @@ func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appli } } + // Append elements from ElementsYaml to the response + if len(appSetGenerator.List.ElementsYaml) > 0 { + + var yamlElements []map[string]interface{} + err := yaml.Unmarshal([]byte(appSetGenerator.List.ElementsYaml), &yamlElements) + if err != nil { + return nil, fmt.Errorf("error unmarshling decoded ElementsYaml %v", err) + } + res = append(res, yamlElements...) + } + return res, nil } diff --git a/applicationset/generators/matrix.go b/applicationset/generators/matrix.go index eadd18b83d5f9..3edac086a4b3c 100644 --- a/applicationset/generators/matrix.go +++ b/applicationset/generators/matrix.go @@ -8,6 +8,8 @@ import ( "github.com/argoproj/argo-cd/v2/applicationset/utils" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + + log "github.com/sirupsen/logrus" ) var _ Generator = (*MatrixGenerator)(nil) @@ -48,7 +50,7 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App g0, err := m.getParams(appSetGenerator.Matrix.Generators[0], appSet, nil) if err != nil { - return nil, err + return nil, fmt.Errorf("error failed to get params for first generator in matrix generator: %w", err) } for _, a := range g0 { g1, err := m.getParams(appSetGenerator.Matrix.Generators[1], appSet, a) @@ -59,11 +61,11 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App if appSet.Spec.GoTemplate { tmp := map[string]interface{}{} - if err := mergo.Merge(&tmp, a); err != nil { - return nil, fmt.Errorf("failed to merge params from the first generator in the matrix generator with temp map: %w", err) + if err := mergo.Merge(&tmp, b, mergo.WithOverride); err != nil { + return nil, fmt.Errorf("failed to merge params from the second generator in the matrix generator with temp map: %w", err) } - if err := mergo.Merge(&tmp, b); err != nil { - return nil, fmt.Errorf("failed to merge params from the first generator in the matrix generator with the second: %w", err) + if err := mergo.Merge(&tmp, a, mergo.WithOverride); err != nil { + return nil, fmt.Errorf("failed to merge params from the second generator in the matrix generator with the first: %w", err) } res = append(res, tmp) } else { @@ -80,27 +82,24 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App } func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, params map[string]interface{}) ([]map[string]interface{}, error) { - var matrix *argoprojiov1alpha1.MatrixGenerator - if appSetBaseGenerator.Matrix != nil { - // Since nested matrix generator is represented as a JSON object in the CRD, we unmarshall it back to a Go struct here. - nestedMatrix, err := argoprojiov1alpha1.ToNestedMatrixGenerator(appSetBaseGenerator.Matrix) - if err != nil { - return nil, fmt.Errorf("unable to unmarshall nested matrix generator: %v", err) - } - if nestedMatrix != nil { - matrix = nestedMatrix.ToMatrixGenerator() - } + matrixGen, err := getMatrixGenerator(appSetBaseGenerator) + if err != nil { + return nil, err } - - var mergeGenerator *argoprojiov1alpha1.MergeGenerator - if appSetBaseGenerator.Merge != nil { - // Since nested merge generator is represented as a JSON object in the CRD, we unmarshall it back to a Go struct here. - nestedMerge, err := argoprojiov1alpha1.ToNestedMergeGenerator(appSetBaseGenerator.Merge) - if err != nil { - return nil, fmt.Errorf("unable to unmarshall nested merge generator: %v", err) + if matrixGen != nil && !appSet.Spec.ApplyNestedSelectors { + foundSelector := dropDisabledNestedSelectors(matrixGen.Generators) + if foundSelector { + log.Warnf("AppSet '%v' defines selector on nested matrix generator's generator without enabling them via 'spec.applyNestedSelectors', ignoring nested selectors", appSet.Name) } - if nestedMerge != nil { - mergeGenerator = nestedMerge.ToMergeGenerator() + } + mergeGen, err := getMergeGenerator(appSetBaseGenerator) + if err != nil { + return nil, fmt.Errorf("error retrieving merge generator: %w", err) + } + if mergeGen != nil && !appSet.Spec.ApplyNestedSelectors { + foundSelector := dropDisabledNestedSelectors(mergeGen.Generators) + if foundSelector { + log.Warnf("AppSet '%v' defines selector on nested merge generator's generator without enabling them via 'spec.applyNestedSelectors', ignoring nested selectors", appSet.Name) } } @@ -112,8 +111,9 @@ func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.Appli SCMProvider: appSetBaseGenerator.SCMProvider, ClusterDecisionResource: appSetBaseGenerator.ClusterDecisionResource, PullRequest: appSetBaseGenerator.PullRequest, - Matrix: matrix, - Merge: mergeGenerator, + Plugin: appSetBaseGenerator.Plugin, + Matrix: matrixGen, + Merge: mergeGen, Selector: appSetBaseGenerator.Selector, }, m.supportedGenerators, @@ -143,11 +143,18 @@ func (m *MatrixGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Ap var found bool for _, r := range appSetGenerator.Matrix.Generators { + matrixGen, _ := getMatrixGenerator(r) + mergeGen, _ := getMergeGenerator(r) base := &argoprojiov1alpha1.ApplicationSetGenerator{ - List: r.List, - Clusters: r.Clusters, - Git: r.Git, - PullRequest: r.PullRequest, + List: r.List, + Clusters: r.Clusters, + Git: r.Git, + PullRequest: r.PullRequest, + Plugin: r.Plugin, + SCMProvider: r.SCMProvider, + ClusterDecisionResource: r.ClusterDecisionResource, + Matrix: matrixGen, + Merge: mergeGen, } generators := GetRelevantGenerators(base, m.supportedGenerators) @@ -168,6 +175,17 @@ func (m *MatrixGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Ap } +func getMatrixGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MatrixGenerator, error) { + if r.Matrix == nil { + return nil, nil + } + matrix, err := argoprojiov1alpha1.ToNestedMatrixGenerator(r.Matrix) + if err != nil { + return nil, err + } + return matrix.ToMatrixGenerator(), nil +} + func (m *MatrixGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate { return &appSetGenerator.Matrix.Template } diff --git a/applicationset/generators/matrix_test.go b/applicationset/generators/matrix_test.go index 1d79c452f6dce..21e88710ae618 100644 --- a/applicationset/generators/matrix_test.go +++ b/applicationset/generators/matrix_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -12,6 +13,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "github.com/argoproj/argo-cd/v2/applicationset/services/mocks" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -28,7 +31,7 @@ func TestMatrixGenerate(t *testing.T) { } listGenerator := &argoprojiov1alpha1.ListGenerator{ - Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url"}`)}}, + Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url", "templated": "test-{{path.basenameNormalized}}"}`)}}, } testCases := []struct { @@ -48,8 +51,8 @@ func TestMatrixGenerate(t *testing.T) { }, }, expected: []map[string]interface{}{ - {"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "cluster": "Cluster", "url": "Url"}, - {"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2", "cluster": "Cluster", "url": "Url"}, + {"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "cluster": "Cluster", "url": "Url", "templated": "test-app1"}, + {"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2", "cluster": "Cluster", "url": "Url", "templated": "test-app2"}, }, }, { @@ -268,6 +271,28 @@ func TestMatrixGenerateGoTemplate(t *testing.T) { {"a": "2", "b": "2"}, }, }, + { + name: "parameter override: first list elements take precedence", + baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{ + { + List: &argoprojiov1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + {Raw: []byte(`{"booleanFalse": false, "booleanTrue": true, "stringFalse": "false", "stringTrue": "true"}`)}, + }, + }, + }, + { + List: &argoprojiov1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + {Raw: []byte(`{"booleanFalse": true, "booleanTrue": false, "stringFalse": "true", "stringTrue": "false"}`)}, + }, + }, + }, + }, + expected: []map[string]interface{}{ + {"booleanFalse": false, "booleanTrue": true, "stringFalse": "false", "stringTrue": "true"}, + }, + }, { name: "returns error if there is less than two base generators", baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{ @@ -401,6 +426,10 @@ func TestMatrixGetRequeueAfter(t *testing.T) { pullRequestGenerator := &argoprojiov1alpha1.PullRequestGenerator{} + scmGenerator := &argoprojiov1alpha1.SCMProviderGenerator{} + + duckTypeGenerator := &argoprojiov1alpha1.DuckTypeGenerator{} + testCases := []struct { name string baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator @@ -458,6 +487,30 @@ func TestMatrixGetRequeueAfter(t *testing.T) { }, expected: time.Duration(30 * time.Minute), }, + { + name: "returns the default time for duck type generator", + baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{ + { + Git: gitGenerator, + }, + { + ClusterDecisionResource: duckTypeGenerator, + }, + }, + expected: time.Duration(3 * time.Minute), + }, + { + name: "returns the default time for scm generator", + baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{ + { + Git: gitGenerator, + }, + { + SCMProvider: scmGenerator, + }, + }, + expected: time.Duration(30 * time.Minute), + }, } for _, testCase := range testCases { @@ -468,18 +521,22 @@ func TestMatrixGetRequeueAfter(t *testing.T) { for _, g := range testCaseCopy.baseGenerators { gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{ - Git: g.Git, - List: g.List, - PullRequest: g.PullRequest, + Git: g.Git, + List: g.List, + PullRequest: g.PullRequest, + SCMProvider: g.SCMProvider, + ClusterDecisionResource: g.ClusterDecisionResource, } mock.On("GetRequeueAfter", &gitGeneratorSpec).Return(testCaseCopy.gitGetRequeueAfter, nil) } var matrixGenerator = NewMatrixGenerator( map[string]Generator{ - "Git": mock, - "List": &ListGenerator{}, - "PullRequest": &PullRequestGenerator{}, + "Git": mock, + "List": &ListGenerator{}, + "PullRequest": &PullRequestGenerator{}, + "SCMProvider": &SCMProviderGenerator{}, + "ClusterDecisionResource": &DuckTypeGenerator{}, }, ) @@ -835,6 +892,172 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) { } } +func TestMatrixGenerateListElementsYaml(t *testing.T) { + + gitGenerator := &argoprojiov1alpha1.GitGenerator{ + RepoURL: "RepoURL", + Revision: "Revision", + Files: []argoprojiov1alpha1.GitFileGeneratorItem{ + {Path: "config.yaml"}, + }, + } + + listGenerator := &argoprojiov1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{}, + ElementsYaml: "{{ .foo.bar | toJson }}", + } + + testCases := []struct { + name string + baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator + expectedErr error + expected []map[string]interface{} + }{ + { + name: "happy flow - generate params", + baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{ + { + Git: gitGenerator, + }, + { + List: listGenerator, + }, + }, + expected: []map[string]interface{}{ + { + "chart": "a", + "version": "1", + "foo": map[string]interface{}{ + "bar": []interface{}{ + map[string]interface{}{ + "chart": "a", + "version": "1", + }, + map[string]interface{}{ + "chart": "b", + "version": "2", + }, + }, + }, + "path": map[string]interface{}{ + "basename": "dir", + "basenameNormalized": "dir", + "filename": "file_name.yaml", + "filenameNormalized": "file-name.yaml", + "path": "path/dir", + "segments": []string{ + "path", + "dir", + }, + }, + }, + { + "chart": "b", + "version": "2", + "foo": map[string]interface{}{ + "bar": []interface{}{ + map[string]interface{}{ + "chart": "a", + "version": "1", + }, + map[string]interface{}{ + "chart": "b", + "version": "2", + }, + }, + }, + "path": map[string]interface{}{ + "basename": "dir", + "basenameNormalized": "dir", + "filename": "file_name.yaml", + "filenameNormalized": "file-name.yaml", + "path": "path/dir", + "segments": []string{ + "path", + "dir", + }, + }, + }, + }, + }, + } + + for _, testCase := range testCases { + testCaseCopy := testCase // Since tests may run in parallel + + t.Run(testCaseCopy.name, func(t *testing.T) { + genMock := &generatorMock{} + appSet := &argoprojiov1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "set", + }, + Spec: argoprojiov1alpha1.ApplicationSetSpec{ + GoTemplate: true, + }, + } + + for _, g := range testCaseCopy.baseGenerators { + + gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{ + Git: g.Git, + List: g.List, + } + genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]any{{ + "foo": map[string]interface{}{ + "bar": []interface{}{ + map[string]interface{}{ + "chart": "a", + "version": "1", + }, + map[string]interface{}{ + "chart": "b", + "version": "2", + }, + }, + }, + "path": map[string]interface{}{ + "basename": "dir", + "basenameNormalized": "dir", + "filename": "file_name.yaml", + "filenameNormalized": "file-name.yaml", + "path": "path/dir", + "segments": []string{ + "path", + "dir", + }, + }, + }}, nil) + genMock.On("GetTemplate", &gitGeneratorSpec). + Return(&argoprojiov1alpha1.ApplicationSetTemplate{}) + + } + + var matrixGenerator = NewMatrixGenerator( + map[string]Generator{ + "Git": genMock, + "List": &ListGenerator{}, + }, + ) + + got, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{ + Matrix: &argoprojiov1alpha1.MatrixGenerator{ + Generators: testCaseCopy.baseGenerators, + Template: argoprojiov1alpha1.ApplicationSetTemplate{}, + }, + }, appSet) + + if testCaseCopy.expectedErr != nil { + assert.ErrorIs(t, err, testCaseCopy.expectedErr) + } else { + assert.NoError(t, err) + assert.Equal(t, testCaseCopy.expected, got) + } + + }) + + } +} + type generatorMock struct { mock.Mock } @@ -857,3 +1080,72 @@ func (g *generatorMock) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Appl return args.Get(0).(time.Duration) } + +func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) { + // Given a matrix generator over a list generator and a git files generator, the nested git files generator should + // be treated as a files generator, and it should produce parameters. + + // This tests for a specific bug where a nested git files generator was being treated as a directory generator. This + // happened because, when the matrix generator was being processed, the nested git files generator was being + // interpolated by the deeplyReplace function. That function cannot differentiate between a nil slice and an empty + // slice. So it was replacing the `Directories` field with an empty slice, which the ApplicationSet controller + // interpreted as meaning this was a directory generator, not a files generator. + + // Now instead of checking for nil, we check whether the field is a non-empty slice. This test prevents a regression + // of that bug. + + listGeneratorMock := &generatorMock{} + listGeneratorMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet")).Return([]map[string]interface{}{ + {"some": "value"}, + }, nil) + listGeneratorMock.On("GetTemplate", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator")).Return(&argoprojiov1alpha1.ApplicationSetTemplate{}) + + gitGeneratorSpec := &argoprojiov1alpha1.GitGenerator{ + RepoURL: "https://git.example.com", + Files: []argoprojiov1alpha1.GitFileGeneratorItem{ + {Path: "some/path.json"}, + }, + } + + repoServiceMock := &mocks.Repos{} + repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{ + "some/path.json": []byte("test: content"), + }, nil) + gitGenerator := NewGitGenerator(repoServiceMock) + + matrixGenerator := NewMatrixGenerator(map[string]Generator{ + "List": listGeneratorMock, + "Git": gitGenerator, + }) + + matrixGeneratorSpec := &argoprojiov1alpha1.MatrixGenerator{ + Generators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{ + { + List: &argoprojiov1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + { + Raw: []byte(`{"some": "value"}`), + }, + }, + }, + }, + { + Git: gitGeneratorSpec, + }, + }, + } + params, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{ + Matrix: matrixGeneratorSpec, + }, &argoprojiov1alpha1.ApplicationSet{}) + require.NoError(t, err) + assert.Equal(t, []map[string]interface{}{{ + "path": "some", + "path.basename": "some", + "path.basenameNormalized": "some", + "path.filename": "path.json", + "path.filenameNormalized": "path.json", + "path[0]": "some", + "some": "value", + "test": "content", + }}, params) +} diff --git a/applicationset/generators/merge.go b/applicationset/generators/merge.go index 69733c847e92d..ebda7180df70f 100644 --- a/applicationset/generators/merge.go +++ b/applicationset/generators/merge.go @@ -9,6 +9,8 @@ import ( "github.com/argoproj/argo-cd/v2/applicationset/utils" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + + log "github.com/sirupsen/logrus" ) var _ Generator = (*MergeGenerator)(nil) @@ -36,10 +38,10 @@ func NewMergeGenerator(supportedGenerators map[string]Generator) Generator { // in slices ordered according to the order of the given generators. func (m *MergeGenerator) getParamSetsForAllGenerators(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([][]map[string]interface{}, error) { var paramSets [][]map[string]interface{} - for _, generator := range generators { + for i, generator := range generators { generatorParamSets, err := m.getParams(generator, appSet) if err != nil { - return nil, err + return nil, fmt.Errorf("error getting params from generator %d of %d: %w", i+1, len(generators), err) } // concatenate param lists produced by each generator paramSets = append(paramSets, generatorParamSets) @@ -59,18 +61,18 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl paramSetsFromGenerators, err := m.getParamSetsForAllGenerators(appSetGenerator.Merge.Generators, appSet) if err != nil { - return nil, err + return nil, fmt.Errorf("error getting param sets from generators: %w", err) } baseParamSetsByMergeKey, err := getParamSetsByMergeKey(appSetGenerator.Merge.MergeKeys, paramSetsFromGenerators[0]) if err != nil { - return nil, err + return nil, fmt.Errorf("error getting param sets by merge key: %w", err) } for _, paramSets := range paramSetsFromGenerators[1:] { paramSetsByMergeKey, err := getParamSetsByMergeKey(appSetGenerator.Merge.MergeKeys, paramSets) if err != nil { - return nil, err + return nil, fmt.Errorf("error getting param sets by merge key: %w", err) } for mergeKeyValue, baseParamSet := range baseParamSetsByMergeKey { @@ -78,13 +80,13 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl if appSet.Spec.GoTemplate { if err := mergo.Merge(&baseParamSet, overrideParamSet, mergo.WithOverride); err != nil { - return nil, fmt.Errorf("failed to merge base param set with override param set: %w", err) + return nil, fmt.Errorf("error merging base param set with override param set: %w", err) } baseParamSetsByMergeKey[mergeKeyValue] = baseParamSet } else { overriddenParamSet, err := utils.CombineStringMapsAllowDuplicates(baseParamSet, overrideParamSet) if err != nil { - return nil, err + return nil, fmt.Errorf("error combining string maps: %w", err) } baseParamSetsByMergeKey[mergeKeyValue] = utils.ConvertToMapStringInterface(overriddenParamSet) } @@ -123,7 +125,7 @@ func getParamSetsByMergeKey(mergeKeys []string, paramSets []map[string]interface } paramSetKeyJson, err := json.Marshal(paramSetKey) if err != nil { - return nil, err + return nil, fmt.Errorf("error marshalling param set key json: %w", err) } paramSetKeyString := string(paramSetKeyJson) if _, exists := paramSetsByMergeKey[paramSetKeyString]; exists { @@ -137,26 +139,24 @@ func getParamSetsByMergeKey(mergeKeys []string, paramSets []map[string]interface // getParams get the parameters generated by this generator. func (m *MergeGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { - - var matrix *argoprojiov1alpha1.MatrixGenerator - if appSetBaseGenerator.Matrix != nil { - nestedMatrix, err := argoprojiov1alpha1.ToNestedMatrixGenerator(appSetBaseGenerator.Matrix) - if err != nil { - return nil, err - } - if nestedMatrix != nil { - matrix = nestedMatrix.ToMatrixGenerator() - } + matrixGen, err := getMatrixGenerator(appSetBaseGenerator) + if err != nil { + return nil, err } - - var mergeGenerator *argoprojiov1alpha1.MergeGenerator - if appSetBaseGenerator.Merge != nil { - nestedMerge, err := argoprojiov1alpha1.ToNestedMergeGenerator(appSetBaseGenerator.Merge) - if err != nil { - return nil, err + if matrixGen != nil && !appSet.Spec.ApplyNestedSelectors { + foundSelector := dropDisabledNestedSelectors(matrixGen.Generators) + if foundSelector { + log.Warnf("AppSet '%v' defines selector on nested matrix generator's generator without enabling them via 'spec.applyNestedSelectors', ignoring nested selector", appSet.Name) } - if nestedMerge != nil { - mergeGenerator = nestedMerge.ToMergeGenerator() + } + mergeGen, err := getMergeGenerator(appSetBaseGenerator) + if err != nil { + return nil, err + } + if mergeGen != nil && !appSet.Spec.ApplyNestedSelectors { + foundSelector := dropDisabledNestedSelectors(mergeGen.Generators) + if foundSelector { + log.Warnf("AppSet '%v' defines selector on nested merge generator's generator without enabling them via 'spec.applyNestedSelectors', ignoring nested selector", appSet.Name) } } @@ -168,8 +168,9 @@ func (m *MergeGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.Applic SCMProvider: appSetBaseGenerator.SCMProvider, ClusterDecisionResource: appSetBaseGenerator.ClusterDecisionResource, PullRequest: appSetBaseGenerator.PullRequest, - Matrix: matrix, - Merge: mergeGenerator, + Plugin: appSetBaseGenerator.Plugin, + Matrix: matrixGen, + Merge: mergeGen, Selector: appSetBaseGenerator.Selector, }, m.supportedGenerators, @@ -197,10 +198,18 @@ func (m *MergeGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.App var found bool for _, r := range appSetGenerator.Merge.Generators { + matrixGen, _ := getMatrixGenerator(r) + mergeGen, _ := getMergeGenerator(r) base := &argoprojiov1alpha1.ApplicationSetGenerator{ - List: r.List, - Clusters: r.Clusters, - Git: r.Git, + List: r.List, + Clusters: r.Clusters, + Git: r.Git, + PullRequest: r.PullRequest, + Plugin: r.Plugin, + SCMProvider: r.SCMProvider, + ClusterDecisionResource: r.ClusterDecisionResource, + Matrix: matrixGen, + Merge: mergeGen, } generators := GetRelevantGenerators(base, m.supportedGenerators) @@ -221,6 +230,17 @@ func (m *MergeGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.App } +func getMergeGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MergeGenerator, error) { + if r.Merge == nil { + return nil, nil + } + merge, err := argoprojiov1alpha1.ToNestedMergeGenerator(r.Merge) + if err != nil { + return nil, fmt.Errorf("error converting to nested merge generator: %w", err) + } + return merge.ToMergeGenerator(), nil +} + // GetTemplate gets the Template field for the MergeGenerator. func (m *MergeGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate { return &appSetGenerator.Merge.Template diff --git a/applicationset/generators/plugin.go b/applicationset/generators/plugin.go new file mode 100644 index 0000000000000..e0acca0622cdc --- /dev/null +++ b/applicationset/generators/plugin.go @@ -0,0 +1,211 @@ +package generators + +import ( + "context" + "fmt" + "strconv" + "strings" + "time" + + "github.com/jeremywohl/flatten" + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" + + argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/util/settings" + + "github.com/argoproj/argo-cd/v2/applicationset/services/plugin" +) + +const ( + DefaultPluginRequeueAfterSeconds = 30 * time.Minute +) + +var _ Generator = (*PluginGenerator)(nil) + +type PluginGenerator struct { + client client.Client + ctx context.Context + clientset kubernetes.Interface + namespace string +} + +func NewPluginGenerator(client client.Client, ctx context.Context, clientset kubernetes.Interface, namespace string) Generator { + g := &PluginGenerator{ + client: client, + ctx: ctx, + clientset: clientset, + namespace: namespace, + } + return g +} + +func (g *PluginGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration { + // Return a requeue default of 30 minutes, if no default is specified. + + if appSetGenerator.Plugin.RequeueAfterSeconds != nil { + return time.Duration(*appSetGenerator.Plugin.RequeueAfterSeconds) * time.Second + } + + return DefaultPluginRequeueAfterSeconds +} + +func (g *PluginGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate { + return &appSetGenerator.Plugin.Template +} + +func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { + + if appSetGenerator == nil { + return nil, EmptyAppSetGeneratorError + } + + if appSetGenerator.Plugin == nil { + return nil, EmptyAppSetGeneratorError + } + + ctx := context.Background() + + providerConfig := appSetGenerator.Plugin + + pluginClient, err := g.getPluginFromGenerator(ctx, applicationSetInfo.Name, providerConfig) + if err != nil { + return nil, fmt.Errorf("error getting plugin from generator: %w", err) + } + + list, err := pluginClient.List(ctx, providerConfig.Input.Parameters) + if err != nil { + return nil, fmt.Errorf("error listing params: %w", err) + } + + res, err := g.generateParams(appSetGenerator, applicationSetInfo, list.Output.Parameters, appSetGenerator.Plugin.Input.Parameters, applicationSetInfo.Spec.GoTemplate) + if err != nil { + return nil, fmt.Errorf("error generating params: %w", err) + } + + return res, nil +} + +func (g *PluginGenerator) getPluginFromGenerator(ctx context.Context, appSetName string, generatorConfig *argoprojiov1alpha1.PluginGenerator) (*plugin.Service, error) { + cm, err := g.getConfigMap(ctx, generatorConfig.ConfigMapRef.Name) + if err != nil { + return nil, fmt.Errorf("error fetching ConfigMap: %w", err) + } + token, err := g.getToken(ctx, cm["token"]) + if err != nil { + return nil, fmt.Errorf("error fetching Secret token: %v", err) + } + + var requestTimeout int + requestTimeoutStr, ok := cm["requestTimeout"] + if ok { + requestTimeout, err = strconv.Atoi(requestTimeoutStr) + if err != nil { + return nil, fmt.Errorf("error set requestTimeout : %w", err) + } + } + + pluginClient, err := plugin.NewPluginService(ctx, appSetName, cm["baseUrl"], token, requestTimeout) + if err != nil { + return nil, fmt.Errorf("error initializing plugin client: %w", err) + } + return pluginClient, nil +} + +func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, objectsFound []map[string]interface{}, pluginParams argoprojiov1alpha1.PluginParameters, useGoTemplate bool) ([]map[string]interface{}, error) { + res := []map[string]interface{}{} + + for _, objectFound := range objectsFound { + + params := map[string]interface{}{} + + if useGoTemplate { + for k, v := range objectFound { + params[k] = v + } + } else { + flat, err := flatten.Flatten(objectFound, "", flatten.DotStyle) + if err != nil { + return nil, err + } + for k, v := range flat { + params[k] = fmt.Sprintf("%v", v) + } + } + + params["generator"] = map[string]interface{}{ + "input": map[string]argoprojiov1alpha1.PluginParameters{ + "parameters": pluginParams, + }, + } + + err := appendTemplatedValues(appSetGenerator.Plugin.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) + if err != nil { + return nil, err + } + + res = append(res, params) + } + + return res, nil +} + +func (g *PluginGenerator) getToken(ctx context.Context, tokenRef string) (string, error) { + + if tokenRef == "" || !strings.HasPrefix(tokenRef, "$") { + return "", fmt.Errorf("token is empty, or does not reference a secret key starting with '$': %v", tokenRef) + } + + secretName, tokenKey := plugin.ParseSecretKey(tokenRef) + + secret := &corev1.Secret{} + err := g.client.Get( + ctx, + client.ObjectKey{ + Name: secretName, + Namespace: g.namespace, + }, + secret) + + if err != nil { + return "", fmt.Errorf("error fetching secret %s/%s: %v", g.namespace, secretName, err) + } + + secretValues := make(map[string]string, len(secret.Data)) + + for k, v := range secret.Data { + secretValues[k] = string(v) + } + + token := settings.ReplaceStringSecret(tokenKey, secretValues) + + return token, err +} + +func (g *PluginGenerator) getConfigMap(ctx context.Context, configMapRef string) (map[string]string, error) { + cm := &corev1.ConfigMap{} + err := g.client.Get( + ctx, + client.ObjectKey{ + Name: configMapRef, + Namespace: g.namespace, + }, + cm) + + if err != nil { + return nil, err + } + + baseUrl, ok := cm.Data["baseUrl"] + if !ok || baseUrl == "" { + return nil, fmt.Errorf("baseUrl not found in ConfigMap") + } + + token, ok := cm.Data["token"] + if !ok || token == "" { + return nil, fmt.Errorf("token not found in ConfigMap") + } + + return cm.Data, nil +} diff --git a/applicationset/generators/plugin_test.go b/applicationset/generators/plugin_test.go new file mode 100644 index 0000000000000..9611a2cbf14c1 --- /dev/null +++ b/applicationset/generators/plugin_test.go @@ -0,0 +1,705 @@ +package generators + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + kubefake "k8s.io/client-go/kubernetes/fake" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/argoproj/argo-cd/v2/applicationset/services/plugin" + argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +func TestPluginGenerateParams(t *testing.T) { + testCases := []struct { + name string + configmap *v1.ConfigMap + secret *v1.Secret + inputParameters map[string]apiextensionsv1.JSON + values map[string]string + gotemplate bool + expected []map[string]interface{} + content []byte + expectedError error + }{ + { + name: "simple case", + configmap: &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "first-plugin-cm", + Namespace: "default", + }, + Data: map[string]string{ + "baseUrl": "http://127.0.0.1", + "token": "$plugin.token", + }, + }, + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "plugin.token": []byte("my-secret"), + }, + }, + inputParameters: map[string]apiextensionsv1.JSON{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + gotemplate: false, + content: []byte(`{"output": { + "parameters": [{ + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123 + }] + }}`), + expected: []map[string]interface{}{ + { + "key1": "val1", + "key2.key2_1": "val2_1", + "key2.key2_2.key2_2_1": "val2_2_1", + "key3": "123", + "generator": map[string]interface{}{ + "input": argoprojiov1alpha1.PluginInput{ + Parameters: argoprojiov1alpha1.PluginParameters{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + }, + }, + }, + }, + expectedError: nil, + }, + { + name: "simple case with values", + configmap: &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "first-plugin-cm", + Namespace: "default", + }, + Data: map[string]string{ + "baseUrl": "http://127.0.0.1", + "token": "$plugin.token", + }, + }, + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "plugin.token": []byte("my-secret"), + }, + }, + inputParameters: map[string]apiextensionsv1.JSON{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + values: map[string]string{ + "valuekey1": "valuevalue1", + "valuekey2": "templated-{{key1}}", + }, + gotemplate: false, + content: []byte(`{"output": { + "parameters": [{ + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123 + }] + }}`), + expected: []map[string]interface{}{ + { + "key1": "val1", + "key2.key2_1": "val2_1", + "key2.key2_2.key2_2_1": "val2_2_1", + "key3": "123", + "values.valuekey1": "valuevalue1", + "values.valuekey2": "templated-val1", + "generator": map[string]interface{}{ + "input": argoprojiov1alpha1.PluginInput{ + Parameters: argoprojiov1alpha1.PluginParameters{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + }, + }, + }, + }, + expectedError: nil, + }, + { + name: "simple case with gotemplate", + configmap: &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "first-plugin-cm", + Namespace: "default", + }, + Data: map[string]string{ + "baseUrl": "http://127.0.0.1", + "token": "$plugin.token", + }, + }, + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "plugin.token": []byte("my-secret"), + }, + }, + inputParameters: map[string]apiextensionsv1.JSON{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + gotemplate: true, + content: []byte(`{"output": { + "parameters": [{ + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123 + }] + }}`), + expected: []map[string]interface{}{ + { + "key1": "val1", + "key2": map[string]interface{}{ + "key2_1": "val2_1", + "key2_2": map[string]interface{}{ + "key2_2_1": "val2_2_1", + }, + }, + "key3": float64(123), + "generator": map[string]interface{}{ + "input": argoprojiov1alpha1.PluginInput{ + Parameters: argoprojiov1alpha1.PluginParameters{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + }, + }, + }, + }, + expectedError: nil, + }, + { + name: "simple case with appended params", + configmap: &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "first-plugin-cm", + Namespace: "default", + }, + Data: map[string]string{ + "baseUrl": "http://127.0.0.1", + "token": "$plugin.token", + }, + }, + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "plugin.token": []byte("my-secret"), + }, + }, + inputParameters: map[string]apiextensionsv1.JSON{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + gotemplate: false, + content: []byte(`{"output": {"parameters": [{ + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123, + "pkey2": "valplugin" + }]}}`), + expected: []map[string]interface{}{ + { + "key1": "val1", + "key2.key2_1": "val2_1", + "key2.key2_2.key2_2_1": "val2_2_1", + "key3": "123", + "pkey2": "valplugin", + "generator": map[string]interface{}{ + "input": argoprojiov1alpha1.PluginInput{ + Parameters: argoprojiov1alpha1.PluginParameters{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + }, + }, + }, + }, + expectedError: nil, + }, + { + name: "no params", + configmap: &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "first-plugin-cm", + Namespace: "default", + }, + Data: map[string]string{ + "baseUrl": "http://127.0.0.1", + "token": "$plugin.token", + }, + }, + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "plugin.token": []byte("my-secret"), + }, + }, + inputParameters: argoprojiov1alpha1.PluginParameters{}, + gotemplate: false, + content: []byte(`{"output": { + "parameters": [{ + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123 + }] + }}`), + expected: []map[string]interface{}{ + { + "key1": "val1", + "key2.key2_1": "val2_1", + "key2.key2_2.key2_2_1": "val2_2_1", + "key3": "123", + "generator": map[string]interface{}{ + "input": map[string]map[string]interface{}{ + "parameters": {}, + }, + }, + }, + }, + expectedError: nil, + }, + { + name: "empty return", + configmap: &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "first-plugin-cm", + Namespace: "default", + }, + Data: map[string]string{ + "baseUrl": "http://127.0.0.1", + "token": "$plugin.token", + }, + }, + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "plugin.token": []byte("my-secret"), + }, + }, + inputParameters: map[string]apiextensionsv1.JSON{}, + gotemplate: false, + content: []byte(`{"input": {"parameters": []}}`), + expected: []map[string]interface{}{}, + expectedError: nil, + }, + { + name: "wrong return", + configmap: &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "first-plugin-cm", + Namespace: "default", + }, + Data: map[string]string{ + "baseUrl": "http://127.0.0.1", + "token": "$plugin.token", + }, + }, + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "plugin.token": []byte("my-secret"), + }, + }, + inputParameters: map[string]apiextensionsv1.JSON{}, + gotemplate: false, + content: []byte(`wrong body ...`), + expected: []map[string]interface{}{}, + expectedError: fmt.Errorf("error listing params: error get api 'set': invalid character 'w' looking for beginning of value: wrong body ..."), + }, + { + name: "external secret", + configmap: &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "first-plugin-cm", + Namespace: "default", + }, + Data: map[string]string{ + "baseUrl": "http://127.0.0.1", + "token": "$plugin-secret:plugin.token", + }, + }, + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "plugin-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "plugin.token": []byte("my-secret"), + }, + }, + inputParameters: map[string]apiextensionsv1.JSON{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + gotemplate: false, + content: []byte(`{"output": {"parameters": [{ + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123, + "pkey2": "valplugin" + }]}}`), + expected: []map[string]interface{}{ + { + "key1": "val1", + "key2.key2_1": "val2_1", + "key2.key2_2.key2_2_1": "val2_2_1", + "key3": "123", + "pkey2": "valplugin", + "generator": map[string]interface{}{ + "input": argoprojiov1alpha1.PluginInput{ + Parameters: argoprojiov1alpha1.PluginParameters{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + }, + }, + }, + }, + expectedError: nil, + }, + { + name: "no secret", + configmap: &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "first-plugin-cm", + Namespace: "default", + }, + Data: map[string]string{ + "baseUrl": "http://127.0.0.1", + "token": "$plugin.token", + }, + }, + secret: &v1.Secret{}, + inputParameters: map[string]apiextensionsv1.JSON{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + gotemplate: false, + content: []byte(`{"output": { + "parameters": [{ + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123 + }] + }}`), + expected: []map[string]interface{}{ + { + "key1": "val1", + "key2.key2_1": "val2_1", + "key2.key2_2.key2_2_1": "val2_2_1", + "key3": "123", + "generator": map[string]interface{}{ + "input": argoprojiov1alpha1.PluginInput{ + Parameters: argoprojiov1alpha1.PluginParameters{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + }, + }, + }, + }, + expectedError: fmt.Errorf("error getting plugin from generator: error fetching Secret token: error fetching secret default/argocd-secret: secrets \"argocd-secret\" not found"), + }, + { + name: "no configmap", + configmap: &v1.ConfigMap{}, + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "plugin.token": []byte("my-secret"), + }, + }, + inputParameters: map[string]apiextensionsv1.JSON{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + gotemplate: false, + content: []byte(`{"output": { + "parameters": [{ + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123 + }] + }}`), + expected: []map[string]interface{}{ + { + "key1": "val1", + "key2.key2_1": "val2_1", + "key2.key2_2.key2_2_1": "val2_2_1", + "key3": "123", + "generator": map[string]interface{}{ + "input": argoprojiov1alpha1.PluginInput{ + Parameters: argoprojiov1alpha1.PluginParameters{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + }, + }, + }, + }, + expectedError: fmt.Errorf("error getting plugin from generator: error fetching ConfigMap: configmaps \"\" not found"), + }, + { + name: "no baseUrl", + configmap: &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "first-plugin-cm", + Namespace: "default", + }, + Data: map[string]string{ + "token": "$plugin.token", + }, + }, + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "plugin.token": []byte("my-secret"), + }, + }, + inputParameters: map[string]apiextensionsv1.JSON{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + gotemplate: false, + content: []byte(`{"output": { + "parameters": [{ + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123 + }] + }}`), + expected: []map[string]interface{}{ + { + "key1": "val1", + "key2.key2_1": "val2_1", + "key2.key2_2.key2_2_1": "val2_2_1", + "key3": "123", + "generator": map[string]interface{}{ + "input": argoprojiov1alpha1.PluginInput{ + Parameters: argoprojiov1alpha1.PluginParameters{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + }, + }, + }, + }, + expectedError: fmt.Errorf("error getting plugin from generator: error fetching ConfigMap: baseUrl not found in ConfigMap"), + }, + { + name: "no token", + configmap: &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "first-plugin-cm", + Namespace: "default", + }, + Data: map[string]string{ + "baseUrl": "http://127.0.0.1", + }, + }, + secret: &v1.Secret{}, + inputParameters: map[string]apiextensionsv1.JSON{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + gotemplate: false, + content: []byte(`{"output": { + "parameters": [{ + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123 + }] + }}`), + expected: []map[string]interface{}{ + { + "key1": "val1", + "key2.key2_1": "val2_1", + "key2.key2_2.key2_2_1": "val2_2_1", + "key3": "123", + "generator": map[string]interface{}{ + "input": argoprojiov1alpha1.PluginInput{ + Parameters: argoprojiov1alpha1.PluginParameters{ + "pkey1": {Raw: []byte(`"val1"`)}, + "pkey2": {Raw: []byte(`"val2"`)}, + }, + }, + }, + }, + }, + expectedError: fmt.Errorf("error getting plugin from generator: error fetching ConfigMap: token not found in ConfigMap"), + }, + } + + ctx := context.Background() + + for _, testCase := range testCases { + + t.Run(testCase.name, func(t *testing.T) { + + generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{ + Plugin: &argoprojiov1alpha1.PluginGenerator{ + ConfigMapRef: argoprojiov1alpha1.PluginConfigMapRef{Name: testCase.configmap.Name}, + Input: argoprojiov1alpha1.PluginInput{ + Parameters: testCase.inputParameters, + }, + Values: testCase.values, + }, + } + + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + authHeader := r.Header.Get("Authorization") + _, tokenKey := plugin.ParseSecretKey(testCase.configmap.Data["token"]) + expectedToken := testCase.secret.Data[strings.Replace(tokenKey, "$", "", -1)] + if authHeader != "Bearer "+string(expectedToken) { + w.WriteHeader(http.StatusUnauthorized) + return + } + + w.Header().Set("Content-Type", "application/json") + _, err := w.Write(testCase.content) + if err != nil { + assert.NoError(t, fmt.Errorf("Error Write %v", err)) + } + }) + + fakeServer := httptest.NewServer(handler) + + defer fakeServer.Close() + + if _, ok := testCase.configmap.Data["baseUrl"]; ok { + testCase.configmap.Data["baseUrl"] = fakeServer.URL + } + + fakeClient := kubefake.NewSimpleClientset(append([]runtime.Object{}, testCase.configmap, testCase.secret)...) + + fakeClientWithCache := fake.NewClientBuilder().WithObjects([]client.Object{testCase.configmap, testCase.secret}...).Build() + + var pluginGenerator = NewPluginGenerator(fakeClientWithCache, ctx, fakeClient, "default") + + applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "set", + }, + Spec: argoprojiov1alpha1.ApplicationSetSpec{ + GoTemplate: testCase.gotemplate, + }, + } + + got, err := pluginGenerator.GenerateParams(&generatorConfig, &applicationSetInfo) + + if err != nil { + fmt.Println(err) + } + + if testCase.expectedError != nil { + assert.EqualError(t, err, testCase.expectedError.Error()) + } else { + assert.NoError(t, err) + expectedJson, err := json.Marshal(testCase.expected) + require.NoError(t, err) + gotJson, err := json.Marshal(got) + require.NoError(t, err) + assert.Equal(t, string(expectedJson), string(gotJson)) + } + }) + } +} diff --git a/applicationset/generators/pull_request.go b/applicationset/generators/pull_request.go index db6c80c6c0f5c..c1dfd5ed978e9 100644 --- a/applicationset/generators/pull_request.go +++ b/applicationset/generators/pull_request.go @@ -11,7 +11,6 @@ import ( "github.com/gosimple/slug" - "github.com/argoproj/argo-cd/v2/applicationset/services/pull_request" pullrequest "github.com/argoproj/argo-cd/v2/applicationset/services/pull_request" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -26,12 +25,18 @@ type PullRequestGenerator struct { client client.Client selectServiceProviderFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) auth SCMAuthProviders + scmRootCAPath string + allowedSCMProviders []string + enableSCMProviders bool } -func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders) Generator { +func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders, scmRootCAPath string, allowedScmProviders []string, enableSCMProviders bool) Generator { g := &PullRequestGenerator{ - client: client, - auth: auth, + client: client, + auth: auth, + scmRootCAPath: scmRootCAPath, + allowedSCMProviders: allowedScmProviders, + enableSCMProviders: enableSCMProviders, } g.selectServiceProviderFunc = g.selectServiceProvider return g @@ -63,10 +68,10 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha ctx := context.Background() svc, err := g.selectServiceProviderFunc(ctx, appSetGenerator.PullRequest, applicationSetInfo) if err != nil { - return nil, fmt.Errorf("failed to select pull request service provider: %v", err) + return nil, fmt.Errorf("failed to select pull request service provider: %w", err) } - pulls, err := pull_request.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters) + pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters) if err != nil { return nil, fmt.Errorf("error listing repos: %v", err) } @@ -84,18 +89,27 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha } var shortSHALength int + var shortSHALength7 int for _, pull := range pulls { shortSHALength = 8 if len(pull.HeadSHA) < 8 { shortSHALength = len(pull.HeadSHA) } + shortSHALength7 = 7 + if len(pull.HeadSHA) < 7 { + shortSHALength7 = len(pull.HeadSHA) + } + paramMap := map[string]interface{}{ - "number": strconv.Itoa(pull.Number), - "branch": pull.Branch, - "branch_slug": slug.Make(pull.Branch), - "head_sha": pull.HeadSHA, - "head_short_sha": pull.HeadSHA[:shortSHALength], + "number": strconv.Itoa(pull.Number), + "branch": pull.Branch, + "branch_slug": slug.Make(pull.Branch), + "target_branch": pull.TargetBranch, + "target_branch_slug": slug.Make(pull.TargetBranch), + "head_sha": pull.HeadSHA, + "head_short_sha": pull.HeadSHA[:shortSHALength], + "head_short_sha_7": pull.HeadSHA[:shortSHALength7], } // PR lables will only be supported for Go Template appsets, since fasttemplate will be deprecated. @@ -109,6 +123,13 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha // selectServiceProvider selects the provider to get pull requests from the configuration func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, generatorConfig *argoprojiov1alpha1.PullRequestGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) { + if !g.enableSCMProviders { + return nil, ErrSCMProvidersDisabled + } + if err := ScmProviderAllowed(applicationSetInfo, generatorConfig, g.allowedSCMProviders); err != nil { + return nil, fmt.Errorf("scm provider not allowed: %w", err) + } + if generatorConfig.Github != nil { return g.github(ctx, generatorConfig.Github, applicationSetInfo) } @@ -118,7 +139,7 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera if err != nil { return nil, fmt.Errorf("error fetching Secret token: %v", err) } - return pullrequest.NewGitLabService(ctx, token, providerConfig.API, providerConfig.Project, providerConfig.Labels, providerConfig.PullRequestState) + return pullrequest.NewGitLabService(ctx, token, providerConfig.API, providerConfig.Project, providerConfig.Labels, providerConfig.PullRequestState, g.scmRootCAPath, providerConfig.Insecure) } if generatorConfig.Gitea != nil { providerConfig := generatorConfig.Gitea @@ -140,6 +161,32 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera return pullrequest.NewBitbucketServiceNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.Repo) } } + if generatorConfig.Bitbucket != nil { + providerConfig := generatorConfig.Bitbucket + if providerConfig.BearerToken != nil { + appToken, err := g.getSecretRef(ctx, providerConfig.BearerToken.TokenRef, applicationSetInfo.Namespace) + if err != nil { + return nil, fmt.Errorf("error fetching Secret Bearer token: %v", err) + } + return pullrequest.NewBitbucketCloudServiceBearerToken(providerConfig.API, appToken, providerConfig.Owner, providerConfig.Repo) + } else if providerConfig.BasicAuth != nil { + password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace) + if err != nil { + return nil, fmt.Errorf("error fetching Secret token: %v", err) + } + return pullrequest.NewBitbucketCloudServiceBasicAuth(providerConfig.API, providerConfig.BasicAuth.Username, password, providerConfig.Owner, providerConfig.Repo) + } else { + return pullrequest.NewBitbucketCloudServiceNoAuth(providerConfig.API, providerConfig.Owner, providerConfig.Repo) + } + } + if generatorConfig.AzureDevOps != nil { + providerConfig := generatorConfig.AzureDevOps + token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace) + if err != nil { + return nil, fmt.Errorf("error fetching Secret token: %v", err) + } + return pullrequest.NewAzureDevOpsService(ctx, token, providerConfig.API, providerConfig.Organization, providerConfig.Project, providerConfig.Repo, providerConfig.Labels) + } return nil, fmt.Errorf("no Pull Request provider implementation configured") } diff --git a/applicationset/generators/pull_request_test.go b/applicationset/generators/pull_request_test.go index caad198670302..9f4d3d0a9b693 100644 --- a/applicationset/generators/pull_request_test.go +++ b/applicationset/generators/pull_request_test.go @@ -27,10 +27,11 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { return pullrequest.NewFakeService( ctx, []*pullrequest.PullRequest{ - &pullrequest.PullRequest{ - Number: 1, - Branch: "branch1", - HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958", + { + Number: 1, + Branch: "branch1", + TargetBranch: "master", + HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958", }, }, nil, @@ -38,11 +39,14 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { }, expected: []map[string]interface{}{ { - "number": "1", - "branch": "branch1", - "branch_slug": "branch1", - "head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958", - "head_short_sha": "089d92cb", + "number": "1", + "branch": "branch1", + "branch_slug": "branch1", + "target_branch": "master", + "target_branch_slug": "master", + "head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958", + "head_short_sha": "089d92cb", + "head_short_sha_7": "089d92c", }, }, expectedErr: nil, @@ -52,10 +56,11 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { return pullrequest.NewFakeService( ctx, []*pullrequest.PullRequest{ - &pullrequest.PullRequest{ - Number: 2, - Branch: "feat/areally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature", - HeadSHA: "9b34ff5bd418e57d58891eb0aa0728043ca1e8be", + { + Number: 2, + Branch: "feat/areally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature", + TargetBranch: "feat/anotherreally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature", + HeadSHA: "9b34ff5bd418e57d58891eb0aa0728043ca1e8be", }, }, nil, @@ -63,11 +68,14 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { }, expected: []map[string]interface{}{ { - "number": "2", - "branch": "feat/areally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature", - "branch_slug": "feat-areally-long-pull-request-name-to-test-argo", - "head_sha": "9b34ff5bd418e57d58891eb0aa0728043ca1e8be", - "head_short_sha": "9b34ff5b", + "number": "2", + "branch": "feat/areally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature", + "branch_slug": "feat-areally-long-pull-request-name-to-test-argo", + "target_branch": "feat/anotherreally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature", + "target_branch_slug": "feat-anotherreally-long-pull-request-name-to-test", + "head_sha": "9b34ff5bd418e57d58891eb0aa0728043ca1e8be", + "head_short_sha": "9b34ff5b", + "head_short_sha_7": "9b34ff5", }, }, expectedErr: nil, @@ -77,10 +85,11 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { return pullrequest.NewFakeService( ctx, []*pullrequest.PullRequest{ - &pullrequest.PullRequest{ - Number: 1, - Branch: "a-very-short-sha", - HeadSHA: "abcd", + { + Number: 1, + Branch: "a-very-short-sha", + TargetBranch: "master", + HeadSHA: "abcd", }, }, nil, @@ -88,11 +97,14 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { }, expected: []map[string]interface{}{ { - "number": "1", - "branch": "a-very-short-sha", - "branch_slug": "a-very-short-sha", - "head_sha": "abcd", - "head_short_sha": "abcd", + "number": "1", + "branch": "a-very-short-sha", + "branch_slug": "a-very-short-sha", + "target_branch": "master", + "target_branch_slug": "master", + "head_sha": "abcd", + "head_short_sha": "abcd", + "head_short_sha_7": "abcd", }, }, expectedErr: nil, @@ -113,11 +125,12 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { return pullrequest.NewFakeService( ctx, []*pullrequest.PullRequest{ - &pullrequest.PullRequest{ - Number: 1, - Branch: "branch1", - HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958", - Labels: []string{"preview"}, + { + Number: 1, + Branch: "branch1", + TargetBranch: "master", + HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958", + Labels: []string{"preview"}, }, }, nil, @@ -125,12 +138,15 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { }, expected: []map[string]interface{}{ { - "number": "1", - "branch": "branch1", - "branch_slug": "branch1", - "head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958", - "head_short_sha": "089d92cb", - "labels": []string{"preview"}, + "number": "1", + "branch": "branch1", + "branch_slug": "branch1", + "target_branch": "master", + "target_branch_slug": "master", + "head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958", + "head_short_sha": "089d92cb", + "head_short_sha_7": "089d92c", + "labels": []string{"preview"}, }, }, expectedErr: nil, @@ -146,11 +162,12 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { return pullrequest.NewFakeService( ctx, []*pullrequest.PullRequest{ - &pullrequest.PullRequest{ - Number: 1, - Branch: "branch1", - HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958", - Labels: []string{"preview"}, + { + Number: 1, + Branch: "branch1", + TargetBranch: "master", + HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958", + Labels: []string{"preview"}, }, }, nil, @@ -158,11 +175,14 @@ func TestPullRequestGithubGenerateParams(t *testing.T) { }, expected: []map[string]interface{}{ { - "number": "1", - "branch": "branch1", - "branch_slug": "branch1", - "head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958", - "head_short_sha": "089d92cb", + "number": "1", + "branch": "branch1", + "branch_slug": "branch1", + "target_branch": "master", + "target_branch_slug": "master", + "head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958", + "head_short_sha": "089d92cb", + "head_short_sha_7": "089d92c", }, }, expectedErr: nil, @@ -253,3 +273,102 @@ func TestPullRequestGetSecretRef(t *testing.T) { }) } } + +func TestAllowedSCMProviderPullRequest(t *testing.T) { + cases := []struct { + name string + providerConfig *argoprojiov1alpha1.PullRequestGenerator + expectedError error + }{ + { + name: "Error Github", + providerConfig: &argoprojiov1alpha1.PullRequestGenerator{ + Github: &argoprojiov1alpha1.PullRequestGeneratorGithub{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + expectedError: &ErrDisallowedSCMProvider{}, + }, + { + name: "Error Gitlab", + providerConfig: &argoprojiov1alpha1.PullRequestGenerator{ + GitLab: &argoprojiov1alpha1.PullRequestGeneratorGitLab{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + expectedError: &ErrDisallowedSCMProvider{}, + }, + { + name: "Error Gitea", + providerConfig: &argoprojiov1alpha1.PullRequestGenerator{ + Gitea: &argoprojiov1alpha1.PullRequestGeneratorGitea{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + expectedError: &ErrDisallowedSCMProvider{}, + }, + { + name: "Error Bitbucket", + providerConfig: &argoprojiov1alpha1.PullRequestGenerator{ + BitbucketServer: &argoprojiov1alpha1.PullRequestGeneratorBitbucketServer{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + expectedError: &ErrDisallowedSCMProvider{}, + }, + } + + for _, testCase := range cases { + testCaseCopy := testCase + + t.Run(testCaseCopy.name, func(t *testing.T) { + t.Parallel() + + pullRequestGenerator := NewPullRequestGenerator(nil, SCMAuthProviders{}, "", []string{ + "github.myorg.com", + "gitlab.myorg.com", + "gitea.myorg.com", + "bitbucket.myorg.com", + "azuredevops.myorg.com", + }, true) + + applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "set", + }, + Spec: argoprojiov1alpha1.ApplicationSetSpec{ + Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{ + PullRequest: testCaseCopy.providerConfig, + }}, + }, + } + + _, err := pullRequestGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) + + assert.Error(t, err, "Must return an error") + assert.ErrorAs(t, err, testCaseCopy.expectedError) + }) + } +} + +func TestSCMProviderDisabled_PRGenerator(t *testing.T) { + generator := NewPullRequestGenerator(nil, SCMAuthProviders{}, "", []string{}, false) + + applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "set", + }, + Spec: argoprojiov1alpha1.ApplicationSetSpec{ + Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{ + PullRequest: &argoprojiov1alpha1.PullRequestGenerator{ + Github: &argoprojiov1alpha1.PullRequestGeneratorGithub{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + }}, + }, + } + + _, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) + assert.ErrorIs(t, err, ErrSCMProvidersDisabled) +} diff --git a/applicationset/generators/scm_provider.go b/applicationset/generators/scm_provider.go index 352b60d26d398..42b7789be67f0 100644 --- a/applicationset/generators/scm_provider.go +++ b/applicationset/generators/scm_provider.go @@ -2,6 +2,7 @@ package generators import ( "context" + "errors" "fmt" "strings" "time" @@ -9,9 +10,12 @@ import ( corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" + log "github.com/sirupsen/logrus" + "github.com/argoproj/argo-cd/v2/applicationset/services/github_app_auth" "github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider" "github.com/argoproj/argo-cd/v2/applicationset/utils" + "github.com/argoproj/argo-cd/v2/common" argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -26,22 +30,28 @@ type SCMProviderGenerator struct { // Testing hooks. overrideProvider scm_provider.SCMProviderService SCMAuthProviders + scmRootCAPath string + allowedSCMProviders []string + enableSCMProviders bool } type SCMAuthProviders struct { GitHubApps github_app_auth.Credentials } -func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders) Generator { +func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders, scmRootCAPath string, allowedSCMProviders []string, enableSCMProviders bool) Generator { return &SCMProviderGenerator{ - client: client, - SCMAuthProviders: providers, + client: client, + SCMAuthProviders: providers, + scmRootCAPath: scmRootCAPath, + allowedSCMProviders: allowedSCMProviders, + enableSCMProviders: enableSCMProviders, } } // Testing generator func NewTestSCMProviderGenerator(overrideProvider scm_provider.SCMProviderService) Generator { - return &SCMProviderGenerator{overrideProvider: overrideProvider} + return &SCMProviderGenerator{overrideProvider: overrideProvider, enableSCMProviders: true} } func (g *SCMProviderGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration { @@ -58,6 +68,46 @@ func (g *SCMProviderGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.A return &appSetGenerator.SCMProvider.Template } +var ErrSCMProvidersDisabled = errors.New("scm providers are disabled") + +type ErrDisallowedSCMProvider struct { + Provider string + Allowed []string +} + +func NewErrDisallowedSCMProvider(provider string, allowed []string) ErrDisallowedSCMProvider { + return ErrDisallowedSCMProvider{ + Provider: provider, + Allowed: allowed, + } +} + +func (e ErrDisallowedSCMProvider) Error() string { + return fmt.Sprintf("scm provider %q not allowed, must use one of the following: %s", e.Provider, strings.Join(e.Allowed, ", ")) +} + +func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, generator SCMGeneratorWithCustomApiUrl, allowedScmProviders []string) error { + url := generator.CustomApiUrl() + + if url == "" || len(allowedScmProviders) == 0 { + return nil + } + + for _, allowedScmProvider := range allowedScmProviders { + if url == allowedScmProvider { + return nil + } + } + + log.WithFields(log.Fields{ + common.SecurityField: common.SecurityMedium, + "applicationset": applicationSetInfo.Name, + "appSetNamespace": applicationSetInfo.Namespace, + }).Debugf("attempted to use disallowed SCM %q, must use one of the following: %s", url, strings.Join(allowedScmProviders, ", ")) + + return NewErrDisallowedSCMProvider(url, allowedScmProviders) +} + func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { if appSetGenerator == nil { return nil, EmptyAppSetGeneratorError @@ -67,10 +117,18 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha return nil, EmptyAppSetGeneratorError } - ctx := context.Background() + if !g.enableSCMProviders { + return nil, ErrSCMProvidersDisabled + } // Create the SCM provider helper. providerConfig := appSetGenerator.SCMProvider + + if err := ScmProviderAllowed(applicationSetInfo, providerConfig, g.allowedSCMProviders); err != nil { + return nil, fmt.Errorf("scm provider not allowed: %w", err) + } + + ctx := context.Background() var provider scm_provider.SCMProviderService if g.overrideProvider != nil { provider = g.overrideProvider @@ -85,7 +143,7 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha if err != nil { return nil, fmt.Errorf("error fetching Gitlab token: %v", err) } - provider, err = scm_provider.NewGitlabProvider(ctx, providerConfig.Gitlab.Group, token, providerConfig.Gitlab.API, providerConfig.Gitlab.AllBranches, providerConfig.Gitlab.IncludeSubgroups) + provider, err = scm_provider.NewGitlabProvider(ctx, providerConfig.Gitlab.Group, token, providerConfig.Gitlab.API, providerConfig.Gitlab.AllBranches, providerConfig.Gitlab.IncludeSubgroups, providerConfig.Gitlab.WillIncludeSharedProjects(), providerConfig.Gitlab.Insecure, g.scmRootCAPath, providerConfig.Gitlab.Topic) if err != nil { return nil, fmt.Errorf("error initializing Gitlab service: %v", err) } @@ -131,6 +189,12 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha if err != nil { return nil, fmt.Errorf("error initializing Bitbucket cloud service: %v", err) } + } else if providerConfig.AWSCodeCommit != nil { + var awsErr error + provider, awsErr = scm_provider.NewAWSCodeCommitProvider(ctx, providerConfig.AWSCodeCommit.TagFilters, providerConfig.AWSCodeCommit.Role, providerConfig.AWSCodeCommit.Region, providerConfig.AWSCodeCommit.AllBranches) + if awsErr != nil { + return nil, fmt.Errorf("error initializing AWS codecommit service: %v", awsErr) + } } else { return nil, fmt.Errorf("no SCM provider implementation configured") } @@ -140,26 +204,40 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha if err != nil { return nil, fmt.Errorf("error listing repos: %v", err) } - params := make([]map[string]interface{}, 0, len(repos)) + paramsArray := make([]map[string]interface{}, 0, len(repos)) var shortSHALength int + var shortSHALength7 int for _, repo := range repos { shortSHALength = 8 if len(repo.SHA) < 8 { shortSHALength = len(repo.SHA) } - params = append(params, map[string]interface{}{ + shortSHALength7 = 7 + if len(repo.SHA) < 7 { + shortSHALength7 = len(repo.SHA) + } + + params := map[string]interface{}{ "organization": repo.Organization, "repository": repo.Repository, "url": repo.URL, "branch": repo.Branch, "sha": repo.SHA, "short_sha": repo.SHA[:shortSHALength], + "short_sha_7": repo.SHA[:shortSHALength7], "labels": strings.Join(repo.Labels, ","), "branchNormalized": utils.SanitizeName(repo.Branch), - }) + } + + err := appendTemplatedValues(appSetGenerator.SCMProvider.Values, params, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions) + if err != nil { + return nil, fmt.Errorf("failed to append templated values: %w", err) + } + + paramsArray = append(paramsArray, params) } - return params, nil + return paramsArray, nil } func (g *SCMProviderGenerator) getSecretRef(ctx context.Context, ref *argoprojiov1alpha1.SecretRef, namespace string) (string, error) { diff --git a/applicationset/generators/scm_provider_test.go b/applicationset/generators/scm_provider_test.go index bcc4490194285..c438aa8f646fe 100644 --- a/applicationset/generators/scm_provider_test.go +++ b/applicationset/generators/scm_provider_test.go @@ -80,38 +80,234 @@ func TestSCMProviderGetSecretRef(t *testing.T) { } func TestSCMProviderGenerateParams(t *testing.T) { - mockProvider := &scm_provider.MockProvider{ - Repos: []*scm_provider.Repository{ - { - Organization: "myorg", - Repository: "repo1", - URL: "git@github.com:myorg/repo1.git", - Branch: "main", - SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b", - Labels: []string{"prod", "staging"}, + cases := []struct { + name string + repos []*scm_provider.Repository + values map[string]string + expected []map[string]interface{} + expectedError error + }{ + { + name: "Multiple repos with labels", + repos: []*scm_provider.Repository{ + { + Organization: "myorg", + Repository: "repo1", + URL: "git@github.com:myorg/repo1.git", + Branch: "main", + SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b", + Labels: []string{"prod", "staging"}, + }, + { + Organization: "myorg", + Repository: "repo2", + URL: "git@github.com:myorg/repo2.git", + Branch: "main", + SHA: "59d0", + }, + }, + expected: []map[string]interface{}{ + { + "organization": "myorg", + "repository": "repo1", + "url": "git@github.com:myorg/repo1.git", + "branch": "main", + "branchNormalized": "main", + "sha": "0bc57212c3cbbec69d20b34c507284bd300def5b", + "short_sha": "0bc57212", + "short_sha_7": "0bc5721", + "labels": "prod,staging", + }, + { + "organization": "myorg", + "repository": "repo2", + "url": "git@github.com:myorg/repo2.git", + "branch": "main", + "branchNormalized": "main", + "sha": "59d0", + "short_sha": "59d0", + "short_sha_7": "59d0", + "labels": "", + }, + }, + }, + { + name: "Value interpolation", + repos: []*scm_provider.Repository{ + { + Organization: "myorg", + Repository: "repo3", + URL: "git@github.com:myorg/repo3.git", + Branch: "main", + SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b", + Labels: []string{"prod", "staging"}, + }, + }, + values: map[string]string{ + "foo": "bar", + "should_i_force_push_to": "{{ branch }}?", }, - { - Organization: "myorg", - Repository: "repo2", - URL: "git@github.com:myorg/repo2.git", - Branch: "main", - SHA: "59d0", + expected: []map[string]interface{}{ + { + "organization": "myorg", + "repository": "repo3", + "url": "git@github.com:myorg/repo3.git", + "branch": "main", + "branchNormalized": "main", + "sha": "0bc57212c3cbbec69d20b34c507284bd300def5b", + "short_sha": "0bc57212", + "short_sha_7": "0bc5721", + "labels": "prod,staging", + "values.foo": "bar", + "values.should_i_force_push_to": "main?", + }, }, }, } - gen := &SCMProviderGenerator{overrideProvider: mockProvider} - params, err := gen.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{ - SCMProvider: &argoprojiov1alpha1.SCMProviderGenerator{}, - }, nil) - assert.Nil(t, err) - assert.Len(t, params, 2) - assert.Equal(t, "myorg", params[0]["organization"]) - assert.Equal(t, "repo1", params[0]["repository"]) - assert.Equal(t, "git@github.com:myorg/repo1.git", params[0]["url"]) - assert.Equal(t, "main", params[0]["branch"]) - assert.Equal(t, "0bc57212c3cbbec69d20b34c507284bd300def5b", params[0]["sha"]) - assert.Equal(t, "0bc57212", params[0]["short_sha"]) - assert.Equal(t, "59d0", params[1]["short_sha"]) - assert.Equal(t, "prod,staging", params[0]["labels"]) - assert.Equal(t, "repo2", params[1]["repository"]) + + for _, testCase := range cases { + testCaseCopy := testCase + + t.Run(testCaseCopy.name, func(t *testing.T) { + t.Parallel() + + mockProvider := &scm_provider.MockProvider{ + Repos: testCaseCopy.repos, + } + scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider, enableSCMProviders: true} + applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "set", + }, + Spec: argoprojiov1alpha1.ApplicationSetSpec{ + Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{ + SCMProvider: &argoprojiov1alpha1.SCMProviderGenerator{ + Values: testCaseCopy.values, + }, + }}, + }, + } + + got, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) + + if testCaseCopy.expectedError != nil { + assert.EqualError(t, err, testCaseCopy.expectedError.Error()) + } else { + assert.NoError(t, err) + assert.Equal(t, testCaseCopy.expected, got) + } + + }) + } +} + +func TestAllowedSCMProvider(t *testing.T) { + cases := []struct { + name string + providerConfig *argoprojiov1alpha1.SCMProviderGenerator + expectedError error + }{ + { + name: "Error Github", + providerConfig: &argoprojiov1alpha1.SCMProviderGenerator{ + Github: &argoprojiov1alpha1.SCMProviderGeneratorGithub{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + expectedError: &ErrDisallowedSCMProvider{}, + }, + { + name: "Error Gitlab", + providerConfig: &argoprojiov1alpha1.SCMProviderGenerator{ + Gitlab: &argoprojiov1alpha1.SCMProviderGeneratorGitlab{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + expectedError: &ErrDisallowedSCMProvider{}, + }, + { + name: "Error Gitea", + providerConfig: &argoprojiov1alpha1.SCMProviderGenerator{ + Gitea: &argoprojiov1alpha1.SCMProviderGeneratorGitea{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + expectedError: &ErrDisallowedSCMProvider{}, + }, + { + name: "Error Bitbucket", + providerConfig: &argoprojiov1alpha1.SCMProviderGenerator{ + BitbucketServer: &argoprojiov1alpha1.SCMProviderGeneratorBitbucketServer{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + expectedError: &ErrDisallowedSCMProvider{}, + }, + { + name: "Error AzureDevops", + providerConfig: &argoprojiov1alpha1.SCMProviderGenerator{ + AzureDevOps: &argoprojiov1alpha1.SCMProviderGeneratorAzureDevOps{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + expectedError: &ErrDisallowedSCMProvider{}, + }, + } + + for _, testCase := range cases { + testCaseCopy := testCase + + t.Run(testCaseCopy.name, func(t *testing.T) { + t.Parallel() + + scmGenerator := &SCMProviderGenerator{ + allowedSCMProviders: []string{ + "github.myorg.com", + "gitlab.myorg.com", + "gitea.myorg.com", + "bitbucket.myorg.com", + "azuredevops.myorg.com", + }, + enableSCMProviders: true, + } + + applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "set", + }, + Spec: argoprojiov1alpha1.ApplicationSetSpec{ + Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{ + SCMProvider: testCaseCopy.providerConfig, + }}, + }, + } + + _, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) + + assert.Error(t, err, "Must return an error") + assert.ErrorAs(t, err, testCaseCopy.expectedError) + }) + } +} + +func TestSCMProviderDisabled_SCMGenerator(t *testing.T) { + generator := &SCMProviderGenerator{enableSCMProviders: false} + + applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "set", + }, + Spec: argoprojiov1alpha1.ApplicationSetSpec{ + Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{ + SCMProvider: &argoprojiov1alpha1.SCMProviderGenerator{ + Github: &argoprojiov1alpha1.SCMProviderGeneratorGithub{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + }}, + }, + } + + _, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) + assert.ErrorIs(t, err, ErrSCMProvidersDisabled) } diff --git a/applicationset/generators/scm_utils.go b/applicationset/generators/scm_utils.go new file mode 100644 index 0000000000000..51ac99d9b7e49 --- /dev/null +++ b/applicationset/generators/scm_utils.go @@ -0,0 +1,5 @@ +package generators + +type SCMGeneratorWithCustomApiUrl interface { + CustomApiUrl() string +} diff --git a/applicationset/generators/value_interpolation.go b/applicationset/generators/value_interpolation.go new file mode 100644 index 0000000000000..05a078d42f782 --- /dev/null +++ b/applicationset/generators/value_interpolation.go @@ -0,0 +1,43 @@ +package generators + +import ( + "fmt" +) + +func appendTemplatedValues(values map[string]string, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) error { + // We create a local map to ensure that we do not fall victim to a billion-laughs attack. We iterate through the + // cluster values map and only replace values in said map if it has already been allowlisted in the params map. + // Once we iterate through all the cluster values we can then safely merge the `tmp` map into the main params map. + tmp := map[string]interface{}{} + + for key, value := range values { + result, err := replaceTemplatedString(value, params, useGoTemplate, goTemplateOptions) + + if err != nil { + return fmt.Errorf("failed to replace templated string: %w", err) + } + + if useGoTemplate { + if tmp["values"] == nil { + tmp["values"] = map[string]string{} + } + tmp["values"].(map[string]string)[key] = result + } else { + tmp[fmt.Sprintf("values.%s", key)] = result + } + } + + for key, value := range tmp { + params[key] = value + } + + return nil +} + +func replaceTemplatedString(value string, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) { + replacedTmplStr, err := render.Replace(value, params, useGoTemplate, goTemplateOptions) + if err != nil { + return "", fmt.Errorf("failed to replace templated string with rendered values: %w", err) + } + return replacedTmplStr, nil +} diff --git a/applicationset/generators/value_interpolation_test.go b/applicationset/generators/value_interpolation_test.go new file mode 100644 index 0000000000000..8aa57dc0c0e65 --- /dev/null +++ b/applicationset/generators/value_interpolation_test.go @@ -0,0 +1,125 @@ +package generators + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValueInterpolation(t *testing.T) { + testCases := []struct { + name string + values map[string]string + params map[string]interface{} + expected map[string]interface{} + }{ + { + name: "Simple interpolation", + values: map[string]string{ + "hello": "{{ world }}", + }, + params: map[string]interface{}{ + "world": "world!", + }, + expected: map[string]interface{}{ + "world": "world!", + "values.hello": "world!", + }, + }, + { + name: "Non-existent", + values: map[string]string{ + "non-existent": "{{ non-existent }}", + }, + params: map[string]interface{}{}, + expected: map[string]interface{}{ + "values.non-existent": "{{ non-existent }}", + }, + }, + { + name: "Billion laughs", + values: map[string]string{ + "lol1": "lol", + "lol2": "{{values.lol1}}{{values.lol1}}", + "lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", + }, + params: map[string]interface{}{}, + expected: map[string]interface{}{ + "values.lol1": "lol", + "values.lol2": "{{values.lol1}}{{values.lol1}}", + "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", + }, + }, + } + + for _, testCase := range testCases { + + t.Run(testCase.name, func(t *testing.T) { + err := appendTemplatedValues(testCase.values, testCase.params, false, nil) + assert.NoError(t, err) + assert.EqualValues(t, testCase.expected, testCase.params) + }) + } +} + +func TestValueInterpolationWithGoTemplating(t *testing.T) { + testCases := []struct { + name string + values map[string]string + params map[string]interface{} + expected map[string]interface{} + }{ + { + name: "Simple interpolation", + values: map[string]string{ + "hello": "{{ .world }}", + }, + params: map[string]interface{}{ + "world": "world!", + }, + expected: map[string]interface{}{ + "world": "world!", + "values": map[string]string{ + "hello": "world!", + }, + }, + }, + { + name: "Non-existent to default", + values: map[string]string{ + "non_existent": "{{ default \"bar\" .non_existent }}", + }, + params: map[string]interface{}{}, + expected: map[string]interface{}{ + "values": map[string]string{ + "non_existent": "bar", + }, + }, + }, + { + name: "Billion laughs", + values: map[string]string{ + "lol1": "lol", + "lol2": "{{.values.lol1}}{{.values.lol1}}", + "lol3": "{{.values.lol2}}{{.values.lol2}}{{.values.lol2}}", + }, + params: map[string]interface{}{}, + expected: map[string]interface{}{ + "values": map[string]string{ + "lol1": "lol", + "lol2": "", + "lol3": "", + }, + }, + }, + } + + for _, testCase := range testCases { + + t.Run(testCase.name, func(t *testing.T) { + err := appendTemplatedValues(testCase.values, testCase.params, true, nil) + assert.NoError(t, err) + assert.EqualValues(t, testCase.expected, testCase.params) + }) + } +} diff --git a/applicationset/services/internal/http/client.go b/applicationset/services/internal/http/client.go new file mode 100644 index 0000000000000..00bcf32f3204f --- /dev/null +++ b/applicationset/services/internal/http/client.go @@ -0,0 +1,161 @@ +package http + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "time" +) + +const ( + userAgent = "argocd-applicationset" + defaultTimeout = 30 +) + +type Client struct { + // URL is the URL used for API requests. + baseURL string + + // UserAgent is the user agent to include in HTTP requests. + UserAgent string + + // Token is used to make authenticated API calls. + token string + + // Client is an HTTP client used to communicate with the API. + client *http.Client +} + +type ErrorResponse struct { + Body []byte + Response *http.Response + Message string +} + +func NewClient(baseURL string, options ...ClientOptionFunc) (*Client, error) { + client, err := newClient(baseURL, options...) + if err != nil { + return nil, err + } + return client, nil +} + +func newClient(baseURL string, options ...ClientOptionFunc) (*Client, error) { + c := &Client{baseURL: baseURL, UserAgent: userAgent} + + // Configure the HTTP client. + c.client = &http.Client{ + Timeout: time.Duration(defaultTimeout) * time.Second, + } + + // Apply any given client options. + for _, fn := range options { + if fn == nil { + continue + } + if err := fn(c); err != nil { + return nil, err + } + } + + return c, nil +} + +func (c *Client) NewRequest(method, path string, body interface{}, options []ClientOptionFunc) (*http.Request, error) { + + // Make sure the given URL end with a slash + if !strings.HasSuffix(c.baseURL, "/") { + c.baseURL += "/" + } + + var buf io.ReadWriter + if body != nil { + buf = &bytes.Buffer{} + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + err := enc.Encode(body) + if err != nil { + return nil, err + } + } + + req, err := http.NewRequest(method, c.baseURL+path, buf) + if err != nil { + return nil, err + } + + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + + if len(c.token) != 0 { + req.Header.Set("Authorization", "Bearer "+c.token) + } + + if c.UserAgent != "" { + req.Header.Set("User-Agent", c.UserAgent) + } + + return req, nil +} + +func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*http.Response, error) { + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + if err := CheckResponse(resp); err != nil { + return resp, err + } + + switch v := v.(type) { + case nil: + case io.Writer: + _, err = io.Copy(v, resp.Body) + default: + buf := new(bytes.Buffer) + teeReader := io.TeeReader(resp.Body, buf) + decErr := json.NewDecoder(teeReader).Decode(v) + if decErr == io.EOF { + decErr = nil // ignore EOF errors caused by empty response body + } + if decErr != nil { + err = fmt.Errorf("%s: %s", decErr.Error(), buf.String()) + } + } + return resp, err +} + +// CheckResponse checks the API response for errors, and returns them if present. +func CheckResponse(resp *http.Response) error { + + if c := resp.StatusCode; 200 <= c && c <= 299 { + return nil + } + + data, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("API error with status code %d: %v", resp.StatusCode, err) + } + + var raw map[string]interface{} + if err := json.Unmarshal(data, &raw); err != nil { + return fmt.Errorf("API error with status code %d: %s", resp.StatusCode, string(data)) + } + + message := "" + if value, ok := raw["message"].(string); ok { + message = value + } else if value, ok := raw["error"].(string); ok { + message = value + } + + return fmt.Errorf("API error with status code %d: %s", resp.StatusCode, message) +} diff --git a/applicationset/services/internal/http/client_options.go b/applicationset/services/internal/http/client_options.go new file mode 100644 index 0000000000000..ec388c9a80605 --- /dev/null +++ b/applicationset/services/internal/http/client_options.go @@ -0,0 +1,22 @@ +package http + +import "time" + +// ClientOptionFunc can be used to customize a new Restful API client. +type ClientOptionFunc func(*Client) error + +// WithToken is an option for NewClient to set token +func WithToken(token string) ClientOptionFunc { + return func(c *Client) error { + c.token = token + return nil + } +} + +// WithTimeout can be used to configure a custom timeout for requests. +func WithTimeout(timeout int) ClientOptionFunc { + return func(c *Client) error { + c.client.Timeout = time.Duration(timeout) * time.Second + return nil + } +} diff --git a/applicationset/services/internal/http/client_test.go b/applicationset/services/internal/http/client_test.go new file mode 100644 index 0000000000000..ca2c916177fee --- /dev/null +++ b/applicationset/services/internal/http/client_test.go @@ -0,0 +1,163 @@ +package http + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestClient(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte("Hello, World!")) + if err != nil { + assert.NoError(t, fmt.Errorf("Error Write %v", err)) + } + })) + defer server.Close() + + var clientOptionFns []ClientOptionFunc + _, err := NewClient(server.URL, clientOptionFns...) + + if err != nil { + t.Fatalf("Failed to create client: %v", err) + } +} + +func TestClientDo(t *testing.T) { + ctx := context.Background() + + for _, c := range []struct { + name string + params map[string]string + content []byte + fakeServer *httptest.Server + clientOptionFns []ClientOptionFunc + expected []map[string]interface{} + expectedCode int + expectedError error + }{ + { + name: "Simple", + params: map[string]string{ + "pkey1": "val1", + "pkey2": "val2", + }, + fakeServer: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`[{ + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123 + }]`)) + if err != nil { + assert.NoError(t, fmt.Errorf("Error Write %v", err)) + } + })), + clientOptionFns: nil, + expected: []map[string]interface{}{ + { + "key1": "val1", + "key2": map[string]interface{}{ + "key2_1": "val2_1", + "key2_2": map[string]interface{}{ + "key2_2_1": "val2_2_1", + }, + }, + "key3": float64(123), + }, + }, + expectedCode: 200, + expectedError: nil, + }, + { + name: "With Token", + params: map[string]string{ + "pkey1": "val1", + "pkey2": "val2", + }, + fakeServer: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + authHeader := r.Header.Get("Authorization") + if authHeader != "Bearer "+string("test-token") { + w.WriteHeader(http.StatusUnauthorized) + return + } + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`[{ + "key1": "val1", + "key2": { + "key2_1": "val2_1", + "key2_2": { + "key2_2_1": "val2_2_1" + } + }, + "key3": 123 + }]`)) + if err != nil { + assert.NoError(t, fmt.Errorf("Error Write %v", err)) + } + })), + clientOptionFns: nil, + expected: []map[string]interface{}(nil), + expectedCode: 401, + expectedError: fmt.Errorf("API error with status code 401: "), + }, + } { + cc := c + t.Run(cc.name, func(t *testing.T) { + defer cc.fakeServer.Close() + + client, err := NewClient(cc.fakeServer.URL, cc.clientOptionFns...) + + if err != nil { + t.Fatalf("NewClient returned unexpected error: %v", err) + } + + req, err := client.NewRequest("POST", "", cc.params, nil) + + if err != nil { + t.Fatalf("NewRequest returned unexpected error: %v", err) + } + + var data []map[string]interface{} + + resp, err := client.Do(ctx, req, &data) + + if cc.expectedError != nil { + assert.EqualError(t, err, cc.expectedError.Error()) + } else { + assert.Equal(t, resp.StatusCode, cc.expectedCode) + assert.Equal(t, data, cc.expected) + assert.NoError(t, err) + } + }) + } +} + +func TestCheckResponse(t *testing.T) { + resp := &http.Response{ + StatusCode: http.StatusBadRequest, + Body: io.NopCloser(bytes.NewBufferString(`{"error":"invalid_request","description":"Invalid token"}`)), + } + + err := CheckResponse(resp) + if err == nil { + t.Error("Expected an error, got nil") + } + + expected := "API error with status code 400: invalid_request" + if err.Error() != expected { + t.Errorf("Expected error '%s', got '%s'", expected, err.Error()) + } +} diff --git a/applicationset/services/mocks/Repos.go b/applicationset/services/mocks/Repos.go new file mode 100644 index 0000000000000..b7620b22f08bb --- /dev/null +++ b/applicationset/services/mocks/Repos.go @@ -0,0 +1,81 @@ +// Code generated by mockery v2.25.1. DO NOT EDIT. + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" +) + +// Repos is an autogenerated mock type for the Repos type +type Repos struct { + mock.Mock +} + +// GetDirectories provides a mock function with given fields: ctx, repoURL, revision, noRevisionCache +func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error) { + ret := _m.Called(ctx, repoURL, revision, noRevisionCache) + + var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, bool) ([]string, error)); ok { + return rf(ctx, repoURL, revision, noRevisionCache) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, bool) []string); ok { + r0 = rf(ctx, repoURL, revision, noRevisionCache) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, bool) error); ok { + r1 = rf(ctx, repoURL, revision, noRevisionCache) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetFiles provides a mock function with given fields: ctx, repoURL, revision, pattern, noRevisionCache +func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error) { + ret := _m.Called(ctx, repoURL, revision, pattern, noRevisionCache) + + var r0 map[string][]byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool) (map[string][]byte, error)); ok { + return rf(ctx, repoURL, revision, pattern, noRevisionCache) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool) map[string][]byte); ok { + r0 = rf(ctx, repoURL, revision, pattern, noRevisionCache) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string][]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, bool) error); ok { + r1 = rf(ctx, repoURL, revision, pattern, noRevisionCache) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewRepos interface { + mock.TestingT + Cleanup(func()) +} + +// NewRepos creates a new instance of Repos. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewRepos(t mockConstructorTestingTNewRepos) *Repos { + mock := &Repos{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/applicationset/services/mocks/RepositoryDB.go b/applicationset/services/mocks/RepositoryDB.go new file mode 100644 index 0000000000000..9d6240d342776 --- /dev/null +++ b/applicationset/services/mocks/RepositoryDB.go @@ -0,0 +1,57 @@ +// Code generated by mockery v2.21.1. DO NOT EDIT. + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + + v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +// RepositoryDB is an autogenerated mock type for the RepositoryDB type +type RepositoryDB struct { + mock.Mock +} + +// GetRepository provides a mock function with given fields: ctx, url +func (_m *RepositoryDB) GetRepository(ctx context.Context, url string) (*v1alpha1.Repository, error) { + ret := _m.Called(ctx, url) + + var r0 *v1alpha1.Repository + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*v1alpha1.Repository, error)); ok { + return rf(ctx, url) + } + if rf, ok := ret.Get(0).(func(context.Context, string) *v1alpha1.Repository); ok { + r0 = rf(ctx, url) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1alpha1.Repository) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, url) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewRepositoryDB interface { + mock.TestingT + Cleanup(func()) +} + +// NewRepositoryDB creates a new instance of RepositoryDB. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewRepositoryDB(t mockConstructorTestingTNewRepositoryDB) *RepositoryDB { + mock := &RepositoryDB{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/applicationset/services/plugin/plugin_service.go b/applicationset/services/plugin/plugin_service.go new file mode 100644 index 0000000000000..95573e0942407 --- /dev/null +++ b/applicationset/services/plugin/plugin_service.go @@ -0,0 +1,73 @@ +package plugin + +import ( + "context" + "fmt" + "net/http" + + internalhttp "github.com/argoproj/argo-cd/v2/applicationset/services/internal/http" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +// ServiceRequest is the request object sent to the plugin service. +type ServiceRequest struct { + // ApplicationSetName is the appSetName of the ApplicationSet for which we're requesting parameters. Useful for logging in + // the plugin service. + ApplicationSetName string `json:"applicationSetName"` + // Input is the map of parameters set in the ApplicationSet spec for this generator. + Input v1alpha1.PluginInput `json:"input"` +} + +type Output struct { + // Parameters is the list of parameter sets returned by the plugin. + Parameters []map[string]interface{} `json:"parameters"` +} + +// ServiceResponse is the response object returned by the plugin service. +type ServiceResponse struct { + // Output is the map of outputs returned by the plugin. + Output Output `json:"output"` +} + +type Service struct { + client *internalhttp.Client + appSetName string +} + +func NewPluginService(ctx context.Context, appSetName string, baseURL string, token string, requestTimeout int) (*Service, error) { + var clientOptionFns []internalhttp.ClientOptionFunc + + clientOptionFns = append(clientOptionFns, internalhttp.WithToken(token)) + + if requestTimeout != 0 { + clientOptionFns = append(clientOptionFns, internalhttp.WithTimeout(requestTimeout)) + } + + client, err := internalhttp.NewClient(baseURL, clientOptionFns...) + if err != nil { + return nil, fmt.Errorf("error creating plugin client: %v", err) + } + + return &Service{ + client: client, + appSetName: appSetName, + }, nil +} + +func (p *Service) List(ctx context.Context, parameters v1alpha1.PluginParameters) (*ServiceResponse, error) { + req, err := p.client.NewRequest(http.MethodPost, "api/v1/getparams.execute", ServiceRequest{ApplicationSetName: p.appSetName, Input: v1alpha1.PluginInput{Parameters: parameters}}, nil) + + if err != nil { + return nil, fmt.Errorf("NewRequest returned unexpected error: %v", err) + } + + var data ServiceResponse + + _, err = p.client.Do(ctx, req, &data) + + if err != nil { + return nil, fmt.Errorf("error get api '%s': %v", p.appSetName, err) + } + + return &data, err +} diff --git a/applicationset/services/plugin/plugin_service_test.go b/applicationset/services/plugin/plugin_service_test.go new file mode 100644 index 0000000000000..6dc81d33df71f --- /dev/null +++ b/applicationset/services/plugin/plugin_service_test.go @@ -0,0 +1,52 @@ +package plugin + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPlugin(t *testing.T) { + expectedJSON := `{"parameters": [{"number":123,"digest":"sha256:942ae2dfd73088b54d7151a3c3fd5af038a51c50029bfcfd21f1e650d9579967"},{"number":456,"digest":"sha256:224e68cc69566e5cbbb76034b3c42cd2ed57c1a66720396e1c257794cb7d68c1"}]}` + token := "0bc57212c3cbbec69d20b34c507284bd300def5b" + + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + authHeader := r.Header.Get("Authorization") + if authHeader != "Bearer "+token { + w.WriteHeader(http.StatusUnauthorized) + return + } + _, err := w.Write([]byte(expectedJSON)) + + if err != nil { + assert.NoError(t, fmt.Errorf("Error Write %v", err)) + } + }) + ts := httptest.NewServer(handler) + defer ts.Close() + + client, err := NewPluginService(context.Background(), "plugin-test", ts.URL, token, 0) + + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + data, err := client.List(context.Background(), nil) + + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + var expectedData ServiceResponse + err = json.Unmarshal([]byte(expectedJSON), &expectedData) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, &expectedData, data) +} diff --git a/applicationset/services/plugin/utils.go b/applicationset/services/plugin/utils.go new file mode 100644 index 0000000000000..26e38e492200d --- /dev/null +++ b/applicationset/services/plugin/utils.go @@ -0,0 +1,21 @@ +package plugin + +import ( + "fmt" + "strings" + + "github.com/argoproj/argo-cd/v2/common" +) + +// ParseSecretKey retrieves secret appSetName if different from common ArgoCDSecretName. +func ParseSecretKey(key string) (secretName string, tokenKey string) { + if strings.Contains(key, ":") { + parts := strings.Split(key, ":") + secretName = parts[0][1:] + tokenKey = fmt.Sprintf("$%s", parts[1]) + } else { + secretName = common.ArgoCDSecretName + tokenKey = key + } + return secretName, tokenKey +} diff --git a/applicationset/services/plugin/utils_test.go b/applicationset/services/plugin/utils_test.go new file mode 100644 index 0000000000000..c364d606392e4 --- /dev/null +++ b/applicationset/services/plugin/utils_test.go @@ -0,0 +1,17 @@ +package plugin + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseSecretKey(t *testing.T) { + secretName, tokenKey := ParseSecretKey("#my-secret:my-token") + assert.Equal(t, "my-secret", secretName) + assert.Equal(t, "$my-token", tokenKey) + + secretName, tokenKey = ParseSecretKey("#my-secret") + assert.Equal(t, "argocd-secret", secretName) + assert.Equal(t, "#my-secret", tokenKey) +} diff --git a/applicationset/services/pull_request/azure_devops.go b/applicationset/services/pull_request/azure_devops.go new file mode 100644 index 0000000000000..9090b829ca0c2 --- /dev/null +++ b/applicationset/services/pull_request/azure_devops.go @@ -0,0 +1,145 @@ +package pull_request + +import ( + "context" + "fmt" + "strings" + + "github.com/microsoft/azure-devops-go-api/azuredevops" + core "github.com/microsoft/azure-devops-go-api/azuredevops/core" + git "github.com/microsoft/azure-devops-go-api/azuredevops/git" +) + +const AZURE_DEVOPS_DEFAULT_URL = "https://dev.azure.com" + +type AzureDevOpsClientFactory interface { + // Returns an Azure Devops Client interface. + GetClient(ctx context.Context) (git.Client, error) +} + +type devopsFactoryImpl struct { + connection *azuredevops.Connection +} + +func (factory *devopsFactoryImpl) GetClient(ctx context.Context) (git.Client, error) { + gitClient, err := git.NewClient(ctx, factory.connection) + if err != nil { + return nil, fmt.Errorf("failed to get new Azure DevOps git client for pull request generator: %w", err) + } + return gitClient, nil +} + +type AzureDevOpsService struct { + clientFactory AzureDevOpsClientFactory + project string + repo string + labels []string +} + +var _ PullRequestService = (*AzureDevOpsService)(nil) +var _ AzureDevOpsClientFactory = &devopsFactoryImpl{} + +func NewAzureDevOpsService(ctx context.Context, token, url, organization, project, repo string, labels []string) (PullRequestService, error) { + organizationUrl := buildURL(url, organization) + + var connection *azuredevops.Connection + if token == "" { + connection = azuredevops.NewAnonymousConnection(organizationUrl) + } else { + connection = azuredevops.NewPatConnection(organizationUrl, token) + } + + return &AzureDevOpsService{ + clientFactory: &devopsFactoryImpl{connection: connection}, + project: project, + repo: repo, + labels: labels, + }, nil +} + +func (a *AzureDevOpsService) List(ctx context.Context) ([]*PullRequest, error) { + client, err := a.clientFactory.GetClient(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get Azure DevOps client: %w", err) + } + + args := git.GetPullRequestsByProjectArgs{ + Project: &a.project, + SearchCriteria: &git.GitPullRequestSearchCriteria{}, + } + + azurePullRequests, err := client.GetPullRequestsByProject(ctx, args) + if err != nil { + return nil, fmt.Errorf("failed to get pull requests by project: %w", err) + } + + pullRequests := []*PullRequest{} + + for _, pr := range *azurePullRequests { + if pr.Repository == nil || + pr.Repository.Name == nil || + pr.PullRequestId == nil || + pr.SourceRefName == nil || + pr.LastMergeSourceCommit == nil || + pr.LastMergeSourceCommit.CommitId == nil { + continue + } + + azureDevOpsLabels := convertLabels(pr.Labels) + if !containAzureDevOpsLabels(a.labels, azureDevOpsLabels) { + continue + } + + if *pr.Repository.Name == a.repo { + pullRequests = append(pullRequests, &PullRequest{ + Number: *pr.PullRequestId, + Branch: strings.Replace(*pr.SourceRefName, "refs/heads/", "", 1), + HeadSHA: *pr.LastMergeSourceCommit.CommitId, + Labels: azureDevOpsLabels, + }) + } + } + + return pullRequests, nil +} + +// convertLabels converts WebApiTagDefinitions to strings +func convertLabels(tags *[]core.WebApiTagDefinition) []string { + if tags == nil { + return []string{} + } + labelStrings := make([]string, len(*tags)) + for i, label := range *tags { + labelStrings[i] = *label.Name + } + return labelStrings +} + +// containAzureDevOpsLabels returns true if gotLabels contains expectedLabels +func containAzureDevOpsLabels(expectedLabels []string, gotLabels []string) bool { + for _, expected := range expectedLabels { + found := false + for _, got := range gotLabels { + if expected == got { + found = true + break + } + } + if !found { + return false + } + } + return true +} + +func buildURL(url, organization string) string { + if url == "" { + url = AZURE_DEVOPS_DEFAULT_URL + } + separator := "" + if !strings.HasSuffix(url, "/") { + separator = "/" + } + devOpsURL := fmt.Sprintf("%s%s%s", url, separator, organization) + return devOpsURL +} diff --git a/applicationset/services/pull_request/azure_devops_test.go b/applicationset/services/pull_request/azure_devops_test.go new file mode 100644 index 0000000000000..5ed8f4de78b9d --- /dev/null +++ b/applicationset/services/pull_request/azure_devops_test.go @@ -0,0 +1,221 @@ +package pull_request + +import ( + "context" + "testing" + + "github.com/microsoft/azure-devops-go-api/azuredevops/core" + git "github.com/microsoft/azure-devops-go-api/azuredevops/git" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + azureMock "github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider/azure_devops/git/mocks" +) + +func createBoolPtr(x bool) *bool { + return &x +} + +func createStringPtr(x string) *string { + return &x +} + +func createIntPtr(x int) *int { + return &x +} + +func createLabelsPtr(x []core.WebApiTagDefinition) *[]core.WebApiTagDefinition { + return &x +} + +type AzureClientFactoryMock struct { + mock *mock.Mock +} + +func (m *AzureClientFactoryMock) GetClient(ctx context.Context) (git.Client, error) { + args := m.mock.Called(ctx) + + var client git.Client + c := args.Get(0) + if c != nil { + client = c.(git.Client) + } + + var err error + if len(args) > 1 { + if e, ok := args.Get(1).(error); ok { + err = e + } + } + + return client, err +} + +func TestListPullRequest(t *testing.T) { + teamProject := "myorg_project" + repoName := "myorg_project_repo" + pr_id := 123 + pr_head_sha := "cd4973d9d14a08ffe6b641a89a68891d6aac8056" + ctx := context.Background() + + pullRequestMock := []git.GitPullRequest{ + { + PullRequestId: createIntPtr(pr_id), + SourceRefName: createStringPtr("refs/heads/feature-branch"), + LastMergeSourceCommit: &git.GitCommitRef{ + CommitId: createStringPtr(pr_head_sha), + }, + Labels: &[]core.WebApiTagDefinition{}, + Repository: &git.GitRepository{ + Name: createStringPtr(repoName), + }, + }, + } + + args := git.GetPullRequestsByProjectArgs{ + Project: &teamProject, + SearchCriteria: &git.GitPullRequestSearchCriteria{}, + } + + gitClientMock := azureMock.Client{} + clientFactoryMock := &AzureClientFactoryMock{mock: &mock.Mock{}} + clientFactoryMock.mock.On("GetClient", mock.Anything).Return(&gitClientMock, nil) + gitClientMock.On("GetPullRequestsByProject", ctx, args).Return(&pullRequestMock, nil) + + provider := AzureDevOpsService{ + clientFactory: clientFactoryMock, + project: teamProject, + repo: repoName, + labels: nil, + } + + list, err := provider.List(ctx) + assert.NoError(t, err) + assert.Equal(t, 1, len(list)) + assert.Equal(t, "feature-branch", list[0].Branch) + assert.Equal(t, pr_head_sha, list[0].HeadSHA) + assert.Equal(t, pr_id, list[0].Number) +} + +func TestConvertLabes(t *testing.T) { + testCases := []struct { + name string + gotLabels *[]core.WebApiTagDefinition + expectedLabels []string + }{ + { + name: "empty labels", + gotLabels: createLabelsPtr([]core.WebApiTagDefinition{}), + expectedLabels: []string{}, + }, + { + name: "nil labels", + gotLabels: createLabelsPtr(nil), + expectedLabels: []string{}, + }, + { + name: "one label", + gotLabels: createLabelsPtr([]core.WebApiTagDefinition{ + {Name: createStringPtr("label1"), Active: createBoolPtr(true)}, + }), + expectedLabels: []string{"label1"}, + }, + { + name: "two label", + gotLabels: createLabelsPtr([]core.WebApiTagDefinition{ + {Name: createStringPtr("label1"), Active: createBoolPtr(true)}, + {Name: createStringPtr("label2"), Active: createBoolPtr(true)}, + }), + expectedLabels: []string{"label1", "label2"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got := convertLabels(tc.gotLabels) + assert.Equal(t, tc.expectedLabels, got) + }) + } +} + +func TestContainAzureDevOpsLabels(t *testing.T) { + testCases := []struct { + name string + expectedLabels []string + gotLabels []string + expectedResult bool + }{ + { + name: "empty labels", + expectedLabels: []string{}, + gotLabels: []string{}, + expectedResult: true, + }, + { + name: "no matching labels", + expectedLabels: []string{"label1", "label2"}, + gotLabels: []string{"label3", "label4"}, + expectedResult: false, + }, + { + name: "some matching labels", + expectedLabels: []string{"label1", "label2"}, + gotLabels: []string{"label1", "label3"}, + expectedResult: false, + }, + { + name: "all matching labels", + expectedLabels: []string{"label1", "label2"}, + gotLabels: []string{"label1", "label2"}, + expectedResult: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got := containAzureDevOpsLabels(tc.expectedLabels, tc.gotLabels) + assert.Equal(t, tc.expectedResult, got) + }) + } +} + +func TestBuildURL(t *testing.T) { + testCases := []struct { + name string + url string + organization string + expected string + }{ + { + name: "Provided default URL and organization", + url: "https://dev.azure.com/", + organization: "myorganization", + expected: "https://dev.azure.com/myorganization", + }, + { + name: "Provided default URL and organization without trailing slash", + url: "https://dev.azure.com", + organization: "myorganization", + expected: "https://dev.azure.com/myorganization", + }, + { + name: "Provided no URL and organization", + url: "", + organization: "myorganization", + expected: "https://dev.azure.com/myorganization", + }, + { + name: "Provided custom URL and organization", + url: "https://azuredevops.example.com/", + organization: "myorganization", + expected: "https://azuredevops.example.com/myorganization", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := buildURL(tc.url, tc.organization) + assert.Equal(t, result, tc.expected) + }) + } +} diff --git a/applicationset/services/pull_request/bitbucket_cloud.go b/applicationset/services/pull_request/bitbucket_cloud.go new file mode 100644 index 0000000000000..5d5f8208f9b06 --- /dev/null +++ b/applicationset/services/pull_request/bitbucket_cloud.go @@ -0,0 +1,138 @@ +package pull_request + +import ( + "context" + "encoding/json" + "fmt" + "net/url" + + "github.com/ktrysmt/go-bitbucket" +) + +type BitbucketCloudService struct { + client *bitbucket.Client + owner string + repositorySlug string +} + +type BitbucketCloudPullRequest struct { + ID int `json:"id"` + Source BitbucketCloudPullRequestSource `json:"source"` +} + +type BitbucketCloudPullRequestSource struct { + Branch BitbucketCloudPullRequestSourceBranch `json:"branch"` + Commit BitbucketCloudPullRequestSourceCommit `json:"commit"` +} + +type BitbucketCloudPullRequestSourceBranch struct { + Name string `json:"name"` +} + +type BitbucketCloudPullRequestSourceCommit struct { + Hash string `json:"hash"` +} + +type PullRequestResponse struct { + Page int32 `json:"page"` + Size int32 `json:"size"` + Pagelen int32 `json:"pagelen"` + Next string `json:"next"` + Previous string `json:"previous"` + Items []PullRequest `json:"values"` +} + +var _ PullRequestService = (*BitbucketCloudService)(nil) + +func parseUrl(uri string) (*url.URL, error) { + if uri == "" { + uri = "https://api.bitbucket.org/2.0" + } + + url, err := url.Parse(uri) + if err != nil { + return nil, err + } + + return url, nil +} + +func NewBitbucketCloudServiceBasicAuth(baseUrl, username, password, owner, repositorySlug string) (PullRequestService, error) { + url, err := parseUrl(baseUrl) + if err != nil { + return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %v", baseUrl, owner, repositorySlug, err) + } + + bitbucketClient := bitbucket.NewBasicAuth(username, password) + bitbucketClient.SetApiBaseURL(*url) + + return &BitbucketCloudService{ + client: bitbucketClient, + owner: owner, + repositorySlug: repositorySlug, + }, nil +} + +func NewBitbucketCloudServiceBearerToken(baseUrl, bearerToken, owner, repositorySlug string) (PullRequestService, error) { + url, err := parseUrl(baseUrl) + if err != nil { + return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %v", baseUrl, owner, repositorySlug, err) + } + + bitbucketClient := bitbucket.NewOAuthbearerToken(bearerToken) + bitbucketClient.SetApiBaseURL(*url) + + return &BitbucketCloudService{ + client: bitbucketClient, + owner: owner, + repositorySlug: repositorySlug, + }, nil +} + +func NewBitbucketCloudServiceNoAuth(baseUrl, owner, repositorySlug string) (PullRequestService, error) { + // There is currently no method to explicitly not require auth + return NewBitbucketCloudServiceBearerToken(baseUrl, "", owner, repositorySlug) +} + +func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error) { + opts := &bitbucket.PullRequestsOptions{ + Owner: b.owner, + RepoSlug: b.repositorySlug, + } + + response, err := b.client.Repositories.PullRequests.Gets(opts) + if err != nil { + return nil, fmt.Errorf("error listing pull requests for %s/%s: %v", b.owner, b.repositorySlug, err) + } + + resp, ok := response.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("unknown type returned from bitbucket pull requests") + } + + repoArray, ok := resp["values"].([]interface{}) + if !ok { + return nil, fmt.Errorf("unknown type returned from response values") + } + + jsonStr, err := json.Marshal(repoArray) + if err != nil { + return nil, fmt.Errorf("error marshalling response body to json: %v", err) + } + + var pulls []BitbucketCloudPullRequest + if err := json.Unmarshal(jsonStr, &pulls); err != nil { + return nil, fmt.Errorf("error unmarshalling json to type '[]BitbucketCloudPullRequest': %v", err) + } + + pullRequests := []*PullRequest{} + for _, pull := range pulls { + pullRequests = append(pullRequests, &PullRequest{ + Number: pull.ID, + Branch: pull.Source.Branch.Name, + HeadSHA: pull.Source.Commit.Hash, + }) + } + + return pullRequests, nil +} diff --git a/applicationset/services/pull_request/bitbucket_cloud_test.go b/applicationset/services/pull_request/bitbucket_cloud_test.go new file mode 100644 index 0000000000000..2f604c1fa9ccf --- /dev/null +++ b/applicationset/services/pull_request/bitbucket_cloud_test.go @@ -0,0 +1,410 @@ +package pull_request + +import ( + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +func defaultHandlerCloud(t *testing.T) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + var err error + switch r.RequestURI { + case "/repositories/OWNER/REPO/pullrequests/": + _, err = io.WriteString(w, `{ + "size": 1, + "pagelen": 10, + "page": 1, + "values": [ + { + "id": 101, + "source": { + "branch": { + "name": "feature/foo-bar" + }, + "commit": { + "type": "commit", + "hash": "1a8dd249c04a" + } + } + } + ] + }`) + default: + t.Fail() + } + if err != nil { + t.Fail() + } + } +} + +func TestParseUrlEmptyUrl(t *testing.T) { + url, err := parseUrl("") + bitbucketUrl, _ := url.Parse("https://api.bitbucket.org/2.0") + + assert.NoError(t, err) + assert.Equal(t, bitbucketUrl, url) +} + +func TestInvalidBaseUrlBasicAuthCloud(t *testing.T) { + _, err := NewBitbucketCloudServiceBasicAuth("http:// example.org", "user", "password", "OWNER", "REPO") + + assert.Error(t, err) +} + +func TestInvalidBaseUrlBearerTokenCloud(t *testing.T) { + _, err := NewBitbucketCloudServiceBearerToken("http:// example.org", "TOKEN", "OWNER", "REPO") + + assert.Error(t, err) +} + +func TestInvalidBaseUrlNoAuthCloud(t *testing.T) { + _, err := NewBitbucketCloudServiceNoAuth("http:// example.org", "OWNER", "REPO") + + assert.Error(t, err) +} + +func TestListPullRequestBearerTokenCloud(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "Bearer TOKEN", r.Header.Get("Authorization")) + defaultHandlerCloud(t)(w, r) + })) + defer ts.Close() + svc, err := NewBitbucketCloudServiceBearerToken(ts.URL, "TOKEN", "OWNER", "REPO") + assert.NoError(t, err) + pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) + assert.NoError(t, err) + assert.Equal(t, 1, len(pullRequests)) + assert.Equal(t, 101, pullRequests[0].Number) + assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch) + assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA) +} + +func TestListPullRequestNoAuthCloud(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Empty(t, r.Header.Get("Authorization")) + defaultHandlerCloud(t)(w, r) + })) + defer ts.Close() + svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") + assert.NoError(t, err) + pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) + assert.NoError(t, err) + assert.Equal(t, 1, len(pullRequests)) + assert.Equal(t, 101, pullRequests[0].Number) + assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch) + assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA) +} + +func TestListPullRequestBasicAuthCloud(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "Basic dXNlcjpwYXNzd29yZA==", r.Header.Get("Authorization")) + defaultHandlerCloud(t)(w, r) + })) + defer ts.Close() + svc, err := NewBitbucketCloudServiceBasicAuth(ts.URL, "user", "password", "OWNER", "REPO") + assert.NoError(t, err) + pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) + assert.NoError(t, err) + assert.Equal(t, 1, len(pullRequests)) + assert.Equal(t, 101, pullRequests[0].Number) + assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch) + assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA) +} + +func TestListPullRequestPaginationCloud(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + var err error + switch r.RequestURI { + case "/repositories/OWNER/REPO/pullrequests/": + _, err = io.WriteString(w, fmt.Sprintf(`{ + "size": 2, + "pagelen": 1, + "page": 1, + "next": "http://%s/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=2", + "values": [ + { + "id": 101, + "source": { + "branch": { + "name": "feature-101" + }, + "commit": { + "type": "commit", + "hash": "1a8dd249c04a" + } + } + }, + { + "id": 102, + "source": { + "branch": { + "name": "feature-102" + }, + "commit": { + "type": "commit", + "hash": "4cf807e67a6d" + } + } + } + ] + }`, r.Host)) + case "/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=2": + _, err = io.WriteString(w, fmt.Sprintf(`{ + "size": 2, + "pagelen": 1, + "page": 2, + "previous": "http://%s/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=1", + "values": [ + { + "id": 103, + "source": { + "branch": { + "name": "feature-103" + }, + "commit": { + "type": "commit", + "hash": "6344d9623e3b" + } + } + } + ] + }`, r.Host)) + default: + t.Fail() + } + if err != nil { + t.Fail() + } + })) + defer ts.Close() + svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") + assert.NoError(t, err) + pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) + assert.NoError(t, err) + assert.Equal(t, 3, len(pullRequests)) + assert.Equal(t, PullRequest{ + Number: 101, + Branch: "feature-101", + HeadSHA: "1a8dd249c04a", + }, *pullRequests[0]) + assert.Equal(t, PullRequest{ + Number: 102, + Branch: "feature-102", + HeadSHA: "4cf807e67a6d", + }, *pullRequests[1]) + assert.Equal(t, PullRequest{ + Number: 103, + Branch: "feature-103", + HeadSHA: "6344d9623e3b", + }, *pullRequests[2]) +} + +func TestListResponseErrorCloud(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(500) + })) + defer ts.Close() + svc, _ := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") + _, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) + assert.Error(t, err) +} + +func TestListResponseMalformedCloud(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + switch r.RequestURI { + case "/repositories/OWNER/REPO/pullrequests/": + _, err := io.WriteString(w, `[{ + "size": 1, + "pagelen": 10, + "page": 1, + "values": [{ "id": 101 }] + }]`) + if err != nil { + t.Fail() + } + default: + t.Fail() + } + })) + defer ts.Close() + svc, _ := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") + _, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) + assert.Error(t, err) +} + +func TestListResponseMalformedValuesCloud(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + switch r.RequestURI { + case "/repositories/OWNER/REPO/pullrequests/": + _, err := io.WriteString(w, `{ + "size": 1, + "pagelen": 10, + "page": 1, + "values": { "id": 101 } + }`) + if err != nil { + t.Fail() + } + default: + t.Fail() + } + })) + defer ts.Close() + svc, _ := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") + _, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) + assert.Error(t, err) +} + +func TestListResponseEmptyCloud(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + switch r.RequestURI { + case "/repositories/OWNER/REPO/pullrequests/": + _, err := io.WriteString(w, `{ + "size": 1, + "pagelen": 10, + "page": 1, + "values": [] + }`) + if err != nil { + t.Fail() + } + default: + t.Fail() + } + })) + defer ts.Close() + svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") + assert.NoError(t, err) + pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{}) + assert.NoError(t, err) + assert.Empty(t, pullRequests) +} + +func TestListPullRequestBranchMatchCloud(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + var err error + switch r.RequestURI { + case "/repositories/OWNER/REPO/pullrequests/": + _, err = io.WriteString(w, fmt.Sprintf(`{ + "size": 2, + "pagelen": 1, + "page": 1, + "next": "http://%s/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=2", + "values": [ + { + "id": 101, + "source": { + "branch": { + "name": "feature-101" + }, + "commit": { + "type": "commit", + "hash": "1a8dd249c04a" + } + } + }, + { + "id": 200, + "source": { + "branch": { + "name": "feature-200" + }, + "commit": { + "type": "commit", + "hash": "4cf807e67a6d" + } + } + } + ] + }`, r.Host)) + case "/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=2": + _, err = io.WriteString(w, fmt.Sprintf(`{ + "size": 2, + "pagelen": 1, + "page": 2, + "previous": "http://%s/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=1", + "values": [ + { + "id": 102, + "source": { + "branch": { + "name": "feature-102" + }, + "commit": { + "type": "commit", + "hash": "6344d9623e3b" + } + } + } + ] + }`, r.Host)) + default: + t.Fail() + } + if err != nil { + t.Fail() + } + })) + defer ts.Close() + regexp := `feature-1[\d]{2}` + svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") + assert.NoError(t, err) + pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{ + { + BranchMatch: ®exp, + }, + }) + assert.NoError(t, err) + assert.Equal(t, 2, len(pullRequests)) + assert.Equal(t, PullRequest{ + Number: 101, + Branch: "feature-101", + HeadSHA: "1a8dd249c04a", + }, *pullRequests[0]) + assert.Equal(t, PullRequest{ + Number: 102, + Branch: "feature-102", + HeadSHA: "6344d9623e3b", + }, *pullRequests[1]) + + regexp = `.*2$` + svc, err = NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") + assert.NoError(t, err) + pullRequests, err = ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{ + { + BranchMatch: ®exp, + }, + }) + assert.NoError(t, err) + assert.Equal(t, 1, len(pullRequests)) + assert.Equal(t, PullRequest{ + Number: 102, + Branch: "feature-102", + HeadSHA: "6344d9623e3b", + }, *pullRequests[0]) + + regexp = `[\d{2}` + svc, err = NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO") + assert.NoError(t, err) + _, err = ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{ + { + BranchMatch: ®exp, + }, + }) + assert.Error(t, err) +} diff --git a/applicationset/services/pull_request/bitbucket_server.go b/applicationset/services/pull_request/bitbucket_server.go index 72cd6dd7e1900..99665d163e1bc 100644 --- a/applicationset/services/pull_request/bitbucket_server.go +++ b/applicationset/services/pull_request/bitbucket_server.go @@ -66,10 +66,11 @@ func (b *BitbucketService) List(_ context.Context) ([]*PullRequest, error) { for _, pull := range pulls { pullRequests = append(pullRequests, &PullRequest{ - Number: pull.ID, - Branch: pull.FromRef.DisplayID, // ID: refs/heads/main DisplayID: main - HeadSHA: pull.FromRef.LatestCommit, // This is not defined in the official docs, but works in practice - Labels: []string{}, // Not supported by library + Number: pull.ID, + Branch: pull.FromRef.DisplayID, // ID: refs/heads/main DisplayID: main + TargetBranch: pull.ToRef.DisplayID, + HeadSHA: pull.FromRef.LatestCommit, // This is not defined in the official docs, but works in practice + Labels: []string{}, // Not supported by library }) } diff --git a/applicationset/services/pull_request/bitbucket_server_test.go b/applicationset/services/pull_request/bitbucket_server_test.go index 0204d2aefa8d4..911e3e7e0ccd0 100644 --- a/applicationset/services/pull_request/bitbucket_server_test.go +++ b/applicationset/services/pull_request/bitbucket_server_test.go @@ -24,6 +24,11 @@ func defaultHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { "values": [ { "id": 101, + "toRef": { + "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", + "displayId": "master", + "id": "refs/heads/master" + }, "fromRef": { "id": "refs/heads/feature-ABC-123", "displayId": "feature-ABC-123", @@ -55,6 +60,7 @@ func TestListPullRequestNoAuth(t *testing.T) { assert.Equal(t, 1, len(pullRequests)) assert.Equal(t, 101, pullRequests[0].Number) assert.Equal(t, "feature-ABC-123", pullRequests[0].Branch) + assert.Equal(t, "master", pullRequests[0].TargetBranch) assert.Equal(t, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA) } @@ -71,6 +77,11 @@ func TestListPullRequestPagination(t *testing.T) { "values": [ { "id": 101, + "toRef": { + "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", + "displayId": "master", + "id": "refs/heads/master" + }, "fromRef": { "id": "refs/heads/feature-101", "displayId": "feature-101", @@ -79,6 +90,11 @@ func TestListPullRequestPagination(t *testing.T) { }, { "id": 102, + "toRef": { + "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", + "displayId": "branch", + "id": "refs/heads/branch" + }, "fromRef": { "id": "refs/heads/feature-102", "displayId": "feature-102", @@ -96,6 +112,11 @@ func TestListPullRequestPagination(t *testing.T) { "values": [ { "id": 200, + "toRef": { + "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", + "displayId": "master", + "id": "refs/heads/master" + }, "fromRef": { "id": "refs/heads/feature-200", "displayId": "feature-200", @@ -119,22 +140,25 @@ func TestListPullRequestPagination(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 3, len(pullRequests)) assert.Equal(t, PullRequest{ - Number: 101, - Branch: "feature-101", - HeadSHA: "ab3cf2e4d1517c83e720d2585b9402dbef71f992", - Labels: []string{}, + Number: 101, + Branch: "feature-101", + TargetBranch: "master", + HeadSHA: "ab3cf2e4d1517c83e720d2585b9402dbef71f992", + Labels: []string{}, }, *pullRequests[0]) assert.Equal(t, PullRequest{ - Number: 102, - Branch: "feature-102", - HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992", - Labels: []string{}, + Number: 102, + Branch: "feature-102", + TargetBranch: "branch", + HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992", + Labels: []string{}, }, *pullRequests[1]) assert.Equal(t, PullRequest{ - Number: 200, - Branch: "feature-200", - HeadSHA: "cb3cf2e4d1517c83e720d2585b9402dbef71f992", - Labels: []string{}, + Number: 200, + Branch: "feature-200", + TargetBranch: "master", + HeadSHA: "cb3cf2e4d1517c83e720d2585b9402dbef71f992", + Labels: []string{}, }, *pullRequests[2]) } @@ -158,7 +182,7 @@ func TestListPullRequestBasicAuth(t *testing.T) { func TestListResponseError(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) })) defer ts.Close() svc, _ := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO") @@ -231,6 +255,11 @@ func TestListPullRequestBranchMatch(t *testing.T) { "values": [ { "id": 101, + "toRef": { + "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", + "displayId": "master", + "id": "refs/heads/master" + }, "fromRef": { "id": "refs/heads/feature-101", "displayId": "feature-101", @@ -239,6 +268,11 @@ func TestListPullRequestBranchMatch(t *testing.T) { }, { "id": 102, + "toRef": { + "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", + "displayId": "branch", + "id": "refs/heads/branch" + }, "fromRef": { "id": "refs/heads/feature-102", "displayId": "feature-102", @@ -256,6 +290,11 @@ func TestListPullRequestBranchMatch(t *testing.T) { "values": [ { "id": 200, + "toRef": { + "latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a", + "displayId": "master", + "id": "refs/heads/master" + }, "fromRef": { "id": "refs/heads/feature-200", "displayId": "feature-200", @@ -284,16 +323,18 @@ func TestListPullRequestBranchMatch(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 2, len(pullRequests)) assert.Equal(t, PullRequest{ - Number: 101, - Branch: "feature-101", - HeadSHA: "ab3cf2e4d1517c83e720d2585b9402dbef71f992", - Labels: []string{}, + Number: 101, + Branch: "feature-101", + TargetBranch: "master", + HeadSHA: "ab3cf2e4d1517c83e720d2585b9402dbef71f992", + Labels: []string{}, }, *pullRequests[0]) assert.Equal(t, PullRequest{ - Number: 102, - Branch: "feature-102", - HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992", - Labels: []string{}, + Number: 102, + Branch: "feature-102", + TargetBranch: "branch", + HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992", + Labels: []string{}, }, *pullRequests[1]) regexp = `.*2$` @@ -307,10 +348,11 @@ func TestListPullRequestBranchMatch(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 1, len(pullRequests)) assert.Equal(t, PullRequest{ - Number: 102, - Branch: "feature-102", - HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992", - Labels: []string{}, + Number: 102, + Branch: "feature-102", + TargetBranch: "branch", + HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992", + Labels: []string{}, }, *pullRequests[0]) regexp = `[\d{2}` diff --git a/applicationset/services/pull_request/gitea.go b/applicationset/services/pull_request/gitea.go index f913fb35580f5..ff385ff281c6d 100644 --- a/applicationset/services/pull_request/gitea.go +++ b/applicationset/services/pull_request/gitea.go @@ -26,11 +26,13 @@ func NewGiteaService(ctx context.Context, token, url, owner, repo string, insecu if insecure { cookieJar, _ := cookiejar.New(nil) + tr := http.DefaultTransport.(*http.Transport).Clone() + tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + httpClient = &http.Client{ - Jar: cookieJar, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }} + Jar: cookieJar, + Transport: tr, + } } client, err := gitea.NewClient(url, gitea.SetToken(token), gitea.SetHTTPClient(httpClient)) if err != nil { @@ -54,10 +56,11 @@ func (g *GiteaService) List(ctx context.Context) ([]*PullRequest, error) { list := []*PullRequest{} for _, pr := range prs { list = append(list, &PullRequest{ - Number: int(pr.Index), - Branch: pr.Head.Ref, - HeadSHA: pr.Head.Sha, - Labels: getGiteaPRLabelNames(pr.Labels), + Number: int(pr.Index), + Branch: pr.Head.Ref, + TargetBranch: pr.Base.Ref, + HeadSHA: pr.Head.Sha, + Labels: getGiteaPRLabelNames(pr.Labels), }) } return list, nil diff --git a/applicationset/services/pull_request/gitea_test.go b/applicationset/services/pull_request/gitea_test.go index 9d5ff25748234..125c8ee481b3a 100644 --- a/applicationset/services/pull_request/gitea_test.go +++ b/applicationset/services/pull_request/gitea_test.go @@ -256,6 +256,7 @@ func TestGiteaList(t *testing.T) { assert.Equal(t, len(prs), 1) assert.Equal(t, prs[0].Number, 1) assert.Equal(t, prs[0].Branch, "test") + assert.Equal(t, prs[0].TargetBranch, "main") assert.Equal(t, prs[0].HeadSHA, "7bbaf62d92ddfafd9cc8b340c619abaec32bc09f") } @@ -268,9 +269,9 @@ func TestGetGiteaPRLabelNames(t *testing.T) { { Name: "PR has labels", PullLabels: []*gitea.Label{ - &gitea.Label{Name: "label1"}, - &gitea.Label{Name: "label2"}, - &gitea.Label{Name: "label3"}, + {Name: "label1"}, + {Name: "label2"}, + {Name: "label3"}, }, ExpectedResult: []string{"label1", "label2", "label3"}, }, diff --git a/applicationset/services/pull_request/github.go b/applicationset/services/pull_request/github.go index a40588ec3d367..7c801e7370f53 100644 --- a/applicationset/services/pull_request/github.go +++ b/applicationset/services/pull_request/github.go @@ -65,10 +65,11 @@ func (g *GithubService) List(ctx context.Context) ([]*PullRequest, error) { continue } pullRequests = append(pullRequests, &PullRequest{ - Number: *pull.Number, - Branch: *pull.Head.Ref, - HeadSHA: *pull.Head.SHA, - Labels: getGithubPRLabelNames(pull.Labels), + Number: *pull.Number, + Branch: *pull.Head.Ref, + TargetBranch: *pull.Base.Ref, + HeadSHA: *pull.Head.SHA, + Labels: getGithubPRLabelNames(pull.Labels), }) } if resp.NextPage == 0 { diff --git a/applicationset/services/pull_request/github_test.go b/applicationset/services/pull_request/github_test.go index 4c89404c09a6a..c47031acb7e31 100644 --- a/applicationset/services/pull_request/github_test.go +++ b/applicationset/services/pull_request/github_test.go @@ -22,9 +22,9 @@ func TestContainLabels(t *testing.T) { Name: "Match labels", Labels: []string{"label1", "label2"}, PullLabels: []*github.Label{ - &github.Label{Name: toPtr("label1")}, - &github.Label{Name: toPtr("label2")}, - &github.Label{Name: toPtr("label3")}, + {Name: toPtr("label1")}, + {Name: toPtr("label2")}, + {Name: toPtr("label3")}, }, Expect: true, }, @@ -32,9 +32,9 @@ func TestContainLabels(t *testing.T) { Name: "Not match labels", Labels: []string{"label1", "label4"}, PullLabels: []*github.Label{ - &github.Label{Name: toPtr("label1")}, - &github.Label{Name: toPtr("label2")}, - &github.Label{Name: toPtr("label3")}, + {Name: toPtr("label1")}, + {Name: toPtr("label2")}, + {Name: toPtr("label3")}, }, Expect: false, }, @@ -42,9 +42,9 @@ func TestContainLabels(t *testing.T) { Name: "No specify", Labels: []string{}, PullLabels: []*github.Label{ - &github.Label{Name: toPtr("label1")}, - &github.Label{Name: toPtr("label2")}, - &github.Label{Name: toPtr("label3")}, + {Name: toPtr("label1")}, + {Name: toPtr("label2")}, + {Name: toPtr("label3")}, }, Expect: true, }, @@ -68,9 +68,9 @@ func TestGetGitHubPRLabelNames(t *testing.T) { { Name: "PR has labels", PullLabels: []*github.Label{ - &github.Label{Name: toPtr("label1")}, - &github.Label{Name: toPtr("label2")}, - &github.Label{Name: toPtr("label3")}, + {Name: toPtr("label1")}, + {Name: toPtr("label2")}, + {Name: toPtr("label3")}, }, ExpectedResult: []string{"label1", "label2", "label3"}, }, diff --git a/applicationset/services/pull_request/gitlab.go b/applicationset/services/pull_request/gitlab.go index 39f6250aae0e8..04a4f3464f6f0 100644 --- a/applicationset/services/pull_request/gitlab.go +++ b/applicationset/services/pull_request/gitlab.go @@ -3,8 +3,11 @@ package pull_request import ( "context" "fmt" + "net/http" "os" + "github.com/argoproj/argo-cd/v2/applicationset/utils" + "github.com/hashicorp/go-retryablehttp" gitlab "github.com/xanzy/go-gitlab" ) @@ -17,7 +20,7 @@ type GitLabService struct { var _ PullRequestService = (*GitLabService)(nil) -func NewGitLabService(ctx context.Context, token, url, project string, labels []string, pullRequestState string) (PullRequestService, error) { +func NewGitLabService(ctx context.Context, token, url, project string, labels []string, pullRequestState string, scmRootCAPath string, insecure bool) (PullRequestService, error) { var clientOptionFns []gitlab.ClientOptionFunc // Set a custom Gitlab base URL if one is provided @@ -29,6 +32,14 @@ func NewGitLabService(ctx context.Context, token, url, project string, labels [] token = os.Getenv("GITLAB_TOKEN") } + tr := http.DefaultTransport.(*http.Transport).Clone() + tr.TLSClientConfig = utils.GetTlsConfig(scmRootCAPath, insecure) + + retryClient := retryablehttp.NewClient() + retryClient.HTTPClient.Transport = tr + + clientOptionFns = append(clientOptionFns, gitlab.WithHTTPClient(retryClient.HTTPClient)) + client, err := gitlab.NewClient(token, clientOptionFns...) if err != nil { return nil, fmt.Errorf("error creating Gitlab client: %v", err) @@ -69,10 +80,11 @@ func (g *GitLabService) List(ctx context.Context) ([]*PullRequest, error) { } for _, mr := range mrs { pullRequests = append(pullRequests, &PullRequest{ - Number: mr.IID, - Branch: mr.SourceBranch, - HeadSHA: mr.SHA, - Labels: mr.Labels, + Number: mr.IID, + Branch: mr.SourceBranch, + TargetBranch: mr.TargetBranch, + HeadSHA: mr.SHA, + Labels: mr.Labels, }) } if resp.NextPage == 0 { diff --git a/applicationset/services/pull_request/gitlab_test.go b/applicationset/services/pull_request/gitlab_test.go index 7c42e6f9f9269..59c476fcd713a 100644 --- a/applicationset/services/pull_request/gitlab_test.go +++ b/applicationset/services/pull_request/gitlab_test.go @@ -34,7 +34,7 @@ func TestGitLabServiceCustomBaseURL(t *testing.T) { writeMRListResponse(t, w) }) - svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", nil, "") + svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", nil, "", "", false) assert.NoError(t, err) _, err = svc.List(context.Background()) @@ -53,7 +53,7 @@ func TestGitLabServiceToken(t *testing.T) { writeMRListResponse(t, w) }) - svc, err := NewGitLabService(context.Background(), "token-123", server.URL, "278964", nil, "") + svc, err := NewGitLabService(context.Background(), "token-123", server.URL, "278964", nil, "", "", false) assert.NoError(t, err) _, err = svc.List(context.Background()) @@ -72,7 +72,7 @@ func TestList(t *testing.T) { writeMRListResponse(t, w) }) - svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "") + svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "", "", false) assert.NoError(t, err) prs, err := svc.List(context.Background()) @@ -80,6 +80,7 @@ func TestList(t *testing.T) { assert.Len(t, prs, 1) assert.Equal(t, prs[0].Number, 15442) assert.Equal(t, prs[0].Branch, "use-structured-logging-for-db-load-balancer") + assert.Equal(t, prs[0].TargetBranch, "master") assert.Equal(t, prs[0].HeadSHA, "2fc4e8b972ff3208ec63b6143e34ad67ff343ad7") } @@ -95,7 +96,7 @@ func TestListWithLabels(t *testing.T) { writeMRListResponse(t, w) }) - svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{"feature", "ready"}, "") + svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{"feature", "ready"}, "", "", false) assert.NoError(t, err) _, err = svc.List(context.Background()) @@ -114,7 +115,7 @@ func TestListWithState(t *testing.T) { writeMRListResponse(t, w) }) - svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "opened") + svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "opened", "", false) assert.NoError(t, err) _, err = svc.List(context.Background()) diff --git a/applicationset/services/pull_request/interface.go b/applicationset/services/pull_request/interface.go index f81dc35e41435..0015cfe5eafa6 100644 --- a/applicationset/services/pull_request/interface.go +++ b/applicationset/services/pull_request/interface.go @@ -10,6 +10,8 @@ type PullRequest struct { Number int // Branch is the name of the branch from which the pull request originated. Branch string + // TargetBranch is the name of the target branch of the pull request. + TargetBranch string // HeadSHA is the SHA of the HEAD from which the pull request originated. HeadSHA string // Labels of the pull request. @@ -22,5 +24,6 @@ type PullRequestService interface { } type Filter struct { - BranchMatch *regexp.Regexp + BranchMatch *regexp.Regexp + TargetBranchMatch *regexp.Regexp } diff --git a/applicationset/services/pull_request/utils.go b/applicationset/services/pull_request/utils.go index c7970170a7d64..50d4e5a3c0098 100644 --- a/applicationset/services/pull_request/utils.go +++ b/applicationset/services/pull_request/utils.go @@ -19,6 +19,12 @@ func compileFilters(filters []argoprojiov1alpha1.PullRequestGeneratorFilter) ([] return nil, fmt.Errorf("error compiling BranchMatch regexp %q: %v", *filter.BranchMatch, err) } } + if filter.TargetBranchMatch != nil { + outFilter.TargetBranchMatch, err = regexp.Compile(*filter.TargetBranchMatch) + if err != nil { + return nil, fmt.Errorf("error compiling TargetBranchMatch regexp %q: %v", *filter.TargetBranchMatch, err) + } + } outFilters = append(outFilters, outFilter) } return outFilters, nil @@ -28,6 +34,9 @@ func matchFilter(pullRequest *PullRequest, filter *Filter) bool { if filter.BranchMatch != nil && !filter.BranchMatch.MatchString(pullRequest.Branch) { return false } + if filter.TargetBranchMatch != nil && !filter.TargetBranchMatch.MatchString(pullRequest.TargetBranch) { + return false + } return true } diff --git a/applicationset/services/pull_request/utils_test.go b/applicationset/services/pull_request/utils_test.go index eb92e5fad866f..3f813127edab7 100644 --- a/applicationset/services/pull_request/utils_test.go +++ b/applicationset/services/pull_request/utils_test.go @@ -16,9 +16,10 @@ func TestFilterBranchMatchBadRegexp(t *testing.T) { context.Background(), []*PullRequest{ { - Number: 1, - Branch: "branch1", - HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958", + Number: 1, + Branch: "branch1", + TargetBranch: "master", + HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958", }, }, nil, @@ -37,24 +38,28 @@ func TestFilterBranchMatch(t *testing.T) { context.Background(), []*PullRequest{ { - Number: 1, - Branch: "one", - HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", + Number: 1, + Branch: "one", + TargetBranch: "master", + HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", }, { - Number: 2, - Branch: "two", - HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", + Number: 2, + Branch: "two", + TargetBranch: "master", + HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", }, { - Number: 3, - Branch: "three", - HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958", + Number: 3, + Branch: "three", + TargetBranch: "master", + HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958", }, { - Number: 4, - Branch: "four", - HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958", + Number: 4, + Branch: "four", + TargetBranch: "master", + HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958", }, }, nil, @@ -70,29 +75,75 @@ func TestFilterBranchMatch(t *testing.T) { assert.Equal(t, "two", pullRequests[0].Branch) } +func TestFilterTargetBranchMatch(t *testing.T) { + provider, _ := NewFakeService( + context.Background(), + []*PullRequest{ + { + Number: 1, + Branch: "one", + TargetBranch: "master", + HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", + }, + { + Number: 2, + Branch: "two", + TargetBranch: "branch1", + HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", + }, + { + Number: 3, + Branch: "three", + TargetBranch: "branch2", + HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958", + }, + { + Number: 4, + Branch: "four", + TargetBranch: "branch3", + HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958", + }, + }, + nil, + ) + filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{ + { + TargetBranchMatch: strp("1"), + }, + } + pullRequests, err := ListPullRequests(context.Background(), provider, filters) + assert.NoError(t, err) + assert.Len(t, pullRequests, 1) + assert.Equal(t, "two", pullRequests[0].Branch) +} + func TestMultiFilterOr(t *testing.T) { provider, _ := NewFakeService( context.Background(), []*PullRequest{ { - Number: 1, - Branch: "one", - HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", + Number: 1, + Branch: "one", + TargetBranch: "master", + HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", }, { - Number: 2, - Branch: "two", - HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", + Number: 2, + Branch: "two", + TargetBranch: "master", + HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", }, { - Number: 3, - Branch: "three", - HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958", + Number: 3, + Branch: "three", + TargetBranch: "master", + HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958", }, { - Number: 4, - Branch: "four", - HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958", + Number: 4, + Branch: "four", + TargetBranch: "master", + HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958", }, }, nil, @@ -113,19 +164,69 @@ func TestMultiFilterOr(t *testing.T) { assert.Equal(t, "four", pullRequests[2].Branch) } +func TestMultiFilterOrWithTargetBranchFilter(t *testing.T) { + provider, _ := NewFakeService( + context.Background(), + []*PullRequest{ + { + Number: 1, + Branch: "one", + TargetBranch: "master", + HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", + }, + { + Number: 2, + Branch: "two", + TargetBranch: "branch1", + HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", + }, + { + Number: 3, + Branch: "three", + TargetBranch: "branch2", + HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958", + }, + { + Number: 4, + Branch: "four", + TargetBranch: "branch3", + HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958", + }, + }, + nil, + ) + filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{ + { + BranchMatch: strp("w"), + TargetBranchMatch: strp("1"), + }, + { + BranchMatch: strp("r"), + TargetBranchMatch: strp("3"), + }, + } + pullRequests, err := ListPullRequests(context.Background(), provider, filters) + assert.NoError(t, err) + assert.Len(t, pullRequests, 2) + assert.Equal(t, "two", pullRequests[0].Branch) + assert.Equal(t, "four", pullRequests[1].Branch) +} + func TestNoFilters(t *testing.T) { provider, _ := NewFakeService( context.Background(), []*PullRequest{ { - Number: 1, - Branch: "one", - HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", + Number: 1, + Branch: "one", + TargetBranch: "master", + HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958", }, { - Number: 2, - Branch: "two", - HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", + Number: 2, + Branch: "two", + TargetBranch: "master", + HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958", }, }, nil, diff --git a/applicationset/services/repo_service.go b/applicationset/services/repo_service.go index 571f497433de8..64fedc34390b8 100644 --- a/applicationset/services/repo_service.go +++ b/applicationset/services/repo_service.go @@ -3,151 +3,100 @@ package services import ( "context" "fmt" - "os" - "path/filepath" - "strings" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/util/db" "github.com/argoproj/argo-cd/v2/util/git" + "github.com/argoproj/argo-cd/v2/util/io" ) +//go:generate go run github.com/vektra/mockery/v2@v2.25.1 --name=RepositoryDB + // RepositoryDB Is a lean facade for ArgoDB, -// Using a lean interface makes it more easy to test the functionality the git generator uses +// Using a lean interface makes it easier to test the functionality of the git generator type RepositoryDB interface { GetRepository(ctx context.Context, url string) (*v1alpha1.Repository, error) } type argoCDService struct { - repositoriesDB RepositoryDB - storecreds git.CredsStore - submoduleEnabled bool + repositoriesDB RepositoryDB + storecreds git.CredsStore + submoduleEnabled bool + repoServerClientSet apiclient.Clientset + newFileGlobbingEnabled bool } +//go:generate go run github.com/vektra/mockery/v2@v2.25.1 --name=Repos + type Repos interface { // GetFiles returns content of files (not directories) within the target repo - GetFiles(ctx context.Context, repoURL string, revision string, pattern string) (map[string][]byte, error) + GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error) // GetDirectories returns a list of directories (not files) within the target repo - GetDirectories(ctx context.Context, repoURL string, revision string) ([]string, error) + GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error) } -func NewArgoCDService(db db.ArgoDB, gitCredStore git.CredsStore, submoduleEnabled bool) Repos { - +func NewArgoCDService(db db.ArgoDB, submoduleEnabled bool, repoClientset apiclient.Clientset, newFileGlobbingEnabled bool) (Repos, error) { return &argoCDService{ - repositoriesDB: db.(RepositoryDB), - storecreds: gitCredStore, - submoduleEnabled: submoduleEnabled, - } + repositoriesDB: db.(RepositoryDB), + submoduleEnabled: submoduleEnabled, + repoServerClientSet: repoClientset, + newFileGlobbingEnabled: newFileGlobbingEnabled, + }, nil } -func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision string, pattern string) (map[string][]byte, error) { +func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error) { repo, err := a.repositoriesDB.GetRepository(ctx, repoURL) if err != nil { - return nil, fmt.Errorf("Error in GetRepository: %w", err) + return nil, fmt.Errorf("error in GetRepository: %w", err) } - gitRepoClient, err := git.NewClient(repo.Repo, repo.GetGitCreds(a.storecreds), repo.IsInsecure(), repo.IsLFSEnabled(), repo.Proxy) - - if err != nil { - return nil, err + fileRequest := &apiclient.GitFilesRequest{ + Repo: repo, + SubmoduleEnabled: a.submoduleEnabled, + Revision: revision, + Path: pattern, + NewGitFileGlobbingEnabled: a.newFileGlobbingEnabled, + NoRevisionCache: noRevisionCache, } - - err = checkoutRepo(gitRepoClient, revision, a.submoduleEnabled) + closer, client, err := a.repoServerClientSet.NewRepoServerClient() if err != nil { - return nil, err + return nil, fmt.Errorf("error initialising new repo server client: %w", err) } + defer io.Close(closer) - paths, err := gitRepoClient.LsFiles(pattern) + fileResponse, err := client.GetGitFiles(ctx, fileRequest) if err != nil { - return nil, fmt.Errorf("Error during listing files of local repo: %w", err) - } - - res := map[string][]byte{} - for _, filePath := range paths { - bytes, err := os.ReadFile(filepath.Join(gitRepoClient.Root(), filePath)) - if err != nil { - return nil, err - } - res[filePath] = bytes + return nil, fmt.Errorf("error retrieving Git files: %w", err) } - - return res, nil + return fileResponse.GetMap(), nil } -func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revision string) ([]string, error) { - +func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error) { repo, err := a.repositoriesDB.GetRepository(ctx, repoURL) if err != nil { - return nil, fmt.Errorf("Error in GetRepository: %w", err) + return nil, fmt.Errorf("error in GetRepository: %w", err) } - gitRepoClient, err := git.NewClient(repo.Repo, repo.GetGitCreds(a.storecreds), repo.IsInsecure(), repo.IsLFSEnabled(), repo.Proxy) - if err != nil { - return nil, fmt.Errorf("error creating a new git client: %w", err) + dirRequest := &apiclient.GitDirectoriesRequest{ + Repo: repo, + SubmoduleEnabled: a.submoduleEnabled, + Revision: revision, + NoRevisionCache: noRevisionCache, } - err = checkoutRepo(gitRepoClient, revision, a.submoduleEnabled) + closer, client, err := a.repoServerClientSet.NewRepoServerClient() if err != nil { - return nil, fmt.Errorf("error while checking out repo: %w", err) + return nil, fmt.Errorf("error initialising new repo server client: %w", err) } + defer io.Close(closer) - filteredPaths := []string{} - - repoRoot := gitRepoClient.Root() - - if err := filepath.Walk(repoRoot, func(path string, info os.FileInfo, fnErr error) error { - if fnErr != nil { - return fmt.Errorf("error walking the file tree: %w", fnErr) - } - if !info.IsDir() { // Skip files: directories only - return nil - } - - fname := info.Name() - if strings.HasPrefix(fname, ".") { // Skip all folders starts with "." - return filepath.SkipDir - } - - relativePath, err := filepath.Rel(repoRoot, path) - if err != nil { - return fmt.Errorf("error constructing relative repo path: %w", err) - } - - if relativePath == "." { // Exclude '.' from results - return nil - } - - filteredPaths = append(filteredPaths, relativePath) - - return nil - }); err != nil { - return nil, err - } - - return filteredPaths, nil - -} - -func checkoutRepo(gitRepoClient git.Client, revision string, submoduleEnabled bool) error { - err := gitRepoClient.Init() + dirResponse, err := client.GetGitDirectories(ctx, dirRequest) if err != nil { - return fmt.Errorf("Error during initializing repo: %w", err) + return nil, fmt.Errorf("error retrieving Git Directories: %w", err) } + return dirResponse.GetPaths(), nil - err = gitRepoClient.Fetch(revision) - if err != nil { - return fmt.Errorf("Error during fetching repo: %w", err) - } - - commitSHA, err := gitRepoClient.LsRemote(revision) - if err != nil { - return fmt.Errorf("Error during fetching commitSHA: %w", err) - } - err = gitRepoClient.Checkout(commitSHA, submoduleEnabled) - if err != nil { - return fmt.Errorf("Error during repo checkout: %w", err) - } - return nil } diff --git a/applicationset/services/repo_service_test.go b/applicationset/services/repo_service_test.go index bc0238b2eacc7..040fe57f96958 100644 --- a/applicationset/services/repo_service_test.go +++ b/applicationset/services/repo_service_test.go @@ -3,231 +3,191 @@ package services import ( "context" "fmt" - "sort" "testing" + "github.com/argoproj/argo-cd/v2/applicationset/services/mocks" + "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + repo_mocks "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks" + db_mocks "github.com/argoproj/argo-cd/v2/util/db/mocks" + "github.com/argoproj/argo-cd/v2/util/git" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) -type ArgocdRepositoryMock struct { - mock *mock.Mock -} - -func (a ArgocdRepositoryMock) GetRepository(ctx context.Context, url string) (*v1alpha1.Repository, error) { - args := a.mock.Called(ctx, url) - - return args.Get(0).(*v1alpha1.Repository), args.Error(1) - -} - func TestGetDirectories(t *testing.T) { - // Hardcode a specific revision to changes to argocd-example-apps from regressing this test: - // Author: Alexander Matyushentsev - // Date: Sun Jan 31 09:54:53 2021 -0800 - // chore: downgrade kustomize guestbook image tag (#73) - exampleRepoRevision := "08f72e2a309beab929d9fd14626071b1a61a47f9" - - for _, c := range []struct { - name string - repoURL string - revision string - repoRes *v1alpha1.Repository - repoErr error - expected []string - expectedError error + type fields struct { + repositoriesDBFuncs []func(*mocks.RepositoryDB) + storecreds git.CredsStore + submoduleEnabled bool + repoServerClientFuncs []func(*repo_mocks.RepoServerServiceClient) + } + type args struct { + ctx context.Context + repoURL string + revision string + noRevisionCache bool + } + tests := []struct { + name string + fields fields + args args + want []string + wantErr assert.ErrorAssertionFunc }{ - { - name: "All child folders should be returned", - repoURL: "https://github.com/argoproj/argocd-example-apps/", - revision: exampleRepoRevision, - repoRes: &v1alpha1.Repository{ - Repo: "https://github.com/argoproj/argocd-example-apps/", + {name: "ErrorGettingRepos", fields: fields{ + repositoriesDBFuncs: []func(*mocks.RepositoryDB){ + func(db *mocks.RepositoryDB) { + db.On("GetRepository", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("unable to get repos")) + }, }, - repoErr: nil, - expected: []string{"apps", "apps/templates", "blue-green", "blue-green/templates", "guestbook", "helm-dependency", - "helm-guestbook", "helm-guestbook/templates", "helm-hooks", "jsonnet-guestbook", "jsonnet-guestbook-tla", - "ksonnet-guestbook", "ksonnet-guestbook/components", "ksonnet-guestbook/environments", "ksonnet-guestbook/environments/default", - "ksonnet-guestbook/environments/dev", "ksonnet-guestbook/environments/prod", "kustomize-guestbook", "plugins", "plugins/kasane", - "plugins/kustomized-helm", "plugins/kustomized-helm/overlays", "pre-post-sync", "sock-shop", "sock-shop/base", "sync-waves"}, - }, - { - name: "If GetRepository returns an error, it should pass back to caller", - repoURL: "https://github.com/argoproj/argocd-example-apps/", - revision: exampleRepoRevision, - repoRes: &v1alpha1.Repository{ - Repo: "https://github.com/argoproj/argocd-example-apps/", + }, args: args{}, want: nil, wantErr: assert.Error}, + {name: "ErrorGettingDirs", fields: fields{ + repositoriesDBFuncs: []func(*mocks.RepositoryDB){ + func(db *mocks.RepositoryDB) { + db.On("GetRepository", mock.Anything, mock.Anything).Return(&v1alpha1.Repository{}, nil) + }, }, - repoErr: fmt.Errorf("Simulated error from GetRepository"), - expected: nil, - expectedError: fmt.Errorf("Error in GetRepository: Simulated error from GetRepository"), - }, - { - name: "Test against repository containing no directories", - // Here I picked an arbitrary repository in argoproj-labs, with a commit containing no folders. - repoURL: "https://github.com/argoproj-labs/argo-workflows-operator/", - revision: "5f50933a576833b73b7a172909d8545a108685f4", - repoRes: &v1alpha1.Repository{ - Repo: "https://github.com/argoproj-labs/argo-workflows-operator/", + repoServerClientFuncs: []func(*repo_mocks.RepoServerServiceClient){ + func(client *repo_mocks.RepoServerServiceClient) { + client.On("GetGitDirectories", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("unable to get dirs")) + }, }, - repoErr: nil, - expected: []string{}, - }, - } { - cc := c - t.Run(cc.name, func(t *testing.T) { - argocdRepositoryMock := ArgocdRepositoryMock{mock: &mock.Mock{}} - - argocdRepositoryMock.mock.On("GetRepository", mock.Anything, cc.repoURL).Return(cc.repoRes, cc.repoErr) - - argocd := argoCDService{ - repositoriesDB: argocdRepositoryMock, + }, args: args{}, want: nil, wantErr: assert.Error}, + {name: "HappyCase", fields: fields{ + repositoriesDBFuncs: []func(*mocks.RepositoryDB){ + func(db *mocks.RepositoryDB) { + db.On("GetRepository", mock.Anything, mock.Anything).Return(&v1alpha1.Repository{}, nil) + }, + }, + repoServerClientFuncs: []func(*repo_mocks.RepoServerServiceClient){ + func(client *repo_mocks.RepoServerServiceClient) { + client.On("GetGitDirectories", mock.Anything, mock.Anything).Return(&apiclient.GitDirectoriesResponse{ + Paths: []string{"foo", "foo/bar", "bar/foo"}, + }, nil) + }, + }, + }, args: args{}, want: []string{"foo", "foo/bar", "bar/foo"}, wantErr: assert.NoError}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockDb := &mocks.RepositoryDB{} + mockRepoClient := &repo_mocks.RepoServerServiceClient{} + // decorate the mocks + for i := range tt.fields.repositoriesDBFuncs { + tt.fields.repositoriesDBFuncs[i](mockDb) + } + for i := range tt.fields.repoServerClientFuncs { + tt.fields.repoServerClientFuncs[i](mockRepoClient) } - got, err := argocd.GetDirectories(context.TODO(), cc.repoURL, cc.revision) - - if cc.expectedError != nil { - assert.EqualError(t, err, cc.expectedError.Error()) - } else { - sort.Strings(got) - sort.Strings(cc.expected) - - assert.Equal(t, got, cc.expected) - assert.NoError(t, err) + a := &argoCDService{ + repositoriesDB: mockDb, + storecreds: tt.fields.storecreds, + submoduleEnabled: tt.fields.submoduleEnabled, + repoServerClientSet: &repo_mocks.Clientset{RepoServerServiceClient: mockRepoClient}, + } + got, err := a.GetDirectories(tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.noRevisionCache) + if !tt.wantErr(t, err, fmt.Sprintf("GetDirectories(%v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.noRevisionCache)) { + return } + assert.Equalf(t, tt.want, got, "GetDirectories(%v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.noRevisionCache) }) } } func TestGetFiles(t *testing.T) { - - // Hardcode a specific commit, so that changes to argoproj/argocd-example-apps/ don't break our tests - // "chore: downgrade kustomize guestbook image tag (#73)" - commitID := "08f72e2a309beab929d9fd14626071b1a61a47f9" - + type fields struct { + repositoriesDBFuncs []func(*mocks.RepositoryDB) + storecreds git.CredsStore + submoduleEnabled bool + repoServerClientFuncs []func(*repo_mocks.RepoServerServiceClient) + } + type args struct { + ctx context.Context + repoURL string + revision string + pattern string + noRevisionCache bool + } tests := []struct { - name string - repoURL string - revision string - pattern string - repoRes *v1alpha1.Repository - repoErr error - - expectSubsetOfPaths []string - doesNotContainPaths []string - expectedError error + name string + fields fields + args args + want map[string][]byte + wantErr assert.ErrorAssertionFunc }{ - { - name: "pull a specific revision of example apps and verify the list is expected", - repoRes: &v1alpha1.Repository{ - Insecure: true, - InsecureIgnoreHostKey: true, - Repo: "https://github.com/argoproj/argocd-example-apps/", + {name: "ErrorGettingRepos", fields: fields{ + repositoriesDBFuncs: []func(*mocks.RepositoryDB){ + func(db *mocks.RepositoryDB) { + db.On("GetRepository", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("unable to get repos")) + }, }, - repoURL: "https://github.com/argoproj/argocd-example-apps/", - revision: commitID, - pattern: "*", - expectSubsetOfPaths: []string{ - "apps/Chart.yaml", - "apps/templates/helm-guestbook.yaml", - "apps/templates/helm-hooks.yaml", - "apps/templates/kustomize-guestbook.yaml", - "apps/templates/namespaces.yaml", - "apps/templates/sync-waves.yaml", - "apps/values.yaml", - "blue-green/.helmignore", - "blue-green/Chart.yaml", - "blue-green/README.md", - "blue-green/templates/NOTES.txt", - "blue-green/templates/rollout.yaml", - "blue-green/templates/services.yaml", - "blue-green/values.yaml", - "guestbook/guestbook-ui-deployment.yaml", - "guestbook/guestbook-ui-svc.yaml", - "kustomize-guestbook/guestbook-ui-deployment.yaml", - "kustomize-guestbook/guestbook-ui-svc.yaml", - "kustomize-guestbook/kustomization.yaml", + }, args: args{}, want: nil, wantErr: assert.Error}, + {name: "ErrorGettingFiles", fields: fields{ + repositoriesDBFuncs: []func(*mocks.RepositoryDB){ + func(db *mocks.RepositoryDB) { + db.On("GetRepository", mock.Anything, mock.Anything).Return(&v1alpha1.Repository{}, nil) + }, }, - }, - { - name: "pull an invalid revision, and confirm an error is returned", - repoRes: &v1alpha1.Repository{ - Insecure: true, - InsecureIgnoreHostKey: true, - Repo: "https://github.com/argoproj/argocd-example-apps/", + repoServerClientFuncs: []func(*repo_mocks.RepoServerServiceClient){ + func(client *repo_mocks.RepoServerServiceClient) { + client.On("GetGitFiles", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("unable to get files")) + }, }, - repoURL: "https://github.com/argoproj/argocd-example-apps/", - revision: "this-tag-does-not-exist", - pattern: "*", - expectSubsetOfPaths: []string{}, - expectedError: fmt.Errorf("Error during fetching repo: `git fetch origin this-tag-does-not-exist --tags --force --prune` failed exit status 128: fatal: couldn't find remote ref this-tag-does-not-exist"), - }, - { - name: "pull a specific revision of example apps, and use a ** pattern", - repoRes: &v1alpha1.Repository{ - Insecure: true, - InsecureIgnoreHostKey: true, - Repo: "https://github.com/argoproj/argocd-example-apps/", + }, args: args{}, want: nil, wantErr: assert.Error}, + {name: "HappyCase", fields: fields{ + repositoriesDBFuncs: []func(*mocks.RepositoryDB){ + func(db *mocks.RepositoryDB) { + db.On("GetRepository", mock.Anything, mock.Anything).Return(&v1alpha1.Repository{}, nil) + }, }, - repoURL: "https://github.com/argoproj/argocd-example-apps/", - revision: commitID, - pattern: "**/*.yaml", - expectSubsetOfPaths: []string{ - "apps/Chart.yaml", - "apps/templates/helm-guestbook.yaml", - "apps/templates/helm-hooks.yaml", - "apps/templates/kustomize-guestbook.yaml", - "apps/templates/namespaces.yaml", - "apps/templates/sync-waves.yaml", - "apps/values.yaml", - "blue-green/templates/rollout.yaml", - "blue-green/templates/services.yaml", - "blue-green/values.yaml", - "guestbook/guestbook-ui-deployment.yaml", - "guestbook/guestbook-ui-svc.yaml", - "kustomize-guestbook/guestbook-ui-deployment.yaml", - "kustomize-guestbook/guestbook-ui-svc.yaml", - "kustomize-guestbook/kustomization.yaml", + repoServerClientFuncs: []func(*repo_mocks.RepoServerServiceClient){ + func(client *repo_mocks.RepoServerServiceClient) { + client.On("GetGitFiles", mock.Anything, mock.Anything).Return(&apiclient.GitFilesResponse{ + Map: map[string][]byte{ + "foo.json": []byte("hello: world!"), + "bar.yaml": []byte("yay: appsets"), + }, + }, nil) + }, }, - doesNotContainPaths: []string{ - "blue-green/.helmignore", - "blue-green/README.md", - "blue-green/templates/NOTES.txt", - }, - }, + }, args: args{}, want: map[string][]byte{ + "foo.json": []byte("hello: world!"), + "bar.yaml": []byte("yay: appsets"), + }, wantErr: assert.NoError}, } - - for _, cc := range tests { - - // Get all the paths for a repository, and confirm that the expected subset of paths is found (or the expected error is returned) - t.Run(cc.name, func(t *testing.T) { - argocdRepositoryMock := ArgocdRepositoryMock{mock: &mock.Mock{}} - - argocdRepositoryMock.mock.On("GetRepository", mock.Anything, cc.repoURL).Return(cc.repoRes, cc.repoErr) - - argocd := argoCDService{ - repositoriesDB: argocdRepositoryMock, + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockDb := &mocks.RepositoryDB{} + mockRepoClient := &repo_mocks.RepoServerServiceClient{} + // decorate the mocks + for i := range tt.fields.repositoriesDBFuncs { + tt.fields.repositoriesDBFuncs[i](mockDb) + } + for i := range tt.fields.repoServerClientFuncs { + tt.fields.repoServerClientFuncs[i](mockRepoClient) } - getPathsRes, err := argocd.GetFiles(context.Background(), cc.repoURL, cc.revision, cc.pattern) - - if cc.expectedError == nil { - - assert.NoError(t, err) - for _, path := range cc.expectSubsetOfPaths { - assert.Contains(t, getPathsRes, path, "Unable to locate path: %s", path) - } - - for _, shouldNotContain := range cc.doesNotContainPaths { - assert.NotContains(t, getPathsRes, shouldNotContain, "GetPaths should not contain %s", shouldNotContain) - } - - } else { - assert.EqualError(t, err, cc.expectedError.Error()) + a := &argoCDService{ + repositoriesDB: mockDb, + storecreds: tt.fields.storecreds, + submoduleEnabled: tt.fields.submoduleEnabled, + repoServerClientSet: &repo_mocks.Clientset{RepoServerServiceClient: mockRepoClient}, + } + got, err := a.GetFiles(tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern, tt.args.noRevisionCache) + if !tt.wantErr(t, err, fmt.Sprintf("GetFiles(%v, %v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern, tt.args.noRevisionCache)) { + return } + assert.Equalf(t, tt.want, got, "GetFiles(%v, %v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern, tt.args.noRevisionCache) }) } } + +func TestNewArgoCDService(t *testing.T) { + service, err := NewArgoCDService(&db_mocks.ArgoDB{}, false, &repo_mocks.Clientset{}, false) + assert.NoError(t, err, err) + assert.NotNil(t, service) +} diff --git a/applicationset/services/scm_provider/aws_codecommit.go b/applicationset/services/scm_provider/aws_codecommit.go new file mode 100644 index 0000000000000..280711271cfb0 --- /dev/null +++ b/applicationset/services/scm_provider/aws_codecommit.go @@ -0,0 +1,376 @@ +package scm_provider + +import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go/aws/request" + pathpkg "path" + "path/filepath" + "strings" + + application "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/credentials/stscreds" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/codecommit" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" + log "github.com/sirupsen/logrus" + "golang.org/x/exp/maps" + "k8s.io/utils/strings/slices" +) + +const ( + resourceTypeCodeCommitRepository = "codecommit:repository" + prefixGitUrlHttps = "https://git-codecommit." + prefixGitUrlHttpsFIPS = "https://git-codecommit-fips." +) + +// AWSCodeCommitClient is a lean facade to the codecommitiface.CodeCommitAPI +// it helps to reduce the mockery generated code. +type AWSCodeCommitClient interface { + ListRepositoriesWithContext(aws.Context, *codecommit.ListRepositoriesInput, ...request.Option) (*codecommit.ListRepositoriesOutput, error) + GetRepositoryWithContext(aws.Context, *codecommit.GetRepositoryInput, ...request.Option) (*codecommit.GetRepositoryOutput, error) + ListBranchesWithContext(aws.Context, *codecommit.ListBranchesInput, ...request.Option) (*codecommit.ListBranchesOutput, error) + GetFolderWithContext(aws.Context, *codecommit.GetFolderInput, ...request.Option) (*codecommit.GetFolderOutput, error) +} + +// AWSTaggingClient is a lean facade to the resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI +// it helps to reduce the mockery generated code. +type AWSTaggingClient interface { + GetResourcesWithContext(aws.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error) +} + +type AWSCodeCommitProvider struct { + codeCommitClient AWSCodeCommitClient + taggingClient AWSTaggingClient + tagFilters []*application.TagFilter + allBranches bool +} + +func NewAWSCodeCommitProvider(ctx context.Context, tagFilters []*application.TagFilter, role string, region string, allBranches bool) (*AWSCodeCommitProvider, error) { + taggingClient, codeCommitClient, err := createAWSDiscoveryClients(ctx, role, region) + if err != nil { + return nil, err + } + return &AWSCodeCommitProvider{ + codeCommitClient: codeCommitClient, + taggingClient: taggingClient, + tagFilters: tagFilters, + allBranches: allBranches, + }, nil +} + +func (p *AWSCodeCommitProvider) ListRepos(ctx context.Context, cloneProtocol string) ([]*Repository, error) { + repos := make([]*Repository, 0) + + repoNames, err := p.listRepoNames(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list codecommit repository: %w", err) + } + + for _, repoName := range repoNames { + repo, err := p.codeCommitClient.GetRepositoryWithContext(ctx, &codecommit.GetRepositoryInput{ + RepositoryName: aws.String(repoName), + }) + if err != nil { + // we don't want to skip at this point. It's a valid repo, we don't want to have flapping Application on an AWS outage. + return nil, fmt.Errorf("failed to get codecommit repository: %w", err) + } + if repo == nil || repo.RepositoryMetadata == nil { + // unlikely to happen, but just in case to protect nil pointer dereferences. + log.Warnf("codecommit returned invalid response for repository %s, skipped", repoName) + continue + } + if aws.StringValue(repo.RepositoryMetadata.DefaultBranch) == "" { + // if a codecommit repo doesn't have default branch, it's uninitialized. not going to bother with it. + log.Warnf("repository %s does not have default branch, skipped", repoName) + continue + } + var url string + switch cloneProtocol { + // default to SSH if unspecified (i.e. if ""). + case "", "ssh": + url = aws.StringValue(repo.RepositoryMetadata.CloneUrlSsh) + case "https": + url = aws.StringValue(repo.RepositoryMetadata.CloneUrlHttp) + case "https-fips": + url, err = getCodeCommitFIPSEndpoint(aws.StringValue(repo.RepositoryMetadata.CloneUrlHttp)) + if err != nil { + return nil, fmt.Errorf("https-fips is provided but repoUrl can't be transformed to FIPS endpoint: %w", err) + } + default: + return nil, fmt.Errorf("unknown clone protocol for codecommit %v", cloneProtocol) + } + repos = append(repos, &Repository{ + // there's no "organization" level at codecommit. + // we are just using AWS accountId for now. + Organization: aws.StringValue(repo.RepositoryMetadata.AccountId), + Repository: aws.StringValue(repo.RepositoryMetadata.RepositoryName), + URL: url, + Branch: aws.StringValue(repo.RepositoryMetadata.DefaultBranch), + // we could propagate repo tag keys, but without value not sure if it's any useful. + Labels: []string{}, + RepositoryId: aws.StringValue(repo.RepositoryMetadata.RepositoryId), + }) + } + + return repos, nil +} + +func (p *AWSCodeCommitProvider) RepoHasPath(ctx context.Context, repo *Repository, path string) (bool, error) { + // we use GetFolder instead of GetFile here because GetFile always downloads the full blob which has scalability problem. + // GetFolder is slightly less concerning. + + path = toAbsolutePath(path) + // shortcut: if it's root folder ('/'), we always return true. + if path == "/" { + return true, nil + } + // here we are sure it's not root folder, strip the suffix for easier comparison. + path = strings.TrimSuffix(path, "/") + + // we always get the parent folder, so we could support both submodule, file, symlink and folder cases. + parentPath := pathpkg.Dir(path) + basePath := pathpkg.Base(path) + + input := &codecommit.GetFolderInput{ + CommitSpecifier: aws.String(repo.Branch), + FolderPath: aws.String(parentPath), + RepositoryName: aws.String(repo.Repository), + } + output, err := p.codeCommitClient.GetFolderWithContext(ctx, input) + if err != nil { + if hasAwsError(err, + codecommit.ErrCodeRepositoryDoesNotExistException, + codecommit.ErrCodeCommitDoesNotExistException, + codecommit.ErrCodeFolderDoesNotExistException, + ) { + return false, nil + } + // unhandled exception, propagate out + return false, err + } + + // anything that matches. + for _, submodule := range output.SubModules { + if basePath == aws.StringValue(submodule.RelativePath) { + return true, nil + } + } + for _, subpath := range output.SubFolders { + if basePath == aws.StringValue(subpath.RelativePath) { + return true, nil + } + } + for _, subpath := range output.Files { + if basePath == aws.StringValue(subpath.RelativePath) { + return true, nil + } + } + for _, subpath := range output.SymbolicLinks { + if basePath == aws.StringValue(subpath.RelativePath) { + return true, nil + } + } + return false, nil +} + +func (p *AWSCodeCommitProvider) GetBranches(ctx context.Context, repo *Repository) ([]*Repository, error) { + repos := make([]*Repository, 0) + if !p.allBranches { + output, err := p.codeCommitClient.GetRepositoryWithContext(ctx, &codecommit.GetRepositoryInput{ + RepositoryName: aws.String(repo.Repository), + }) + if err != nil { + return nil, err + } + repos = append(repos, &Repository{ + Organization: repo.Organization, + Repository: repo.Repository, + URL: repo.URL, + Branch: aws.StringValue(output.RepositoryMetadata.DefaultBranch), + RepositoryId: repo.RepositoryId, + Labels: repo.Labels, + // getting SHA of the branch requires a separate GetBranch call. + // too expensive. for now, we just don't support it. + // SHA: "", + }) + } else { + input := &codecommit.ListBranchesInput{ + RepositoryName: aws.String(repo.Repository), + } + for { + output, err := p.codeCommitClient.ListBranchesWithContext(ctx, input) + if err != nil { + return nil, err + } + for _, branch := range output.Branches { + repos = append(repos, &Repository{ + Organization: repo.Organization, + Repository: repo.Repository, + URL: repo.URL, + Branch: aws.StringValue(branch), + RepositoryId: repo.RepositoryId, + Labels: repo.Labels, + // getting SHA of the branch requires a separate GetBranch call. + // too expensive. for now, we just don't support it. + // SHA: "", + }) + } + input.NextToken = output.NextToken + if aws.StringValue(output.NextToken) == "" { + break + } + } + } + + return repos, nil +} + +func (p *AWSCodeCommitProvider) listRepoNames(ctx context.Context) ([]string, error) { + tagFilters := p.getTagFilters() + repoNames := make([]string, 0) + var err error + + if len(tagFilters) < 1 { + log.Debugf("no tag filer, calling codecommit api to list repos") + listReposInput := &codecommit.ListRepositoriesInput{} + var output *codecommit.ListRepositoriesOutput + for { + output, err = p.codeCommitClient.ListRepositoriesWithContext(ctx, listReposInput) + if err != nil { + break + } + for _, repo := range output.Repositories { + repoNames = append(repoNames, aws.StringValue(repo.RepositoryName)) + } + listReposInput.NextToken = output.NextToken + if aws.StringValue(output.NextToken) == "" { + break + } + } + } else { + log.Debugf("tag filer is specified, calling tagging api to list repos") + discoveryInput := &resourcegroupstaggingapi.GetResourcesInput{ + ResourceTypeFilters: aws.StringSlice([]string{resourceTypeCodeCommitRepository}), + TagFilters: tagFilters, + } + var output *resourcegroupstaggingapi.GetResourcesOutput + for { + output, err = p.taggingClient.GetResourcesWithContext(ctx, discoveryInput) + if err != nil { + break + } + for _, resource := range output.ResourceTagMappingList { + repoArn := aws.StringValue(resource.ResourceARN) + log.Debugf("discovered codecommit repo with arn %s", repoArn) + repoName, extractErr := getCodeCommitRepoName(repoArn) + if extractErr != nil { + log.Warnf("discovered codecommit repoArn %s cannot be parsed due to %v", repoArn, err) + continue + } + repoNames = append(repoNames, repoName) + } + discoveryInput.PaginationToken = output.PaginationToken + if aws.StringValue(output.PaginationToken) == "" { + break + } + } + } + return repoNames, err +} + +func (p *AWSCodeCommitProvider) getTagFilters() []*resourcegroupstaggingapi.TagFilter { + filters := make(map[string]*resourcegroupstaggingapi.TagFilter) + for _, tagFilter := range p.tagFilters { + filter, hasKey := filters[tagFilter.Key] + if !hasKey { + filter = &resourcegroupstaggingapi.TagFilter{ + Key: aws.String(tagFilter.Key), + } + filters[tagFilter.Key] = filter + } + if tagFilter.Value != "" { + filter.Values = append(filter.Values, aws.String(tagFilter.Value)) + } + } + return maps.Values(filters) +} + +func getCodeCommitRepoName(repoArn string) (string, error) { + parsedArn, err := arn.Parse(repoArn) + if err != nil { + return "", fmt.Errorf("failed to parse codecommit repository ARN: %w", err) + } + // see: https://docs.aws.amazon.com/codecommit/latest/userguide/auth-and-access-control-permissions-reference.html + // arn:aws:codecommit:region:account-id:repository-name + return parsedArn.Resource, nil +} + +// getCodeCommitFIPSEndpoint transforms provided https:// codecommit URL to a FIPS-compliant endpoint. +// note that the specified region must support FIPS, otherwise the returned URL won't be reachable +// see: https://docs.aws.amazon.com/codecommit/latest/userguide/regions.html#regions-git +func getCodeCommitFIPSEndpoint(repoUrl string) (string, error) { + if strings.HasPrefix(repoUrl, prefixGitUrlHttpsFIPS) { + log.Debugf("provided repoUrl %s is already a fips endpoint", repoUrl) + return repoUrl, nil + } + if !strings.HasPrefix(repoUrl, prefixGitUrlHttps) { + return "", fmt.Errorf("the provided https endpoint isn't recognized, cannot be transformed to FIPS endpoint: %s", repoUrl) + } + // we already have the prefix, so we guarantee to replace exactly the prefix only. + return strings.Replace(repoUrl, prefixGitUrlHttps, prefixGitUrlHttpsFIPS, 1), nil +} + +func hasAwsError(err error, codes ...string) bool { + if awsErr, ok := err.(awserr.Error); ok { + return slices.Contains(codes, awsErr.Code()) + } + return false +} + +// toAbsolutePath transforms a path input to absolute path, as required by AWS CodeCommit +// see https://docs.aws.amazon.com/codecommit/latest/APIReference/API_GetFolder.html +func toAbsolutePath(path string) string { + if filepath.IsAbs(path) { + return path + } + return filepath.ToSlash(filepath.Join("/", path)) +} + +func createAWSDiscoveryClients(_ context.Context, role string, region string) (*resourcegroupstaggingapi.ResourceGroupsTaggingAPI, *codecommit.CodeCommit, error) { + podSession, err := session.NewSession() + if err != nil { + return nil, nil, fmt.Errorf("error creating new AWS pod session: %w", err) + } + discoverySession := podSession + // assume role if provided - this allows cross account CodeCommit repo discovery. + if role != "" { + log.Debugf("role %s is provided for AWS CodeCommit discovery", role) + assumeRoleCreds := stscreds.NewCredentials(podSession, role) + discoverySession, err = session.NewSession(&aws.Config{ + Credentials: assumeRoleCreds, + }) + if err != nil { + return nil, nil, fmt.Errorf("error creating new AWS discovery session: %s", err) + } + } else { + log.Debugf("role is not provided for AWS CodeCommit discovery, using pod role") + } + // use region explicitly if provided - this allows cross region CodeCommit repo discovery. + if region != "" { + log.Debugf("region %s is provided for AWS CodeCommit discovery", region) + discoverySession = discoverySession.Copy(&aws.Config{ + Region: aws.String(region), + }) + } else { + log.Debugf("region is not provided for AWS CodeCommit discovery, using pod region") + } + + taggingClient := resourcegroupstaggingapi.New(discoverySession) + codeCommitClient := codecommit.New(discoverySession) + + return taggingClient, codeCommitClient, nil +} diff --git a/applicationset/services/scm_provider/aws_codecommit/mocks/AWSCodeCommitClient.go b/applicationset/services/scm_provider/aws_codecommit/mocks/AWSCodeCommitClient.go new file mode 100644 index 0000000000000..b9d6f6a5d5956 --- /dev/null +++ b/applicationset/services/scm_provider/aws_codecommit/mocks/AWSCodeCommitClient.go @@ -0,0 +1,321 @@ +// Code generated by mockery v2.26.1. DO NOT EDIT. + +package mocks + +import ( + context "context" + + codecommit "github.com/aws/aws-sdk-go/service/codecommit" + + mock "github.com/stretchr/testify/mock" + + request "github.com/aws/aws-sdk-go/aws/request" +) + +// AWSCodeCommitClient is an autogenerated mock type for the AWSCodeCommitClient type +type AWSCodeCommitClient struct { + mock.Mock +} + +type AWSCodeCommitClient_Expecter struct { + mock *mock.Mock +} + +func (_m *AWSCodeCommitClient) EXPECT() *AWSCodeCommitClient_Expecter { + return &AWSCodeCommitClient_Expecter{mock: &_m.Mock} +} + +// GetFolderWithContext provides a mock function with given fields: _a0, _a1, _a2 +func (_m *AWSCodeCommitClient) GetFolderWithContext(_a0 context.Context, _a1 *codecommit.GetFolderInput, _a2 ...request.Option) (*codecommit.GetFolderOutput, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *codecommit.GetFolderOutput + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *codecommit.GetFolderInput, ...request.Option) (*codecommit.GetFolderOutput, error)); ok { + return rf(_a0, _a1, _a2...) + } + if rf, ok := ret.Get(0).(func(context.Context, *codecommit.GetFolderInput, ...request.Option) *codecommit.GetFolderOutput); ok { + r0 = rf(_a0, _a1, _a2...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*codecommit.GetFolderOutput) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *codecommit.GetFolderInput, ...request.Option) error); ok { + r1 = rf(_a0, _a1, _a2...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AWSCodeCommitClient_GetFolderWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFolderWithContext' +type AWSCodeCommitClient_GetFolderWithContext_Call struct { + *mock.Call +} + +// GetFolderWithContext is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *codecommit.GetFolderInput +// - _a2 ...request.Option +func (_e *AWSCodeCommitClient_Expecter) GetFolderWithContext(_a0 interface{}, _a1 interface{}, _a2 ...interface{}) *AWSCodeCommitClient_GetFolderWithContext_Call { + return &AWSCodeCommitClient_GetFolderWithContext_Call{Call: _e.mock.On("GetFolderWithContext", + append([]interface{}{_a0, _a1}, _a2...)...)} +} + +func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) Run(run func(_a0 context.Context, _a1 *codecommit.GetFolderInput, _a2 ...request.Option)) *AWSCodeCommitClient_GetFolderWithContext_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]request.Option, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(request.Option) + } + } + run(args[0].(context.Context), args[1].(*codecommit.GetFolderInput), variadicArgs...) + }) + return _c +} + +func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) Return(_a0 *codecommit.GetFolderOutput, _a1 error) *AWSCodeCommitClient_GetFolderWithContext_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) RunAndReturn(run func(context.Context, *codecommit.GetFolderInput, ...request.Option) (*codecommit.GetFolderOutput, error)) *AWSCodeCommitClient_GetFolderWithContext_Call { + _c.Call.Return(run) + return _c +} + +// GetRepositoryWithContext provides a mock function with given fields: _a0, _a1, _a2 +func (_m *AWSCodeCommitClient) GetRepositoryWithContext(_a0 context.Context, _a1 *codecommit.GetRepositoryInput, _a2 ...request.Option) (*codecommit.GetRepositoryOutput, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *codecommit.GetRepositoryOutput + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *codecommit.GetRepositoryInput, ...request.Option) (*codecommit.GetRepositoryOutput, error)); ok { + return rf(_a0, _a1, _a2...) + } + if rf, ok := ret.Get(0).(func(context.Context, *codecommit.GetRepositoryInput, ...request.Option) *codecommit.GetRepositoryOutput); ok { + r0 = rf(_a0, _a1, _a2...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*codecommit.GetRepositoryOutput) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *codecommit.GetRepositoryInput, ...request.Option) error); ok { + r1 = rf(_a0, _a1, _a2...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AWSCodeCommitClient_GetRepositoryWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRepositoryWithContext' +type AWSCodeCommitClient_GetRepositoryWithContext_Call struct { + *mock.Call +} + +// GetRepositoryWithContext is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *codecommit.GetRepositoryInput +// - _a2 ...request.Option +func (_e *AWSCodeCommitClient_Expecter) GetRepositoryWithContext(_a0 interface{}, _a1 interface{}, _a2 ...interface{}) *AWSCodeCommitClient_GetRepositoryWithContext_Call { + return &AWSCodeCommitClient_GetRepositoryWithContext_Call{Call: _e.mock.On("GetRepositoryWithContext", + append([]interface{}{_a0, _a1}, _a2...)...)} +} + +func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) Run(run func(_a0 context.Context, _a1 *codecommit.GetRepositoryInput, _a2 ...request.Option)) *AWSCodeCommitClient_GetRepositoryWithContext_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]request.Option, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(request.Option) + } + } + run(args[0].(context.Context), args[1].(*codecommit.GetRepositoryInput), variadicArgs...) + }) + return _c +} + +func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) Return(_a0 *codecommit.GetRepositoryOutput, _a1 error) *AWSCodeCommitClient_GetRepositoryWithContext_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) RunAndReturn(run func(context.Context, *codecommit.GetRepositoryInput, ...request.Option) (*codecommit.GetRepositoryOutput, error)) *AWSCodeCommitClient_GetRepositoryWithContext_Call { + _c.Call.Return(run) + return _c +} + +// ListBranchesWithContext provides a mock function with given fields: _a0, _a1, _a2 +func (_m *AWSCodeCommitClient) ListBranchesWithContext(_a0 context.Context, _a1 *codecommit.ListBranchesInput, _a2 ...request.Option) (*codecommit.ListBranchesOutput, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *codecommit.ListBranchesOutput + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *codecommit.ListBranchesInput, ...request.Option) (*codecommit.ListBranchesOutput, error)); ok { + return rf(_a0, _a1, _a2...) + } + if rf, ok := ret.Get(0).(func(context.Context, *codecommit.ListBranchesInput, ...request.Option) *codecommit.ListBranchesOutput); ok { + r0 = rf(_a0, _a1, _a2...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*codecommit.ListBranchesOutput) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *codecommit.ListBranchesInput, ...request.Option) error); ok { + r1 = rf(_a0, _a1, _a2...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AWSCodeCommitClient_ListBranchesWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListBranchesWithContext' +type AWSCodeCommitClient_ListBranchesWithContext_Call struct { + *mock.Call +} + +// ListBranchesWithContext is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *codecommit.ListBranchesInput +// - _a2 ...request.Option +func (_e *AWSCodeCommitClient_Expecter) ListBranchesWithContext(_a0 interface{}, _a1 interface{}, _a2 ...interface{}) *AWSCodeCommitClient_ListBranchesWithContext_Call { + return &AWSCodeCommitClient_ListBranchesWithContext_Call{Call: _e.mock.On("ListBranchesWithContext", + append([]interface{}{_a0, _a1}, _a2...)...)} +} + +func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) Run(run func(_a0 context.Context, _a1 *codecommit.ListBranchesInput, _a2 ...request.Option)) *AWSCodeCommitClient_ListBranchesWithContext_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]request.Option, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(request.Option) + } + } + run(args[0].(context.Context), args[1].(*codecommit.ListBranchesInput), variadicArgs...) + }) + return _c +} + +func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) Return(_a0 *codecommit.ListBranchesOutput, _a1 error) *AWSCodeCommitClient_ListBranchesWithContext_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) RunAndReturn(run func(context.Context, *codecommit.ListBranchesInput, ...request.Option) (*codecommit.ListBranchesOutput, error)) *AWSCodeCommitClient_ListBranchesWithContext_Call { + _c.Call.Return(run) + return _c +} + +// ListRepositoriesWithContext provides a mock function with given fields: _a0, _a1, _a2 +func (_m *AWSCodeCommitClient) ListRepositoriesWithContext(_a0 context.Context, _a1 *codecommit.ListRepositoriesInput, _a2 ...request.Option) (*codecommit.ListRepositoriesOutput, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *codecommit.ListRepositoriesOutput + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *codecommit.ListRepositoriesInput, ...request.Option) (*codecommit.ListRepositoriesOutput, error)); ok { + return rf(_a0, _a1, _a2...) + } + if rf, ok := ret.Get(0).(func(context.Context, *codecommit.ListRepositoriesInput, ...request.Option) *codecommit.ListRepositoriesOutput); ok { + r0 = rf(_a0, _a1, _a2...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*codecommit.ListRepositoriesOutput) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *codecommit.ListRepositoriesInput, ...request.Option) error); ok { + r1 = rf(_a0, _a1, _a2...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AWSCodeCommitClient_ListRepositoriesWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRepositoriesWithContext' +type AWSCodeCommitClient_ListRepositoriesWithContext_Call struct { + *mock.Call +} + +// ListRepositoriesWithContext is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *codecommit.ListRepositoriesInput +// - _a2 ...request.Option +func (_e *AWSCodeCommitClient_Expecter) ListRepositoriesWithContext(_a0 interface{}, _a1 interface{}, _a2 ...interface{}) *AWSCodeCommitClient_ListRepositoriesWithContext_Call { + return &AWSCodeCommitClient_ListRepositoriesWithContext_Call{Call: _e.mock.On("ListRepositoriesWithContext", + append([]interface{}{_a0, _a1}, _a2...)...)} +} + +func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) Run(run func(_a0 context.Context, _a1 *codecommit.ListRepositoriesInput, _a2 ...request.Option)) *AWSCodeCommitClient_ListRepositoriesWithContext_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]request.Option, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(request.Option) + } + } + run(args[0].(context.Context), args[1].(*codecommit.ListRepositoriesInput), variadicArgs...) + }) + return _c +} + +func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) Return(_a0 *codecommit.ListRepositoriesOutput, _a1 error) *AWSCodeCommitClient_ListRepositoriesWithContext_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) RunAndReturn(run func(context.Context, *codecommit.ListRepositoriesInput, ...request.Option) (*codecommit.ListRepositoriesOutput, error)) *AWSCodeCommitClient_ListRepositoriesWithContext_Call { + _c.Call.Return(run) + return _c +} + +type mockConstructorTestingTNewAWSCodeCommitClient interface { + mock.TestingT + Cleanup(func()) +} + +// NewAWSCodeCommitClient creates a new instance of AWSCodeCommitClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewAWSCodeCommitClient(t mockConstructorTestingTNewAWSCodeCommitClient) *AWSCodeCommitClient { + mock := &AWSCodeCommitClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/applicationset/services/scm_provider/aws_codecommit/mocks/AWSTaggingClient.go b/applicationset/services/scm_provider/aws_codecommit/mocks/AWSTaggingClient.go new file mode 100644 index 0000000000000..9acd8979b7818 --- /dev/null +++ b/applicationset/services/scm_provider/aws_codecommit/mocks/AWSTaggingClient.go @@ -0,0 +1,110 @@ +// Code generated by mockery v2.26.1. DO NOT EDIT. + +package mocks + +import ( + context "context" + + request "github.com/aws/aws-sdk-go/aws/request" + mock "github.com/stretchr/testify/mock" + + resourcegroupstaggingapi "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" +) + +// AWSTaggingClient is an autogenerated mock type for the AWSTaggingClient type +type AWSTaggingClient struct { + mock.Mock +} + +type AWSTaggingClient_Expecter struct { + mock *mock.Mock +} + +func (_m *AWSTaggingClient) EXPECT() *AWSTaggingClient_Expecter { + return &AWSTaggingClient_Expecter{mock: &_m.Mock} +} + +// GetResourcesWithContext provides a mock function with given fields: _a0, _a1, _a2 +func (_m *AWSTaggingClient) GetResourcesWithContext(_a0 context.Context, _a1 *resourcegroupstaggingapi.GetResourcesInput, _a2 ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *resourcegroupstaggingapi.GetResourcesOutput + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error)); ok { + return rf(_a0, _a1, _a2...) + } + if rf, ok := ret.Get(0).(func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) *resourcegroupstaggingapi.GetResourcesOutput); ok { + r0 = rf(_a0, _a1, _a2...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*resourcegroupstaggingapi.GetResourcesOutput) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) error); ok { + r1 = rf(_a0, _a1, _a2...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AWSTaggingClient_GetResourcesWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetResourcesWithContext' +type AWSTaggingClient_GetResourcesWithContext_Call struct { + *mock.Call +} + +// GetResourcesWithContext is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *resourcegroupstaggingapi.GetResourcesInput +// - _a2 ...request.Option +func (_e *AWSTaggingClient_Expecter) GetResourcesWithContext(_a0 interface{}, _a1 interface{}, _a2 ...interface{}) *AWSTaggingClient_GetResourcesWithContext_Call { + return &AWSTaggingClient_GetResourcesWithContext_Call{Call: _e.mock.On("GetResourcesWithContext", + append([]interface{}{_a0, _a1}, _a2...)...)} +} + +func (_c *AWSTaggingClient_GetResourcesWithContext_Call) Run(run func(_a0 context.Context, _a1 *resourcegroupstaggingapi.GetResourcesInput, _a2 ...request.Option)) *AWSTaggingClient_GetResourcesWithContext_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]request.Option, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(request.Option) + } + } + run(args[0].(context.Context), args[1].(*resourcegroupstaggingapi.GetResourcesInput), variadicArgs...) + }) + return _c +} + +func (_c *AWSTaggingClient_GetResourcesWithContext_Call) Return(_a0 *resourcegroupstaggingapi.GetResourcesOutput, _a1 error) *AWSTaggingClient_GetResourcesWithContext_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *AWSTaggingClient_GetResourcesWithContext_Call) RunAndReturn(run func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error)) *AWSTaggingClient_GetResourcesWithContext_Call { + _c.Call.Return(run) + return _c +} + +type mockConstructorTestingTNewAWSTaggingClient interface { + mock.TestingT + Cleanup(func()) +} + +// NewAWSTaggingClient creates a new instance of AWSTaggingClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewAWSTaggingClient(t mockConstructorTestingTNewAWSTaggingClient) *AWSTaggingClient { + mock := &AWSTaggingClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/applicationset/services/scm_provider/aws_codecommit_test.go b/applicationset/services/scm_provider/aws_codecommit_test.go new file mode 100644 index 0000000000000..3a4f7c1a9a6a8 --- /dev/null +++ b/applicationset/services/scm_provider/aws_codecommit_test.go @@ -0,0 +1,483 @@ +package scm_provider + +import ( + "context" + "errors" + "sort" + "testing" + + "github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider/aws_codecommit/mocks" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/codecommit" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +type awsCodeCommitTestRepository struct { + name string + id string + arn string + accountId string + defaultBranch string + expectedCloneUrl string + getRepositoryError error + getRepositoryNilMetadata bool + valid bool +} + +func TestAWSCodeCommitListRepos(t *testing.T) { + testCases := []struct { + name string + repositories []*awsCodeCommitTestRepository + cloneProtocol string + tagFilters []*v1alpha1.TagFilter + expectTagFilters []*resourcegroupstaggingapi.TagFilter + listRepositoryError error + expectOverallError bool + expectListAtCodeCommit bool + }{ + { + name: "ListRepos by tag with https", + cloneProtocol: "https", + repositories: []*awsCodeCommitTestRepository{ + { + name: "repo1", + id: "8235624d-d248-4df9-a983-2558b01dbe83", + arn: "arn:aws:codecommit:us-east-1:111111111111:repo1", + defaultBranch: "main", + expectedCloneUrl: "https://git-codecommit.us-east-1.amazonaws.com/v1/repos/repo1", + valid: true, + }, + }, + tagFilters: []*v1alpha1.TagFilter{ + {Key: "key1", Value: "value1"}, + {Key: "key1", Value: "value2"}, + {Key: "key2"}, + }, + expectTagFilters: []*resourcegroupstaggingapi.TagFilter{ + {Key: aws.String("key1"), Values: aws.StringSlice([]string{"value1", "value2"})}, + {Key: aws.String("key2")}, + }, + expectOverallError: false, + expectListAtCodeCommit: false, + }, + { + name: "ListRepos by tag with https-fips", + cloneProtocol: "https-fips", + repositories: []*awsCodeCommitTestRepository{ + { + name: "repo1", + id: "8235624d-d248-4df9-a983-2558b01dbe83", + arn: "arn:aws:codecommit:us-east-1:111111111111:repo1", + defaultBranch: "main", + expectedCloneUrl: "https://git-codecommit-fips.us-east-1.amazonaws.com/v1/repos/repo1", + valid: true, + }, + }, + tagFilters: []*v1alpha1.TagFilter{ + {Key: "key1"}, + }, + expectTagFilters: []*resourcegroupstaggingapi.TagFilter{ + {Key: aws.String("key1")}, + }, + expectOverallError: false, + expectListAtCodeCommit: false, + }, + { + name: "ListRepos without tag with invalid repo", + cloneProtocol: "ssh", + repositories: []*awsCodeCommitTestRepository{ + { + name: "repo1", + id: "8235624d-d248-4df9-a983-2558b01dbe83", + arn: "arn:aws:codecommit:us-east-1:111111111111:repo1", + defaultBranch: "main", + expectedCloneUrl: "ssh://git-codecommit.us-east-1.amazonaws.com/v1/repos/repo1", + valid: true, + }, + { + name: "repo2", + id: "640d5859-d265-4e27-a9fa-e0731eb13ed7", + arn: "arn:aws:codecommit:us-east-1:111111111111:repo2", + valid: false, + }, + { + name: "repo3-nil-metadata", + id: "24a6ee96-d3a0-4be6-a595-c5e5b1ab1617", + arn: "arn:aws:codecommit:us-east-1:111111111111:repo3-nil-metadata", + getRepositoryNilMetadata: true, + valid: false, + }, + }, + expectOverallError: false, + expectListAtCodeCommit: true, + }, + { + name: "ListRepos with invalid protocol", + cloneProtocol: "invalid-protocol", + repositories: []*awsCodeCommitTestRepository{ + { + name: "repo1", + id: "8235624d-d248-4df9-a983-2558b01dbe83", + arn: "arn:aws:codecommit:us-east-1:111111111111:repo1", + defaultBranch: "main", + valid: true, + }, + }, + expectOverallError: true, + expectListAtCodeCommit: true, + }, + { + name: "ListRepos error on listRepos", + cloneProtocol: "https", + listRepositoryError: errors.New("list repo error"), + expectOverallError: true, + expectListAtCodeCommit: true, + }, + { + name: "ListRepos error on getRepo", + cloneProtocol: "https", + repositories: []*awsCodeCommitTestRepository{ + { + name: "repo1", + id: "8235624d-d248-4df9-a983-2558b01dbe83", + arn: "arn:aws:codecommit:us-east-1:111111111111:repo1", + defaultBranch: "main", + getRepositoryError: errors.New("get repo error"), + valid: true, + }, + }, + expectOverallError: true, + expectListAtCodeCommit: true, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + codeCommitClient := mocks.NewAWSCodeCommitClient(t) + taggingClient := mocks.NewAWSTaggingClient(t) + ctx := context.Background() + codecommitRepoNameIdPairs := make([]*codecommit.RepositoryNameIdPair, 0) + resourceTaggings := make([]*resourcegroupstaggingapi.ResourceTagMapping, 0) + validRepositories := make([]*awsCodeCommitTestRepository, 0) + + for _, repo := range testCase.repositories { + repoMetadata := &codecommit.RepositoryMetadata{ + AccountId: aws.String(repo.accountId), + Arn: aws.String(repo.arn), + CloneUrlHttp: aws.String("https://git-codecommit.us-east-1.amazonaws.com/v1/repos/" + repo.name), + CloneUrlSsh: aws.String("ssh://git-codecommit.us-east-1.amazonaws.com/v1/repos/" + repo.name), + DefaultBranch: aws.String(repo.defaultBranch), + RepositoryId: aws.String(repo.id), + RepositoryName: aws.String(repo.name), + } + if repo.getRepositoryNilMetadata { + repoMetadata = nil + } + codeCommitClient.EXPECT(). + GetRepositoryWithContext(ctx, &codecommit.GetRepositoryInput{RepositoryName: aws.String(repo.name)}). + Return(&codecommit.GetRepositoryOutput{RepositoryMetadata: repoMetadata}, repo.getRepositoryError) + codecommitRepoNameIdPairs = append(codecommitRepoNameIdPairs, &codecommit.RepositoryNameIdPair{ + RepositoryId: aws.String(repo.id), + RepositoryName: aws.String(repo.name), + }) + resourceTaggings = append(resourceTaggings, &resourcegroupstaggingapi.ResourceTagMapping{ + ResourceARN: aws.String(repo.arn), + }) + if repo.valid { + validRepositories = append(validRepositories, repo) + } + } + + if testCase.expectListAtCodeCommit { + codeCommitClient.EXPECT(). + ListRepositoriesWithContext(ctx, &codecommit.ListRepositoriesInput{}). + Return(&codecommit.ListRepositoriesOutput{ + Repositories: codecommitRepoNameIdPairs, + }, testCase.listRepositoryError) + } else { + taggingClient.EXPECT(). + GetResourcesWithContext(ctx, mock.MatchedBy(equalIgnoringTagFilterOrder(&resourcegroupstaggingapi.GetResourcesInput{ + TagFilters: testCase.expectTagFilters, + ResourceTypeFilters: aws.StringSlice([]string{resourceTypeCodeCommitRepository}), + }))). + Return(&resourcegroupstaggingapi.GetResourcesOutput{ + ResourceTagMappingList: resourceTaggings, + }, testCase.listRepositoryError) + } + + provider := &AWSCodeCommitProvider{ + codeCommitClient: codeCommitClient, + taggingClient: taggingClient, + tagFilters: testCase.tagFilters, + } + repos, err := provider.ListRepos(ctx, testCase.cloneProtocol) + if testCase.expectOverallError { + assert.Error(t, err) + } else { + assert.Len(t, repos, len(validRepositories)) + for i, repo := range repos { + originRepo := validRepositories[i] + assert.Equal(t, originRepo.accountId, repo.Organization) + assert.Equal(t, originRepo.name, repo.Repository) + assert.Equal(t, originRepo.id, repo.RepositoryId) + assert.Equal(t, originRepo.defaultBranch, repo.Branch) + assert.Equal(t, originRepo.expectedCloneUrl, repo.URL) + assert.Empty(t, repo.SHA, "SHA is always empty") + } + } + }) + } +} + +func TestAWSCodeCommitRepoHasPath(t *testing.T) { + organization := "111111111111" + repoName := "repo1" + branch := "main" + + testCases := []struct { + name string + path string + expectedGetFolderPath string + getFolderOutput *codecommit.GetFolderOutput + getFolderError error + expectOverallError bool + expectedResult bool + }{ + { + name: "RepoHasPath on regular file", + path: "lib/config.yaml", + expectedGetFolderPath: "/lib", + getFolderOutput: &codecommit.GetFolderOutput{ + Files: []*codecommit.File{ + {RelativePath: aws.String("config.yaml")}, + }, + }, + expectOverallError: false, + expectedResult: true, + }, + { + name: "RepoHasPath on folder", + path: "lib/config", + expectedGetFolderPath: "/lib", + getFolderOutput: &codecommit.GetFolderOutput{ + SubFolders: []*codecommit.Folder{ + {RelativePath: aws.String("config")}, + }, + }, + expectOverallError: false, + expectedResult: true, + }, + { + name: "RepoHasPath on submodules", + path: "/lib/submodule/", + expectedGetFolderPath: "/lib", + getFolderOutput: &codecommit.GetFolderOutput{ + SubModules: []*codecommit.SubModule{ + {RelativePath: aws.String("submodule")}, + }, + }, + expectOverallError: false, + expectedResult: true, + }, + { + name: "RepoHasPath on symlink", + path: "./lib/service.json", + expectedGetFolderPath: "/lib", + getFolderOutput: &codecommit.GetFolderOutput{ + SymbolicLinks: []*codecommit.SymbolicLink{ + {RelativePath: aws.String("service.json")}, + }, + }, + expectOverallError: false, + expectedResult: true, + }, + { + name: "RepoHasPath when no match", + path: "no-match.json", + expectedGetFolderPath: "/", + getFolderOutput: &codecommit.GetFolderOutput{ + Files: []*codecommit.File{ + {RelativePath: aws.String("config.yaml")}, + }, + SubFolders: []*codecommit.Folder{ + {RelativePath: aws.String("config")}, + }, + SubModules: []*codecommit.SubModule{ + {RelativePath: aws.String("submodule")}, + }, + SymbolicLinks: []*codecommit.SymbolicLink{ + {RelativePath: aws.String("service.json")}, + }, + }, + expectOverallError: false, + expectedResult: false, + }, + { + name: "RepoHasPath when parent folder not found", + path: "lib/submodule", + expectedGetFolderPath: "/lib", + getFolderError: &codecommit.FolderDoesNotExistException{}, + expectOverallError: false, + }, + { + name: "RepoHasPath when unknown error", + path: "lib/submodule", + expectedGetFolderPath: "/lib", + getFolderError: errors.New("unknown error"), + expectOverallError: true, + }, + { + name: "RepoHasPath on root folder - './'", + path: "./", + expectOverallError: false, + expectedResult: true, + }, + { + name: "RepoHasPath on root folder - '/'", + path: "/", + expectOverallError: false, + expectedResult: true, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + codeCommitClient := mocks.NewAWSCodeCommitClient(t) + taggingClient := mocks.NewAWSTaggingClient(t) + ctx := context.Background() + if testCase.expectedGetFolderPath != "" { + codeCommitClient.EXPECT(). + GetFolderWithContext(ctx, &codecommit.GetFolderInput{ + CommitSpecifier: aws.String(branch), + FolderPath: aws.String(testCase.expectedGetFolderPath), + RepositoryName: aws.String(repoName), + }). + Return(testCase.getFolderOutput, testCase.getFolderError) + } + provider := &AWSCodeCommitProvider{ + codeCommitClient: codeCommitClient, + taggingClient: taggingClient, + } + actual, err := provider.RepoHasPath(ctx, &Repository{ + Organization: organization, + Repository: repoName, + Branch: branch, + }, testCase.path) + if testCase.expectOverallError { + assert.Error(t, err) + } else { + assert.Equal(t, testCase.expectedResult, actual) + } + }) + } +} + +func TestAWSCodeCommitGetBranches(t *testing.T) { + name := "repo1" + id := "1a64adc4-2fb5-4abd-afe7-127984ba83c0" + defaultBranch := "main" + organization := "111111111111" + cloneUrl := "https://git-codecommit.us-east-1.amazonaws.com/v1/repos/repo1" + + testCases := []struct { + name string + branches []string + apiError error + expectOverallError bool + allBranches bool + }{ + { + name: "GetBranches all branches", + branches: []string{"main", "feature/codecommit", "chore/go-upgrade"}, + allBranches: true, + }, + { + name: "GetBranches default branch only", + allBranches: false, + }, + { + name: "GetBranches default branch only", + allBranches: false, + }, + { + name: "GetBranches all branches on api error", + apiError: errors.New("api error"), + expectOverallError: true, + allBranches: true, + }, + { + name: "GetBranches default branch on api error", + apiError: errors.New("api error"), + expectOverallError: true, + allBranches: false, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + codeCommitClient := mocks.NewAWSCodeCommitClient(t) + taggingClient := mocks.NewAWSTaggingClient(t) + ctx := context.Background() + if testCase.allBranches { + codeCommitClient.EXPECT(). + ListBranchesWithContext(ctx, &codecommit.ListBranchesInput{ + RepositoryName: aws.String(name), + }). + Return(&codecommit.ListBranchesOutput{Branches: aws.StringSlice(testCase.branches)}, testCase.apiError) + } else { + codeCommitClient.EXPECT(). + GetRepositoryWithContext(ctx, &codecommit.GetRepositoryInput{RepositoryName: aws.String(name)}). + Return(&codecommit.GetRepositoryOutput{RepositoryMetadata: &codecommit.RepositoryMetadata{ + AccountId: aws.String(organization), + DefaultBranch: aws.String(defaultBranch), + }}, testCase.apiError) + } + provider := &AWSCodeCommitProvider{ + codeCommitClient: codeCommitClient, + taggingClient: taggingClient, + allBranches: testCase.allBranches, + } + actual, err := provider.GetBranches(ctx, &Repository{ + Organization: organization, + Repository: name, + URL: cloneUrl, + RepositoryId: id, + }) + if testCase.expectOverallError { + assert.Error(t, err) + } else { + assertCopiedProperties := func(repo *Repository) { + assert.Equal(t, id, repo.RepositoryId) + assert.Equal(t, name, repo.Repository) + assert.Equal(t, cloneUrl, repo.URL) + assert.Equal(t, organization, repo.Organization) + assert.Empty(t, repo.SHA) + } + actualBranches := make([]string, 0) + for _, repo := range actual { + assertCopiedProperties(repo) + actualBranches = append(actualBranches, repo.Branch) + } + if testCase.allBranches { + assert.ElementsMatch(t, testCase.branches, actualBranches) + } else { + assert.ElementsMatch(t, []string{defaultBranch}, actualBranches) + } + } + }) + } +} + +// equalIgnoringTagFilterOrder provides an argumentMatcher function that can be used to compare equality of GetResourcesInput ignoring the tagFilter ordering. +func equalIgnoringTagFilterOrder(expected *resourcegroupstaggingapi.GetResourcesInput) func(*resourcegroupstaggingapi.GetResourcesInput) bool { + return func(actual *resourcegroupstaggingapi.GetResourcesInput) bool { + sort.Slice(actual.TagFilters, func(i, j int) bool { + return *actual.TagFilters[i].Key < *actual.TagFilters[j].Key + }) + return cmp.Equal(expected, actual) + } +} diff --git a/applicationset/services/scm_provider/bitbucket_cloud.go b/applicationset/services/scm_provider/bitbucket_cloud.go index f2af9d44cdec9..3c453f6b9c17d 100644 --- a/applicationset/services/scm_provider/bitbucket_cloud.go +++ b/applicationset/services/scm_provider/bitbucket_cloud.go @@ -29,7 +29,7 @@ func (c *ExtendedClient) GetContents(repo *Repository, path string) (bool, error urlStr += fmt.Sprintf("/repositories/%s/%s/src/%s/%s?format=meta", c.owner, repo.Repository, repo.SHA, path) body := strings.NewReader("") - req, err := http.NewRequest("GET", urlStr, body) + req, err := http.NewRequest(http.MethodGet, urlStr, body) if err != nil { return false, err } diff --git a/applicationset/services/scm_provider/bitbucket_cloud_test.go b/applicationset/services/scm_provider/bitbucket_cloud_test.go index 359eac17e3f11..fca03e1693ade 100644 --- a/applicationset/services/scm_provider/bitbucket_cloud_test.go +++ b/applicationset/services/scm_provider/bitbucket_cloud_test.go @@ -5,7 +5,6 @@ import ( "fmt" "net/http" "net/http/httptest" - "os" "testing" "github.com/stretchr/testify/assert" @@ -62,7 +61,7 @@ func TestBitbucketHasRepo(t *testing.T) { })) defer func() { testServer.Close() }() - os.Setenv("BITBUCKET_API_BASE_URL", testServer.URL) + t.Setenv("BITBUCKET_API_BASE_URL", testServer.URL) cases := []struct { name, path, repo, owner, sha string status int @@ -449,7 +448,7 @@ func TestBitbucketListRepos(t *testing.T) { })) defer func() { testServer.Close() }() - os.Setenv("BITBUCKET_API_BASE_URL", testServer.URL) + t.Setenv("BITBUCKET_API_BASE_URL", testServer.URL) cases := []struct { name, proto, owner string hasError, allBranches bool diff --git a/applicationset/services/scm_provider/bitbucket_server.go b/applicationset/services/scm_provider/bitbucket_server.go index f8a08bc4ccfc0..9e46569512156 100644 --- a/applicationset/services/scm_provider/bitbucket_server.go +++ b/applicationset/services/scm_provider/bitbucket_server.go @@ -3,6 +3,7 @@ package scm_provider import ( "context" "fmt" + "io" "github.com/argoproj/argo-cd/v2/applicationset/utils" bitbucketv1 "github.com/gfleury/go-bitbucket-v1" @@ -183,8 +184,9 @@ func (b *BitbucketServerProvider) listBranches(repo *Repository) ([]bitbucketv1. func (b *BitbucketServerProvider) getDefaultBranch(org string, repo string) (*bitbucketv1.Branch, error) { response, err := b.client.DefaultApi.GetDefaultBranch(org, repo) - if response != nil && response.StatusCode == 404 { - // There's no default branch i.e. empty repo, not an error + // The API will return 404 if a default branch is set but doesn't exist. In case the repo is empty and default branch is unset, + // we will get an EOF and a nil response. + if (response != nil && response.StatusCode == 404) || (response == nil && err == io.EOF) { return nil, nil } if err != nil { diff --git a/applicationset/services/scm_provider/bitbucket_server_test.go b/applicationset/services/scm_provider/bitbucket_server_test.go index 986e03a895655..d403bd72caaac 100644 --- a/applicationset/services/scm_provider/bitbucket_server_test.go +++ b/applicationset/services/scm_provider/bitbucket_server_test.go @@ -347,7 +347,7 @@ func TestGetBranchesMissingDefault(t *testing.T) { assert.Empty(t, r.Header.Get("Authorization")) switch r.RequestURI { case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": - http.Error(w, "Not found", 404) + http.Error(w, "Not found", http.StatusNotFound) } defaultHandler(t)(w, r) })) @@ -365,12 +365,34 @@ func TestGetBranchesMissingDefault(t *testing.T) { assert.Empty(t, repos) } +func TestGetBranchesEmptyRepo(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Empty(t, r.Header.Get("Authorization")) + switch r.RequestURI { + case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": + return + } + })) + defer ts.Close() + provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) + assert.NoError(t, err) + repos, err := provider.GetBranches(context.Background(), &Repository{ + Organization: "PROJECT", + Repository: "REPO", + URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", + Labels: []string{}, + RepositoryId: 1, + }) + assert.Empty(t, repos) + assert.NoError(t, err) +} + func TestGetBranchesErrorDefaultBranch(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Empty(t, r.Header.Get("Authorization")) switch r.RequestURI { case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": - http.Error(w, "Internal server error", 500) + http.Error(w, "Internal server error", http.StatusInternalServerError) } defaultHandler(t)(w, r) })) @@ -442,7 +464,7 @@ func TestListReposMissingDefaultBranch(t *testing.T) { assert.Empty(t, r.Header.Get("Authorization")) switch r.RequestURI { case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": - http.Error(w, "Not found", 404) + http.Error(w, "Not found", http.StatusNotFound) } defaultHandler(t)(w, r) })) @@ -459,7 +481,7 @@ func TestListReposErrorDefaultBranch(t *testing.T) { assert.Empty(t, r.Header.Get("Authorization")) switch r.RequestURI { case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": - http.Error(w, "Internal server error", 500) + http.Error(w, "Internal server error", http.StatusInternalServerError) } defaultHandler(t)(w, r) })) @@ -516,17 +538,17 @@ func TestBitbucketServerHasPath(t *testing.T) { _, err = io.WriteString(w, `{"type":"FILE"}`) case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/anotherpkg/missing.txt?at=main&limit=100&type=true": - http.Error(w, "The path \"anotherpkg/missing.txt\" does not exist at revision \"main\"", 404) + http.Error(w, "The path \"anotherpkg/missing.txt\" does not exist at revision \"main\"", http.StatusNotFound) case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/notathing?at=main&limit=100&type=true": - http.Error(w, "The path \"notathing\" does not exist at revision \"main\"", 404) + http.Error(w, "The path \"notathing\" does not exist at revision \"main\"", http.StatusNotFound) case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/return-redirect?at=main&limit=100&type=true": - http.Redirect(w, r, "http://"+r.Host+"/rest/api/1.0/projects/PROJECT/repos/REPO/browse/redirected?at=main&limit=100&type=true", 301) + http.Redirect(w, r, "http://"+r.Host+"/rest/api/1.0/projects/PROJECT/repos/REPO/browse/redirected?at=main&limit=100&type=true", http.StatusMovedPermanently) case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/redirected?at=main&limit=100&type=true": _, err = io.WriteString(w, `{"type":"DIRECTORY"}`) case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/unauthorized-response?at=main&limit=100&type=true": - http.Error(w, "Authentication failed", 401) + http.Error(w, "Authentication failed", http.StatusUnauthorized) default: t.Fail() diff --git a/applicationset/services/scm_provider/gitea.go b/applicationset/services/scm_provider/gitea.go index d00916051ed05..25554d52af85f 100644 --- a/applicationset/services/scm_provider/gitea.go +++ b/applicationset/services/scm_provider/gitea.go @@ -27,11 +27,13 @@ func NewGiteaProvider(ctx context.Context, owner, token, url string, allBranches if insecure { cookieJar, _ := cookiejar.New(nil) + tr := http.DefaultTransport.(*http.Transport).Clone() + tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + httpClient = &http.Client{ - Jar: cookieJar, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }} + Jar: cookieJar, + Transport: tr, + } } client, err := gitea.NewClient(url, gitea.SetToken(token), gitea.SetHTTPClient(httpClient)) if err != nil { @@ -47,7 +49,7 @@ func NewGiteaProvider(ctx context.Context, owner, token, url string, allBranches func (g *GiteaProvider) GetBranches(ctx context.Context, repo *Repository) ([]*Repository, error) { if !g.allBranches { branch, status, err := g.client.GetRepoBranch(g.owner, repo.Repository, repo.Branch) - if status.StatusCode == 404 { + if status.StatusCode == http.StatusNotFound { return nil, fmt.Errorf("got 404 while getting default branch %q for repo %q - check your repo config: %w", repo.Branch, repo.Repository, err) } if err != nil { diff --git a/applicationset/services/scm_provider/gitea_test.go b/applicationset/services/scm_provider/gitea_test.go index b8b8af44263b9..3d17e3175c4f8 100644 --- a/applicationset/services/scm_provider/gitea_test.go +++ b/applicationset/services/scm_provider/gitea_test.go @@ -247,7 +247,7 @@ func giteaMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { _, err := io.WriteString(w, testdata.ReposGiteaGoSdkContentsGiteaResponse) require.NoError(t, err) case "/api/v1/repos/gitea/go-sdk/contents/notathing?ref=master": - w.WriteHeader(404) + w.WriteHeader(http.StatusNotFound) _, err := io.WriteString(w, `{"errors":["object does not exist [id: , rel_path: notathing]"],"message":"GetContentsOrList","url":"https://gitea.com/api/swagger"}`) require.NoError(t, err) default: diff --git a/applicationset/services/scm_provider/github.go b/applicationset/services/scm_provider/github.go index e05b37b8b958c..1a6edae5837e9 100644 --- a/applicationset/services/scm_provider/github.go +++ b/applicationset/services/scm_provider/github.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net/http" "os" "github.com/google/go-github/v35/github" @@ -123,7 +124,7 @@ func (g *GithubProvider) listBranches(ctx context.Context, repo *Repository) ([] if err != nil { var githubErrorResponse *github.ErrorResponse if errors.As(err, &githubErrorResponse) { - if githubErrorResponse.Response.StatusCode == 404 { + if githubErrorResponse.Response.StatusCode == http.StatusNotFound { // Default branch doesn't exist, so the repo is empty. return []github.Branch{}, nil } diff --git a/applicationset/services/scm_provider/github_test.go b/applicationset/services/scm_provider/github_test.go index 8314216cd20d4..d413250f03126 100644 --- a/applicationset/services/scm_provider/github_test.go +++ b/applicationset/services/scm_provider/github_test.go @@ -196,7 +196,7 @@ func githubMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { t.Fail() } default: - w.WriteHeader(404) + w.WriteHeader(http.StatusNotFound) } } } diff --git a/applicationset/services/scm_provider/gitlab.go b/applicationset/services/scm_provider/gitlab.go index cbd0f36293515..ca174de540887 100644 --- a/applicationset/services/scm_provider/gitlab.go +++ b/applicationset/services/scm_provider/gitlab.go @@ -3,41 +3,54 @@ package scm_provider import ( "context" "fmt" + "net/http" "os" pathpkg "path" - gitlab "github.com/xanzy/go-gitlab" + "github.com/argoproj/argo-cd/v2/applicationset/utils" + "github.com/hashicorp/go-retryablehttp" + "github.com/xanzy/go-gitlab" ) type GitlabProvider struct { - client *gitlab.Client - organization string - allBranches bool - includeSubgroups bool + client *gitlab.Client + organization string + allBranches bool + includeSubgroups bool + includeSharedProjects bool + topic string } var _ SCMProviderService = &GitlabProvider{} -func NewGitlabProvider(ctx context.Context, organization string, token string, url string, allBranches, includeSubgroups bool) (*GitlabProvider, error) { +func NewGitlabProvider(ctx context.Context, organization string, token string, url string, allBranches, includeSubgroups, includeSharedProjects, insecure bool, scmRootCAPath, topic string) (*GitlabProvider, error) { // Undocumented environment variable to set a default token, to be used in testing to dodge anonymous rate limits. if token == "" { token = os.Getenv("GITLAB_TOKEN") } var client *gitlab.Client + + tr := http.DefaultTransport.(*http.Transport).Clone() + tr.TLSClientConfig = utils.GetTlsConfig(scmRootCAPath, insecure) + + retryClient := retryablehttp.NewClient() + retryClient.HTTPClient.Transport = tr + if url == "" { var err error - client, err = gitlab.NewClient(token) + client, err = gitlab.NewClient(token, gitlab.WithHTTPClient(retryClient.HTTPClient)) if err != nil { return nil, err } } else { var err error - client, err = gitlab.NewClient(token, gitlab.WithBaseURL(url)) + client, err = gitlab.NewClient(token, gitlab.WithBaseURL(url), gitlab.WithHTTPClient(retryClient.HTTPClient)) if err != nil { return nil, err } } - return &GitlabProvider{client: client, organization: organization, allBranches: allBranches, includeSubgroups: includeSubgroups}, nil + + return &GitlabProvider{client: client, organization: organization, allBranches: allBranches, includeSubgroups: includeSubgroups, includeSharedProjects: includeSharedProjects, topic: topic}, nil } func (g *GitlabProvider) GetBranches(ctx context.Context, repo *Repository) ([]*Repository, error) { @@ -64,8 +77,11 @@ func (g *GitlabProvider) GetBranches(ctx context.Context, repo *Repository) ([]* func (g *GitlabProvider) ListRepos(ctx context.Context, cloneProtocol string) ([]*Repository, error) { opt := &gitlab.ListGroupProjectsOptions{ ListOptions: gitlab.ListOptions{PerPage: 100}, - IncludeSubgroups: &g.includeSubgroups, + IncludeSubGroups: &g.includeSubgroups, + WithShared: &g.includeSharedProjects, + Topic: &g.topic, } + repos := []*Repository{} for { gitlabRepos, resp, err := g.client.Groups.ListGroupProjects(g.organization, opt) @@ -84,12 +100,20 @@ func (g *GitlabProvider) ListRepos(ctx context.Context, cloneProtocol string) ([ return nil, fmt.Errorf("unknown clone protocol for Gitlab %v", cloneProtocol) } + var repoLabels []string + if len(gitlabRepo.Topics) == 0 { + // fallback to for gitlab prior to 14.5 + repoLabels = gitlabRepo.TagList + } else { + repoLabels = gitlabRepo.Topics + } + repos = append(repos, &Repository{ Organization: gitlabRepo.Namespace.FullPath, Repository: gitlabRepo.Path, URL: url, Branch: gitlabRepo.DefaultBranch, - Labels: gitlabRepo.TagList, + Labels: repoLabels, RepositoryId: gitlabRepo.ID, }) } @@ -144,7 +168,11 @@ func (g *GitlabProvider) listBranches(_ context.Context, repo *Repository) ([]gi branches := []gitlab.Branch{} // If we don't specifically want to query for all branches, just use the default branch and call it a day. if !g.allBranches { - gitlabBranch, _, err := g.client.Branches.GetBranch(repo.RepositoryId, repo.Branch, nil) + gitlabBranch, resp, err := g.client.Branches.GetBranch(repo.RepositoryId, repo.Branch, nil) + // 404s are not an error here, just a normal false. + if resp != nil && resp.StatusCode == http.StatusNotFound { + return []gitlab.Branch{}, nil + } if err != nil { return nil, err } @@ -157,6 +185,10 @@ func (g *GitlabProvider) listBranches(_ context.Context, repo *Repository) ([]gi } for { gitlabBranches, resp, err := g.client.Branches.ListBranches(repo.RepositoryId, opt) + // 404s are not an error here, just a normal false. + if resp != nil && resp.StatusCode == http.StatusNotFound { + return []gitlab.Branch{}, nil + } if err != nil { return nil, err } diff --git a/applicationset/services/scm_provider/gitlab_test.go b/applicationset/services/scm_provider/gitlab_test.go index 272eab17c94da..b93616fa8367f 100644 --- a/applicationset/services/scm_provider/gitlab_test.go +++ b/applicationset/services/scm_provider/gitlab_test.go @@ -19,7 +19,7 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { switch r.RequestURI { case "/api/v4": fmt.Println("here1") - case "/api/v4/groups/test-argocd-proton/projects?include_subgroups=false&per_page=100": + case "/api/v4/groups/test-argocd-proton/projects?include_subgroups=false&per_page=100", "/api/v4/groups/test-argocd-proton/projects?include_subgroups=false&per_page=100&topic=&with_shared=false": fmt.Println("here") _, err := io.WriteString(w, `[{ "id": 27084533, @@ -30,8 +30,12 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { "path_with_namespace": "test-argocd-proton/argocd", "created_at": "2021-06-01T17:30:44.724Z", "default_branch": "master", - "tag_list": [], - "topics": [], + "tag_list": [ + "test-topic" + ], + "topics": [ + "test-topic" + ], "ssh_url_to_repo": "git@gitlab.com:test-argocd-proton/argocd.git", "http_url_to_repo": "https://gitlab.com/test-argocd-proton/argocd.git", "web_url": "https://gitlab.com/test-argocd-proton/argocd", @@ -143,6 +147,650 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { if err != nil { t.Fail() } + case "/api/v4/groups/test-argocd-proton/projects?include_subgroups=true&per_page=100&topic=&with_shared=false": + fmt.Println("here") + _, err := io.WriteString(w, `[{ + "id": 27084533, + "description": "", + "name": "argocd", + "name_with_namespace": "test argocd proton / argocd", + "path": "argocd", + "path_with_namespace": "test-argocd-proton/argocd", + "created_at": "2021-06-01T17:30:44.724Z", + "default_branch": "master", + "tag_list": [ + "test-topic", + "specific-topic" + ], + "topics": [ + "test-topic", + "specific-topic" + ], + "ssh_url_to_repo": "git@gitlab.com:test-argocd-proton/argocd.git", + "http_url_to_repo": "https://gitlab.com/test-argocd-proton/argocd.git", + "web_url": "https://gitlab.com/test-argocd-proton/argocd", + "readme_url": null, + "avatar_url": null, + "forks_count": 0, + "star_count": 0, + "last_activity_at": "2021-06-04T08:19:51.656Z", + "namespace": { + "id": 12258515, + "name": "test argocd proton", + "path": "test-argocd-proton", + "kind": "gro* Connection #0 to host gitlab.com left intact up ", + "full_path ": "test - argocd - proton ", + "parent_id ": null, + "avatar_url ": null, + "web_url ": "https: //gitlab.com/groups/test-argocd-proton" + }, + "container_registry_image_prefix": "registry.gitlab.com/test-argocd-proton/argocd", + "_links": { + "self": "https://gitlab.com/api/v4/projects/27084533", + "issues": "https://gitlab.com/api/v4/projects/27084533/issues", + "merge_requests": "https://gitlab.com/api/v4/projects/27084533/merge_requests", + "repo_branches": "https://gitlab.com/api/v4/projects/27084533/repository/branches", + "labels": "https://gitlab.com/api/v4/projects/27084533/labels", + "events": "https://gitlab.com/api/v4/projects/27084533/events", + "members": "https://gitlab.com/api/v4/projects/27084533/members", + "cluster_agents": "https://gitlab.com/api/v4/projects/27084533/cluster_agents" + }, + "packages_enabled": true, + "empty_repo": false, + "archived": false, + "visibility": "public", + "resolve_outdated_diff_discussions": false, + "container_expiration_policy": { + "cadence": "1d", + "enabled": false, + "keep_n": 10, + "older_than": "90d", + "name_regex": ".*", + "name_regex_keep": null, + "next_run_at": "2021-06-02T17:30:44.740Z" + }, + "issues_enabled": true, + "merge_requests_enabled": true, + "wiki_enabled": true, + "jobs_enabled": true, + "snippets_enabled": true, + "container_registry_enabled": true, + "service_desk_enabled": true, + "can_create_merge_request_in": false, + "issues_access_level": "enabled", + "repository_access_level": "enabled", + "merge_requests_access_level": "enabled", + "forking_access_level": "enabled", + "wiki_access_level": "enabled", + "builds_access_level": "enabled", + "snippets_access_level": "enabled", + "pages_access_level": "enabled", + "operations_access_level": "enabled", + "analytics_access_level": "enabled", + "container_registry_access_level": "enabled", + "security_and_compliance_access_level": "private", + "emails_disabled": null, + "shared_runners_enabled": true, + "lfs_enabled": true, + "creator_id": 2378866, + "import_status": "none", + "open_issues_count": 0, + "ci_default_git_depth": 50, + "ci_forward_deployment_enabled": true, + "ci_job_token_scope_enabled": false, + "public_jobs": true, + "build_timeout": 3600, + "auto_cancel_pending_pipelines": "enabled", + "ci_config_path": "", + "shared_with_groups": [], + "only_allow_merge_if_pipeline_succeeds": false, + "allow_merge_on_skipped_pipeline": null, + "restrict_user_defined_variables": false, + "request_access_enabled": true, + "only_allow_merge_if_all_discussions_are_resolved": false, + "remove_source_branch_after_merge": true, + "printing_merge_request_link_enabled": true, + "merge_method": "merge", + "squash_option": "default_off", + "suggestion_commit_message": null, + "merge_commit_template": null, + "squash_commit_template": null, + "auto_devops_enabled": false, + "auto_devops_deploy_strategy": "continuous", + "autoclose_referenced_issues": true, + "keep_latest_artifact": true, + "runner_token_expiration_interval": null, + "approvals_before_merge": 0, + "mirror": false, + "external_authorization_classification_label": "", + "marked_for_deletion_at": null, + "marked_for_deletion_on": null, + "requirements_enabled": true, + "requirements_access_level": "enabled", + "security_and_compliance_enabled": false, + "compliance_frameworks": [], + "issues_template": null, + "merge_requests_template": null, + "merge_pipelines_enabled": false, + "merge_trains_enabled": false + }, + { + "id": 27084538, + "description": "This is a Project from a Subgroup", + "name": "argocd-subgroup", + "name_with_namespace": "test argocd proton / subgroup / argocd-subgroup", + "path": "argocd-subgroup", + "path_with_namespace": "test-argocd-proton/subgroup/argocd-subgroup", + "created_at": "2021-06-01T17:30:44.724Z", + "default_branch": "master", + "tag_list": [ + "test-topic" + ], + "topics": [ + "test-topic" + ], + "ssh_url_to_repo": "git@gitlab.com:test-argocd-proton/subgroup/argocd-subgroup.git", + "http_url_to_repo": "https://gitlab.com/test-argocd-proton/subgroup/argocd-subgroup.git", + "web_url": "https://gitlab.com/test-argocd-proton/subgroup/argocd-subgroup", + "readme_url": null, + "avatar_url": null, + "forks_count": 0, + "star_count": 0, + "last_activity_at": "2021-06-04T08:19:51.656Z", + "namespace": { + "id": 12258542, + "name": "subgroup", + "path": "subgroup", + "kind": "group ", + "full_path ": "test-argocd-proton/subgroup", + "parent_id ": 12258515, + "avatar_url ": null, + "web_url ": "https: //gitlab.com/groups/test-argocd-proton/subgroup" + }, + "container_registry_image_prefix": "registry.gitlab.com/test-argocd-proton/subgroup/argocd", + "_links": { + "self": "https://gitlab.com/api/v4/projects/27084538", + "issues": "https://gitlab.com/api/v4/projects/27084538/issues", + "merge_requests": "https://gitlab.com/api/v4/projects/27084538/merge_requests", + "repo_branches": "https://gitlab.com/api/v4/projects/27084538/repository/branches", + "labels": "https://gitlab.com/api/v4/projects/27084538/labels", + "events": "https://gitlab.com/api/v4/projects/27084538/events", + "members": "https://gitlab.com/api/v4/projects/27084538/members", + "cluster_agents": "https://gitlab.com/api/v4/projects/27084538/cluster_agents" + }, + "packages_enabled": true, + "empty_repo": false, + "archived": false, + "visibility": "public", + "resolve_outdated_diff_discussions": false, + "container_expiration_policy": { + "cadence": "1d", + "enabled": false, + "keep_n": 10, + "older_than": "90d", + "name_regex": ".*", + "name_regex_keep": null, + "next_run_at": "2021-06-02T17:30:44.740Z" + }, + "issues_enabled": true, + "merge_requests_enabled": true, + "wiki_enabled": true, + "jobs_enabled": true, + "snippets_enabled": true, + "container_registry_enabled": true, + "service_desk_enabled": true, + "can_create_merge_request_in": false, + "issues_access_level": "enabled", + "repository_access_level": "enabled", + "merge_requests_access_level": "enabled", + "forking_access_level": "enabled", + "wiki_access_level": "enabled", + "builds_access_level": "enabled", + "snippets_access_level": "enabled", + "pages_access_level": "enabled", + "operations_access_level": "enabled", + "analytics_access_level": "enabled", + "container_registry_access_level": "enabled", + "security_and_compliance_access_level": "private", + "emails_disabled": null, + "shared_runners_enabled": true, + "lfs_enabled": true, + "creator_id": 2378866, + "import_status": "none", + "open_issues_count": 0, + "ci_default_git_depth": 50, + "ci_forward_deployment_enabled": true, + "ci_job_token_scope_enabled": false, + "public_jobs": true, + "build_timeout": 3600, + "auto_cancel_pending_pipelines": "enabled", + "ci_config_path": "", + "shared_with_groups": [], + "only_allow_merge_if_pipeline_succeeds": false, + "allow_merge_on_skipped_pipeline": null, + "restrict_user_defined_variables": false, + "request_access_enabled": true, + "only_allow_merge_if_all_discussions_are_resolved": false, + "remove_source_branch_after_merge": true, + "printing_merge_request_link_enabled": true, + "merge_method": "merge", + "squash_option": "default_off", + "suggestion_commit_message": null, + "merge_commit_template": null, + "squash_commit_template": null, + "auto_devops_enabled": false, + "auto_devops_deploy_strategy": "continuous", + "autoclose_referenced_issues": true, + "keep_latest_artifact": true, + "runner_token_expiration_interval": null, + "approvals_before_merge": 0, + "mirror": false, + "external_authorization_classification_label": "", + "marked_for_deletion_at": null, + "marked_for_deletion_on": null, + "requirements_enabled": true, + "requirements_access_level": "enabled", + "security_and_compliance_enabled": false, + "compliance_frameworks": [], + "issues_template": null, + "merge_requests_template": null, + "merge_pipelines_enabled": false, + "merge_trains_enabled": false + } + ]`) + if err != nil { + t.Fail() + } + case "/api/v4/groups/test-argocd-proton/projects?include_subgroups=false&per_page=100&topic=specific-topic&with_shared=false": + fmt.Println("here") + _, err := io.WriteString(w, `[{ + "id": 27084533, + "description": "", + "name": "argocd", + "name_with_namespace": "test argocd proton / argocd", + "path": "argocd", + "path_with_namespace": "test-argocd-proton/argocd", + "created_at": "2021-06-01T17:30:44.724Z", + "default_branch": "master", + "tag_list": [ + "test-topic", + "specific-topic" + ], + "topics": [ + "test-topic", + "specific-topic" + ], + "ssh_url_to_repo": "git@gitlab.com:test-argocd-proton/argocd.git", + "http_url_to_repo": "https://gitlab.com/test-argocd-proton/argocd.git", + "web_url": "https://gitlab.com/test-argocd-proton/argocd", + "readme_url": null, + "avatar_url": null, + "forks_count": 0, + "star_count": 0, + "last_activity_at": "2021-06-04T08:19:51.656Z", + "namespace": { + "id": 12258515, + "name": "test argocd proton", + "path": "test-argocd-proton", + "kind": "gro* Connection #0 to host gitlab.com left intact up ", + "full_path ": "test - argocd - proton ", + "parent_id ": null, + "avatar_url ": null, + "web_url ": "https: //gitlab.com/groups/test-argocd-proton" + }, + "container_registry_image_prefix": "registry.gitlab.com/test-argocd-proton/argocd", + "_links": { + "self": "https://gitlab.com/api/v4/projects/27084533", + "issues": "https://gitlab.com/api/v4/projects/27084533/issues", + "merge_requests": "https://gitlab.com/api/v4/projects/27084533/merge_requests", + "repo_branches": "https://gitlab.com/api/v4/projects/27084533/repository/branches", + "labels": "https://gitlab.com/api/v4/projects/27084533/labels", + "events": "https://gitlab.com/api/v4/projects/27084533/events", + "members": "https://gitlab.com/api/v4/projects/27084533/members", + "cluster_agents": "https://gitlab.com/api/v4/projects/27084533/cluster_agents" + }, + "packages_enabled": true, + "empty_repo": false, + "archived": false, + "visibility": "public", + "resolve_outdated_diff_discussions": false, + "container_expiration_policy": { + "cadence": "1d", + "enabled": false, + "keep_n": 10, + "older_than": "90d", + "name_regex": ".*", + "name_regex_keep": null, + "next_run_at": "2021-06-02T17:30:44.740Z" + }, + "issues_enabled": true, + "merge_requests_enabled": true, + "wiki_enabled": true, + "jobs_enabled": true, + "snippets_enabled": true, + "container_registry_enabled": true, + "service_desk_enabled": true, + "can_create_merge_request_in": false, + "issues_access_level": "enabled", + "repository_access_level": "enabled", + "merge_requests_access_level": "enabled", + "forking_access_level": "enabled", + "wiki_access_level": "enabled", + "builds_access_level": "enabled", + "snippets_access_level": "enabled", + "pages_access_level": "enabled", + "operations_access_level": "enabled", + "analytics_access_level": "enabled", + "container_registry_access_level": "enabled", + "security_and_compliance_access_level": "private", + "emails_disabled": null, + "shared_runners_enabled": true, + "lfs_enabled": true, + "creator_id": 2378866, + "import_status": "none", + "open_issues_count": 0, + "ci_default_git_depth": 50, + "ci_forward_deployment_enabled": true, + "ci_job_token_scope_enabled": false, + "public_jobs": true, + "build_timeout": 3600, + "auto_cancel_pending_pipelines": "enabled", + "ci_config_path": "", + "shared_with_groups": [], + "only_allow_merge_if_pipeline_succeeds": false, + "allow_merge_on_skipped_pipeline": null, + "restrict_user_defined_variables": false, + "request_access_enabled": true, + "only_allow_merge_if_all_discussions_are_resolved": false, + "remove_source_branch_after_merge": true, + "printing_merge_request_link_enabled": true, + "merge_method": "merge", + "squash_option": "default_off", + "suggestion_commit_message": null, + "merge_commit_template": null, + "squash_commit_template": null, + "auto_devops_enabled": false, + "auto_devops_deploy_strategy": "continuous", + "autoclose_referenced_issues": true, + "keep_latest_artifact": true, + "runner_token_expiration_interval": null, + "approvals_before_merge": 0, + "mirror": false, + "external_authorization_classification_label": "", + "marked_for_deletion_at": null, + "marked_for_deletion_on": null, + "requirements_enabled": true, + "requirements_access_level": "enabled", + "security_and_compliance_enabled": false, + "compliance_frameworks": [], + "issues_template": null, + "merge_requests_template": null, + "merge_pipelines_enabled": false, + "merge_trains_enabled": false + } + ]`) + if err != nil { + t.Fail() + } + case "/api/v4/groups/test-argocd-proton/projects?include_subgroups=true&per_page=100&topic=&with_shared=true": + fmt.Println("here") + _, err := io.WriteString(w, `[{ + "id": 27084533, + "description": "", + "name": "argocd", + "name_with_namespace": "test argocd proton / argocd", + "path": "argocd", + "path_with_namespace": "test-argocd-proton/argocd", + "created_at": "2021-06-01T17:30:44.724Z", + "default_branch": "master", + "tag_list": [ + "test-topic" + ], + "topics": [ + "test-topic" + ], + "ssh_url_to_repo": "git@gitlab.com:test-argocd-proton/argocd.git", + "http_url_to_repo": "https://gitlab.com/test-argocd-proton/argocd.git", + "web_url": "https://gitlab.com/test-argocd-proton/argocd", + "readme_url": null, + "avatar_url": null, + "forks_count": 0, + "star_count": 0, + "last_activity_at": "2021-06-04T08:19:51.656Z", + "namespace": { + "id": 12258515, + "name": "test argocd proton", + "path": "test-argocd-proton", + "kind": "gro* Connection #0 to host gitlab.com left intact up ", + "full_path ": "test - argocd - proton ", + "parent_id ": null, + "avatar_url ": null, + "web_url ": "https: //gitlab.com/groups/test-argocd-proton" + }, + "container_registry_image_prefix": "registry.gitlab.com/test-argocd-proton/argocd", + "_links": { + "self": "https://gitlab.com/api/v4/projects/27084533", + "issues": "https://gitlab.com/api/v4/projects/27084533/issues", + "merge_requests": "https://gitlab.com/api/v4/projects/27084533/merge_requests", + "repo_branches": "https://gitlab.com/api/v4/projects/27084533/repository/branches", + "labels": "https://gitlab.com/api/v4/projects/27084533/labels", + "events": "https://gitlab.com/api/v4/projects/27084533/events", + "members": "https://gitlab.com/api/v4/projects/27084533/members", + "cluster_agents": "https://gitlab.com/api/v4/projects/27084533/cluster_agents" + }, + "packages_enabled": true, + "empty_repo": false, + "archived": false, + "visibility": "public", + "resolve_outdated_diff_discussions": false, + "container_expiration_policy": { + "cadence": "1d", + "enabled": false, + "keep_n": 10, + "older_than": "90d", + "name_regex": ".*", + "name_regex_keep": null, + "next_run_at": "2021-06-02T17:30:44.740Z" + }, + "issues_enabled": true, + "merge_requests_enabled": true, + "wiki_enabled": true, + "jobs_enabled": true, + "snippets_enabled": true, + "container_registry_enabled": true, + "service_desk_enabled": true, + "can_create_merge_request_in": false, + "issues_access_level": "enabled", + "repository_access_level": "enabled", + "merge_requests_access_level": "enabled", + "forking_access_level": "enabled", + "wiki_access_level": "enabled", + "builds_access_level": "enabled", + "snippets_access_level": "enabled", + "pages_access_level": "enabled", + "operations_access_level": "enabled", + "analytics_access_level": "enabled", + "container_registry_access_level": "enabled", + "security_and_compliance_access_level": "private", + "emails_disabled": null, + "shared_runners_enabled": true, + "lfs_enabled": true, + "creator_id": 2378866, + "import_status": "none", + "open_issues_count": 0, + "ci_default_git_depth": 50, + "ci_forward_deployment_enabled": true, + "ci_job_token_scope_enabled": false, + "public_jobs": true, + "build_timeout": 3600, + "auto_cancel_pending_pipelines": "enabled", + "ci_config_path": "", + "shared_with_groups": [], + "only_allow_merge_if_pipeline_succeeds": false, + "allow_merge_on_skipped_pipeline": null, + "restrict_user_defined_variables": false, + "request_access_enabled": true, + "only_allow_merge_if_all_discussions_are_resolved": false, + "remove_source_branch_after_merge": true, + "printing_merge_request_link_enabled": true, + "merge_method": "merge", + "squash_option": "default_off", + "suggestion_commit_message": null, + "merge_commit_template": null, + "squash_commit_template": null, + "auto_devops_enabled": false, + "auto_devops_deploy_strategy": "continuous", + "autoclose_referenced_issues": true, + "keep_latest_artifact": true, + "runner_token_expiration_interval": null, + "approvals_before_merge": 0, + "mirror": false, + "external_authorization_classification_label": "", + "marked_for_deletion_at": null, + "marked_for_deletion_on": null, + "requirements_enabled": true, + "requirements_access_level": "enabled", + "security_and_compliance_enabled": false, + "compliance_frameworks": [], + "issues_template": null, + "merge_requests_template": null, + "merge_pipelines_enabled": false, + "merge_trains_enabled": false + }, + { + "id": 27084534, + "description": "This is a Shared Project", + "name": "shared-argocd", + "name_with_namespace": "shared project to test argocd proton / argocd", + "path": "shared-argocd", + "path_with_namespace": "test-shared-argocd-proton/shared-argocd", + "created_at": "2021-06-11T17:30:44.724Z", + "default_branch": "master", + "tag_list": [ + "test-topic" + ], + "topics": [ + "test-topic" + ], + "ssh_url_to_repo": "git@gitlab.com:test-shared-argocd-proton/shared-argocd.git", + "http_url_to_repo": "https://gitlab.com/test-shared-argocd-proton/shared-argocd.git", + "web_url": "https://gitlab.com/test-shared-argocd-proton/shared-argocd", + "readme_url": null, + "avatar_url": null, + "forks_count": 0, + "star_count": 0, + "last_activity_at": "2021-06-04T08:19:51.656Z", + "namespace": { + "id": 12258518, + "name": "test shared argocd proton", + "path": "test-shared-argocd-proton", + "kind": "group", + "full_path ": "test-shared-argocd-proton", + "parent_id ": null, + "avatar_url ": null, + "web_url ": "https: //gitlab.com/groups/test-shared-argocd-proton" + }, + "container_registry_image_prefix": "registry.gitlab.com/test-shared-argocd-proton/shared-argocd", + "_links": { + "self": "https://gitlab.com/api/v4/projects/27084534", + "issues": "https://gitlab.com/api/v4/projects/27084534/issues", + "merge_requests": "https://gitlab.com/api/v4/projects/27084534/merge_requests", + "repo_branches": "https://gitlab.com/api/v4/projects/27084534/repository/branches", + "labels": "https://gitlab.com/api/v4/projects/27084534/labels", + "events": "https://gitlab.com/api/v4/projects/27084534/events", + "members": "https://gitlab.com/api/v4/projects/27084534/members", + "cluster_agents": "https://gitlab.com/api/v4/projects/27084534/cluster_agents" + }, + "packages_enabled": true, + "empty_repo": false, + "archived": false, + "visibility": "public", + "resolve_outdated_diff_discussions": false, + "container_expiration_policy": { + "cadence": "1d", + "enabled": false, + "keep_n": 10, + "older_than": "90d", + "name_regex": ".*", + "name_regex_keep": null, + "next_run_at": "2021-06-12T17:30:44.740Z" + }, + "issues_enabled": true, + "merge_requests_enabled": true, + "wiki_enabled": true, + "jobs_enabled": true, + "snippets_enabled": true, + "container_registry_enabled": true, + "service_desk_enabled": true, + "can_create_merge_request_in": false, + "issues_access_level": "enabled", + "repository_access_level": "enabled", + "merge_requests_access_level": "enabled", + "forking_access_level": "enabled", + "wiki_access_level": "enabled", + "builds_access_level": "enabled", + "snippets_access_level": "enabled", + "pages_access_level": "enabled", + "operations_access_level": "enabled", + "analytics_access_level": "enabled", + "container_registry_access_level": "enabled", + "security_and_compliance_access_level": "private", + "emails_disabled": null, + "shared_runners_enabled": true, + "lfs_enabled": true, + "creator_id": 2378866, + "import_status": "none", + "open_issues_count": 0, + "ci_default_git_depth": 50, + "ci_forward_deployment_enabled": true, + "ci_job_token_scope_enabled": false, + "public_jobs": true, + "build_timeout": 3600, + "auto_cancel_pending_pipelines": "enabled", + "ci_config_path": "", + "shared_with_groups": [ + { + "group_id": 12258515, + "group_name": "test-argocd-proton", + "group_full_path": "test-shared-argocd-proton", + "group_access_level": 30, + "expires_at": null + } + ], + "only_allow_merge_if_pipeline_succeeds": false, + "allow_merge_on_skipped_pipeline": null, + "restrict_user_defined_variables": false, + "request_access_enabled": true, + "only_allow_merge_if_all_discussions_are_resolved": false, + "remove_source_branch_after_merge": true, + "printing_merge_request_link_enabled": true, + "merge_method": "merge", + "squash_option": "default_off", + "suggestion_commit_message": null, + "merge_commit_template": null, + "squash_commit_template": null, + "auto_devops_enabled": false, + "auto_devops_deploy_strategy": "continuous", + "autoclose_referenced_issues": true, + "keep_latest_artifact": true, + "runner_token_expiration_interval": null, + "approvals_before_merge": 0, + "mirror": false, + "external_authorization_classification_label": "", + "marked_for_deletion_at": null, + "marked_for_deletion_on": null, + "requirements_enabled": true, + "requirements_access_level": "enabled", + "security_and_compliance_enabled": false, + "compliance_frameworks": [], + "issues_template": null, + "merge_requests_template": null, + "merge_pipelines_enabled": false, + "merge_trains_enabled": false + }]`) + if err != nil { + t.Fail() + } case "/api/v4/projects/27084533/repository/branches/master": fmt.Println("returning") _, err := io.WriteString(w, `{ @@ -229,6 +877,116 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { if err != nil { t.Fail() } + case "/api/v4/projects/27084534/repository/branches?per_page=100": + _, err := io.WriteString(w, `[{ + "name": "master", + "commit": { + "id": "8898d7999fc99dd0fd578650b58b244fc63f6b53", + "short_id": "8898d799", + "created_at": "2021-06-04T08:24:44.000+00:00", + "parent_ids": null, + "title": "Merge branch 'pipeline-1317911429' into 'master'", + "message": "Merge branch 'pipeline-1317911429' into 'master'", + "author_name": "Martin Vozník", + "author_email": "martin@voznik.cz", + "authored_date": "2021-06-04T08:24:44.000+00:00", + "committer_name": "Martin Vozník", + "committer_email": "martin@voznik.cz", + "committed_date": "2021-06-04T08:24:44.000+00:00", + "trailers": null, + "web_url": "https://gitlab.com/test-shared-argocd-proton/shared-argocd/-/commit/8898d7999fc99dd0fd578650b58b244fc63f6b53" + }, + "merged": false, + "protected": true, + "developers_can_push": false, + "developers_can_merge": false, + "can_push": false, + "default": true, + "web_url": "https://gitlab.com/test-shared-argocd-proton/shared-argocd/-/tree/master" + }, { + "name": "pipeline-2310077506", + "commit": { + "id": "0f92540e5f396ba960adea4ed0aa905baf3f73d1", + "short_id": "0f92540e", + "created_at": "2021-06-01T18:39:59.000+00:00", + "parent_ids": null, + "title": "[testapp-ci] manifests/demo/test-app.yaml: release v1.0.1", + "message": "[testapp-ci] manifests/demo/test-app.yaml: release v1.0.1", + "author_name": "ci-test-app", + "author_email": "mvoznik+cicd@protonmail.com", + "authored_date": "2021-06-01T18:39:59.000+00:00", + "committer_name": "ci-test-app", + "committer_email": "mvoznik+cicd@protonmail.com", + "committed_date": "2021-06-01T18:39:59.000+00:00", + "trailers": null, + "web_url": "https://gitlab.com/test-shared-argocd-proton/shared-argocd/-/commit/0f92540e5f396ba960adea4ed0aa905baf3f73d1" + }, + "merged": false, + "protected": false, + "developers_can_push": false, + "developers_can_merge": false, + "can_push": false, + "default": false, + "web_url": "https://gitlab.com/test-shared-argocd-proton/shared-argocd/-/tree/pipeline-1310077506" + }]`) + if err != nil { + t.Fail() + } + case "/api/v4/projects/27084538/repository/branches?per_page=100": + _, err := io.WriteString(w, `[{ + "name": "master", + "commit": { + "id": "8898d7999fc99dd0fd578650b58b244fc63f6b58", + "short_id": "8898d801", + "created_at": "2021-06-04T08:24:44.000+00:00", + "parent_ids": null, + "title": "Merge branch 'pipeline-1317911429' into 'master'", + "message": "Merge branch 'pipeline-1317911429' into 'master'", + "author_name": "Martin Vozník", + "author_email": "martin@voznik.cz", + "authored_date": "2021-06-04T08:24:44.000+00:00", + "committer_name": "Martin Vozník", + "committer_email": "martin@voznik.cz", + "committed_date": "2021-06-04T08:24:44.000+00:00", + "trailers": null, + "web_url": "https://gitlab.com/test-argocd-proton/subgroup/argocd-subgroup/-/commit/8898d7999fc99dd0fd578650b58b244fc63f6b53" + }, + "merged": false, + "protected": true, + "developers_can_push": false, + "developers_can_merge": false, + "can_push": false, + "default": true, + "web_url": "https://gitlab.com/test-argocd-proton/subgroup/argocd-subgroup/-/tree/master" + }, { + "name": "pipeline-2310077506", + "commit": { + "id": "0f92540e5f396ba960adea4ed0aa905baf3f73d1", + "short_id": "0f92540e", + "created_at": "2021-06-01T18:39:59.000+00:00", + "parent_ids": null, + "title": "[testapp-ci] manifests/demo/test-app.yaml: release v1.0.1", + "message": "[testapp-ci] manifests/demo/test-app.yaml: release v1.0.1", + "author_name": "ci-test-app", + "author_email": "mvoznik+cicd@protonmail.com", + "authored_date": "2021-06-01T18:39:59.000+00:00", + "committer_name": "ci-test-app", + "committer_email": "mvoznik+cicd@protonmail.com", + "committed_date": "2021-06-01T18:39:59.000+00:00", + "trailers": null, + "web_url": "https://gitlab.com/test-argocd-proton/subgroup/argocd-subgroup/-/commit/0f92540e5f396ba960adea4ed0aa905baf3f73d1" + }, + "merged": false, + "protected": false, + "developers_can_push": false, + "developers_can_merge": false, + "can_push": false, + "default": false, + "web_url": "https://gitlab.com/test-argocd-proton/subgroup/argocd-subgroup/-/tree/pipeline-1310077506" + }]`) + if err != nil { + t.Fail() + } case "/api/v4/projects/test-argocd-proton%2Fargocd": fmt.Println("auct") _, err := io.WriteString(w, `{ @@ -240,8 +998,12 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { "path_with_namespace": "test-argocd-proton/argocd", "created_at": "2021-06-01T17:30:44.724Z", "default_branch": "master", - "tag_list": [], - "topics": [], + "tag_list": [ + "test-topic" + ], + "topics": [ + "test-topic" + ], "ssh_url_to_repo": "git@gitlab.com:test-argocd-proton/argocd.git", "http_url_to_repo": "https://gitlab.com/test-argocd-proton/argocd.git", "web_url": "https://gitlab.com/test-argocd-proton/argocd", @@ -274,6 +1036,8 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { if err != nil { t.Fail() } + case "/api/v4/projects/27084533/repository/branches/foo": + w.WriteHeader(http.StatusNotFound) default: _, err := io.WriteString(w, `[]`) if err != nil { @@ -284,10 +1048,10 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { } func TestGitlabListRepos(t *testing.T) { cases := []struct { - name, proto, url string - hasError, allBranches, includeSubgroups bool - branches []string - filters []v1alpha1.SCMProviderGeneratorFilter + name, proto, url, topic string + hasError, allBranches, includeSubgroups, includeSharedProjects, insecure bool + branches []string + filters []v1alpha1.SCMProviderGeneratorFilter }{ { name: "blank protocol", @@ -299,6 +1063,16 @@ func TestGitlabListRepos(t *testing.T) { proto: "ssh", url: "git@gitlab.com:test-argocd-proton/argocd.git", }, + { + name: "labelmatch", + proto: "ssh", + url: "git@gitlab.com:test-argocd-proton/argocd.git", + filters: []v1alpha1.SCMProviderGeneratorFilter{ + { + LabelMatch: strp("test-topic"), + }, + }, + }, { name: "https protocol", proto: "https", @@ -315,32 +1089,66 @@ func TestGitlabListRepos(t *testing.T) { url: "git@gitlab.com:test-argocd-proton/argocd.git", branches: []string{"master"}, }, + { + name: "all subgroups", + allBranches: true, + url: "git@gitlab.com:test-argocd-proton/argocd.git", + branches: []string{"master"}, + includeSharedProjects: false, + includeSubgroups: true, + }, + { + name: "all subgroups and shared projects", + allBranches: true, + url: "git@gitlab.com:test-argocd-proton/argocd.git", + branches: []string{"master"}, + includeSharedProjects: true, + includeSubgroups: true, + }, + { + name: "specific topic", + allBranches: true, + url: "git@gitlab.com:test-argocd-proton/argocd.git", + branches: []string{"master"}, + includeSubgroups: false, + topic: "specific-topic", + }, } ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gitlabMockHandler(t)(w, r) })) for _, c := range cases { t.Run(c.name, func(t *testing.T) { - provider, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, c.allBranches, c.includeSubgroups) + provider, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, c.allBranches, c.includeSubgroups, c.includeSharedProjects, c.insecure, "", c.topic) rawRepos, err := ListRepos(context.Background(), provider, c.filters, c.proto) if c.hasError { assert.NotNil(t, err) } else { assert.Nil(t, err) - // Just check that this one project shows up. Not a great test but better thing nothing? + // Just check that this one project shows up. Not a great test but better than nothing? repos := []*Repository{} + uniqueRepos := map[string]int{} branches := []string{} for _, r := range rawRepos { if r.Repository == "argocd" { repos = append(repos, r) branches = append(branches, r.Branch) } + uniqueRepos[r.Repository]++ } assert.NotEmpty(t, repos) assert.Equal(t, c.url, repos[0].URL) for _, b := range c.branches { assert.Contains(t, branches, b) } + // In case of listing subgroups, validate the number of returned projects + if c.includeSubgroups || c.includeSharedProjects { + assert.Equal(t, 2, len(uniqueRepos)) + } + // In case we filter on the topic, ensure we got only one repo returned + if c.topic != "" { + assert.Equal(t, 1, len(uniqueRepos)) + } } }) } @@ -350,7 +1158,7 @@ func TestGitlabHasPath(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gitlabMockHandler(t)(w, r) })) - host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true) + host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true, true, false, "", "") repo := &Repository{ Organization: "test-argocd-proton", Repository: "argocd", @@ -391,3 +1199,29 @@ func TestGitlabHasPath(t *testing.T) { }) } } + +func TestGitlabGetBranches(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + gitlabMockHandler(t)(w, r) + })) + host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true, true, false, "", "") + + repo := &Repository{ + RepositoryId: 27084533, + Branch: "master", + } + t.Run("branch exists", func(t *testing.T) { + repos, err := host.GetBranches(context.Background(), repo) + assert.Nil(t, err) + assert.Equal(t, repos[0].Branch, "master") + }) + + repo2 := &Repository{ + RepositoryId: 27084533, + Branch: "foo", + } + t.Run("unknown branch", func(t *testing.T) { + _, err := host.GetBranches(context.Background(), repo2) + assert.NoError(t, err) + }) +} diff --git a/applicationset/utils/clusterUtils.go b/applicationset/utils/clusterUtils.go index e06d7b39fac50..3b34a5a863dbd 100644 --- a/applicationset/utils/clusterUtils.go +++ b/applicationset/utils/clusterUtils.go @@ -50,10 +50,10 @@ const ( // ValidateDestination checks: // if we used destination name we infer the server url // if we used both name and server then we return an invalid spec error -func ValidateDestination(ctx context.Context, dest *appv1.ApplicationDestination, clientset kubernetes.Interface, namespace string) error { +func ValidateDestination(ctx context.Context, dest *appv1.ApplicationDestination, clientset kubernetes.Interface, argoCDNamespace string) error { if dest.Name != "" { if dest.Server == "" { - server, err := getDestinationServer(ctx, dest.Name, clientset, namespace) + server, err := getDestinationServer(ctx, dest.Name, clientset, argoCDNamespace) if err != nil { return fmt.Errorf("unable to find destination server: %v", err) } @@ -70,11 +70,11 @@ func ValidateDestination(ctx context.Context, dest *appv1.ApplicationDestination return nil } -func getDestinationServer(ctx context.Context, clusterName string, clientset kubernetes.Interface, namespace string) (string, error) { +func getDestinationServer(ctx context.Context, clusterName string, clientset kubernetes.Interface, argoCDNamespace string) (string, error) { // settingsMgr := settings.NewSettingsManager(context.TODO(), clientset, namespace) // argoDB := db.NewDB(namespace, settingsMgr, clientset) // clusterList, err := argoDB.ListClusters(ctx) - clusterList, err := ListClusters(ctx, clientset, namespace) + clusterList, err := ListClusters(ctx, clientset, argoCDNamespace) if err != nil { return "", err } @@ -180,7 +180,7 @@ func secretToCluster(s *corev1.Secret) (*appv1.Cluster, error) { if val, err := strconv.Atoi(string(shardStr)); err != nil { log.Warnf("Error while parsing shard in cluster secret '%s': %v", s.Name, err) } else { - shard = pointer.Int64Ptr(int64(val)) + shard = pointer.Int64(int64(val)) } } cluster := appv1.Cluster{ diff --git a/applicationset/utils/clusterUtils_test.go b/applicationset/utils/clusterUtils_test.go index ba5d24d1604f6..70332afdd80fb 100644 --- a/applicationset/utils/clusterUtils_test.go +++ b/applicationset/utils/clusterUtils_test.go @@ -13,7 +13,6 @@ import ( kubetesting "k8s.io/client-go/testing" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" ) const ( @@ -69,7 +68,7 @@ func createClusterSecret(secretName string, clusterName string, clusterServer st secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, - Namespace: utils.ArgoCDNamespace, + Namespace: fakeNamespace, Labels: map[string]string{ ArgoCDSecretTypeLabel: ArgoCDSecretTypeCluster, }, @@ -111,7 +110,7 @@ func TestValidateDestination(t *testing.T) { objects = append(objects, secret) kubeclientset := fake.NewSimpleClientset(objects...) - appCond := ValidateDestination(context.Background(), &dest, kubeclientset, utils.ArgoCDNamespace) + appCond := ValidateDestination(context.Background(), &dest, kubeclientset, fakeNamespace) assert.Nil(t, appCond) assert.Equal(t, "https://127.0.0.1:6443", dest.Server) assert.True(t, dest.IsServerInferred()) @@ -124,7 +123,7 @@ func TestValidateDestination(t *testing.T) { Namespace: "default", } - err := ValidateDestination(context.Background(), &dest, nil, utils.ArgoCDNamespace) + err := ValidateDestination(context.Background(), &dest, nil, fakeNamespace) assert.Equal(t, "application destination can't have both name and server defined: minikube https://127.0.0.1:6443", err.Error()) assert.False(t, dest.IsServerInferred()) }) @@ -139,7 +138,7 @@ func TestValidateDestination(t *testing.T) { return true, nil, fmt.Errorf("an error occurred") }) - err := ValidateDestination(context.Background(), &dest, kubeclientset, utils.ArgoCDNamespace) + err := ValidateDestination(context.Background(), &dest, kubeclientset, fakeNamespace) assert.Equal(t, "unable to find destination server: an error occurred", err.Error()) assert.False(t, dest.IsServerInferred()) }) @@ -154,7 +153,7 @@ func TestValidateDestination(t *testing.T) { objects = append(objects, secret) kubeclientset := fake.NewSimpleClientset(objects...) - err := ValidateDestination(context.Background(), &dest, kubeclientset, utils.ArgoCDNamespace) + err := ValidateDestination(context.Background(), &dest, kubeclientset, fakeNamespace) assert.Equal(t, "unable to find destination server: there are no clusters with this name: minikube", err.Error()) assert.False(t, dest.IsServerInferred()) }) @@ -171,7 +170,7 @@ func TestValidateDestination(t *testing.T) { objects = append(objects, secret, secret2) kubeclientset := fake.NewSimpleClientset(objects...) - err := ValidateDestination(context.Background(), &dest, kubeclientset, utils.ArgoCDNamespace) + err := ValidateDestination(context.Background(), &dest, kubeclientset, fakeNamespace) assert.Equal(t, "unable to find destination server: there are 2 clusters with the same name: [https://127.0.0.1:2443 https://127.0.0.1:8443]", err.Error()) assert.False(t, dest.IsServerInferred()) }) diff --git a/applicationset/utils/createOrUpdate.go b/applicationset/utils/createOrUpdate.go index a9775eb7994c5..1f2a8a9c4a54c 100644 --- a/applicationset/utils/createOrUpdate.go +++ b/applicationset/utils/createOrUpdate.go @@ -2,18 +2,24 @@ package utils import ( "context" + "encoding/json" "fmt" + log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/util/argo" + argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff" ) // CreateOrUpdate overrides "sigs.k8s.io/controller-runtime" function @@ -29,7 +35,7 @@ import ( // The MutateFn is called regardless of creating or updating an object. // // It returns the executed operation and an error. -func CreateOrUpdate(ctx context.Context, c client.Client, obj client.Object, f controllerutil.MutateFn) (controllerutil.OperationResult, error) { +func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ignoreAppDifferences argov1alpha1.ApplicationSetIgnoreDifferences, obj *argov1alpha1.Application, f controllerutil.MutateFn) (controllerutil.OperationResult, error) { key := client.ObjectKeyFromObject(obj) if err := c.Get(ctx, key, obj); err != nil { @@ -45,11 +51,24 @@ func CreateOrUpdate(ctx context.Context, c client.Client, obj client.Object, f c return controllerutil.OperationResultCreated, nil } - existing := obj.DeepCopyObject() + normalizedLive := obj.DeepCopy() + + // Mutate the live object to match the desired state. if err := mutate(f, key, obj); err != nil { return controllerutil.OperationResultNone, err } + // Apply ignoreApplicationDifferences rules to remove ignored fields from both the live and the desired state. This + // prevents those differences from appearing in the diff and therefore in the patch. + err := applyIgnoreDifferences(ignoreAppDifferences, normalizedLive, obj) + if err != nil { + return controllerutil.OperationResultNone, fmt.Errorf("failed to apply ignore differences: %w", err) + } + + // Normalize to avoid diffing on unimportant differences. + normalizedLive.Spec = *argo.NormalizeApplicationSpec(&normalizedLive.Spec) + obj.Spec = *argo.NormalizeApplicationSpec(&obj.Spec) + equality := conversion.EqualitiesOrDie( func(a, b resource.Quantity) bool { // Ignore formatting, only care that numeric value stayed the same. @@ -75,16 +94,34 @@ func CreateOrUpdate(ctx context.Context, c client.Client, obj client.Object, f c }, ) - if equality.DeepEqual(existing, obj) { + if equality.DeepEqual(normalizedLive, obj) { return controllerutil.OperationResultNone, nil } - if err := c.Update(ctx, obj); err != nil { + patch := client.MergeFrom(normalizedLive) + if log.IsLevelEnabled(log.DebugLevel) { + LogPatch(logCtx, patch, obj) + } + if err := c.Patch(ctx, obj, patch); err != nil { return controllerutil.OperationResultNone, err } return controllerutil.OperationResultUpdated, nil } +func LogPatch(logCtx *log.Entry, patch client.Patch, obj *argov1alpha1.Application) { + patchBytes, err := patch.Data(obj) + if err != nil { + logCtx.Errorf("failed to generate patch: %v", err) + } + // Get the patch as a plain object so it is easier to work with in json logs. + var patchObj map[string]interface{} + err = json.Unmarshal(patchBytes, &patchObj) + if err != nil { + logCtx.Errorf("failed to unmarshal patch: %v", err) + } + logCtx.WithField("patch", patchObj).Debug("patching application") +} + // mutate wraps a MutateFn and applies validation to its result func mutate(f controllerutil.MutateFn, key client.ObjectKey, obj client.Object) error { if err := f(); err != nil { @@ -95,3 +132,71 @@ func mutate(f controllerutil.MutateFn, key client.ObjectKey, obj client.Object) } return nil } + +// applyIgnoreDifferences applies the ignore differences rules to the found application. It modifies the applications in place. +func applyIgnoreDifferences(applicationSetIgnoreDifferences argov1alpha1.ApplicationSetIgnoreDifferences, found *argov1alpha1.Application, generatedApp *argov1alpha1.Application) error { + if len(applicationSetIgnoreDifferences) == 0 { + return nil + } + + generatedAppCopy := generatedApp.DeepCopy() + diffConfig, err := argodiff.NewDiffConfigBuilder(). + WithDiffSettings(applicationSetIgnoreDifferences.ToApplicationIgnoreDifferences(), nil, false). + WithNoCache(). + Build() + if err != nil { + return fmt.Errorf("failed to build diff config: %w", err) + } + unstructuredFound, err := appToUnstructured(found) + if err != nil { + return fmt.Errorf("failed to convert found application to unstructured: %w", err) + } + unstructuredGenerated, err := appToUnstructured(generatedApp) + if err != nil { + return fmt.Errorf("failed to convert found application to unstructured: %w", err) + } + result, err := argodiff.Normalize([]*unstructured.Unstructured{unstructuredFound}, []*unstructured.Unstructured{unstructuredGenerated}, diffConfig) + if err != nil { + return fmt.Errorf("failed to normalize application spec: %w", err) + } + if len(result.Lives) != 1 { + return fmt.Errorf("expected 1 normalized application, got %d", len(result.Lives)) + } + foundJsonNormalized, err := json.Marshal(result.Lives[0].Object) + if err != nil { + return fmt.Errorf("failed to marshal normalized app to json: %w", err) + } + foundNormalized := &argov1alpha1.Application{} + err = json.Unmarshal(foundJsonNormalized, &foundNormalized) + if err != nil { + return fmt.Errorf("failed to unmarshal normalized app to json: %w", err) + } + if len(result.Targets) != 1 { + return fmt.Errorf("expected 1 normalized application, got %d", len(result.Targets)) + } + foundNormalized.DeepCopyInto(found) + generatedJsonNormalized, err := json.Marshal(result.Targets[0].Object) + if err != nil { + return fmt.Errorf("failed to marshal normalized app to json: %w", err) + } + generatedAppNormalized := &argov1alpha1.Application{} + err = json.Unmarshal(generatedJsonNormalized, &generatedAppNormalized) + if err != nil { + return fmt.Errorf("failed to unmarshal normalized app json to structured app: %w", err) + } + generatedAppNormalized.DeepCopyInto(generatedApp) + // Prohibit jq queries from mutating silly things. + generatedApp.TypeMeta = generatedAppCopy.TypeMeta + generatedApp.Name = generatedAppCopy.Name + generatedApp.Namespace = generatedAppCopy.Namespace + generatedApp.Operation = generatedAppCopy.Operation + return nil +} + +func appToUnstructured(app client.Object) (*unstructured.Unstructured, error) { + u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(app) + if err != nil { + return nil, fmt.Errorf("failed to convert app object to unstructured: %w", err) + } + return &unstructured.Unstructured{Object: u}, nil +} diff --git a/applicationset/utils/createOrUpdate_test.go b/applicationset/utils/createOrUpdate_test.go new file mode 100644 index 0000000000000..a294e89281974 --- /dev/null +++ b/applicationset/utils/createOrUpdate_test.go @@ -0,0 +1,234 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +func Test_applyIgnoreDifferences(t *testing.T) { + appMeta := metav1.TypeMeta{ + APIVersion: v1alpha1.ApplicationSchemaGroupVersionKind.GroupVersion().String(), + Kind: v1alpha1.ApplicationSchemaGroupVersionKind.Kind, + } + testCases := []struct { + name string + ignoreDifferences v1alpha1.ApplicationSetIgnoreDifferences + foundApp string + generatedApp string + expectedApp string + }{ + { + name: "empty ignoreDifferences", + foundApp: ` +spec: {}`, + generatedApp: ` +spec: {}`, + expectedApp: ` +spec: {}`, + }, + { + // For this use case: https://github.com/argoproj/argo-cd/issues/9101#issuecomment-1191138278 + name: "ignore target revision with jq", + ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{ + {JQPathExpressions: []string{".spec.source.targetRevision"}}, + }, + foundApp: ` +spec: + source: + targetRevision: foo`, + generatedApp: ` +spec: + source: + targetRevision: bar`, + expectedApp: ` +spec: + source: + targetRevision: foo`, + }, + { + // For this use case: https://github.com/argoproj/argo-cd/issues/9101#issuecomment-1103593714 + name: "ignore helm parameter with jq", + ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{ + {JQPathExpressions: []string{`.spec.source.helm.parameters | select(.name == "image.tag")`}}, + }, + foundApp: ` +spec: + source: + helm: + parameters: + - name: image.tag + value: test + - name: another + value: value`, + generatedApp: ` +spec: + source: + helm: + parameters: + - name: image.tag + value: v1.0.0 + - name: another + value: value`, + expectedApp: ` +spec: + source: + helm: + parameters: + - name: image.tag + value: test + - name: another + value: value`, + }, + { + // For this use case: https://github.com/argoproj/argo-cd/issues/9101#issuecomment-1191138278 + name: "ignore auto-sync in appset when it's not in the cluster with jq", + ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{ + {JQPathExpressions: []string{".spec.syncPolicy.automated"}}, + }, + foundApp: ` +spec: + syncPolicy: + retry: + limit: 5`, + generatedApp: ` +spec: + syncPolicy: + automated: + selfHeal: true + retry: + limit: 5`, + expectedApp: ` +spec: + syncPolicy: + retry: + limit: 5`, + }, + { + name: "ignore auto-sync in the cluster when it's not in the appset with jq", + ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{ + {JQPathExpressions: []string{".spec.syncPolicy.automated"}}, + }, + foundApp: ` +spec: + syncPolicy: + automated: + selfHeal: true + retry: + limit: 5`, + generatedApp: ` +spec: + syncPolicy: + retry: + limit: 5`, + expectedApp: ` +spec: + syncPolicy: + automated: + selfHeal: true + retry: + limit: 5`, + }, + { + // For this use case: https://github.com/argoproj/argo-cd/issues/9101#issuecomment-1420656537 + name: "ignore a one-off annotation with jq", + ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{ + {JQPathExpressions: []string{`.metadata.annotations | select(.["foo.bar"] == "baz")`}}, + }, + foundApp: ` +metadata: + annotations: + foo.bar: baz + some.other: annotation`, + generatedApp: ` +metadata: + annotations: + some.other: annotation`, + expectedApp: ` +metadata: + annotations: + foo.bar: baz + some.other: annotation`, + }, + { + // For this use case: https://github.com/argoproj/argo-cd/issues/9101#issuecomment-1515672638 + name: "ignore the source.plugin field with a json pointer", + ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{ + {JSONPointers: []string{"/spec/source/plugin"}}, + }, + foundApp: ` +spec: + source: + plugin: + parameters: + - name: url + string: https://example.com`, + generatedApp: ` +spec: + source: + plugin: + parameters: + - name: url + string: https://example.com/wrong`, + expectedApp: ` +spec: + source: + plugin: + parameters: + - name: url + string: https://example.com`, + }, + { + // For this use case: https://github.com/argoproj/argo-cd/pull/14743#issuecomment-1761954799 + name: "ignore parameters added to a multi-source app in the cluster", + ignoreDifferences: v1alpha1.ApplicationSetIgnoreDifferences{ + {JQPathExpressions: []string{`.spec.sources[] | select(.repoURL | contains("test-repo")).helm.parameters`}}, + }, + foundApp: ` +spec: + sources: + - repoURL: https://git.example.com/test-org/test-repo + helm: + parameters: + - name: test + value: hi`, + generatedApp: ` +spec: + sources: + - repoURL: https://git.example.com/test-org/test-repo`, + expectedApp: ` +spec: + sources: + - repoURL: https://git.example.com/test-org/test-repo + helm: + parameters: + - name: test + value: hi`, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + foundApp := v1alpha1.Application{TypeMeta: appMeta} + err := yaml.Unmarshal([]byte(tc.foundApp), &foundApp) + require.NoError(t, err, tc.foundApp) + generatedApp := v1alpha1.Application{TypeMeta: appMeta} + err = yaml.Unmarshal([]byte(tc.generatedApp), &generatedApp) + require.NoError(t, err, tc.generatedApp) + err = applyIgnoreDifferences(tc.ignoreDifferences, &foundApp, &generatedApp) + require.NoError(t, err) + yamlFound, err := yaml.Marshal(tc.foundApp) + require.NoError(t, err) + yamlExpected, err := yaml.Marshal(tc.expectedApp) + require.NoError(t, err) + assert.Equal(t, string(yamlExpected), string(yamlFound)) + }) + } +} diff --git a/applicationset/utils/policy.go b/applicationset/utils/policy.go index 926a50926cd05..a06509265a540 100644 --- a/applicationset/utils/policy.go +++ b/applicationset/utils/policy.go @@ -1,55 +1,22 @@ package utils -// Policy allows to apply different rules to a set of changes. -type Policy interface { - Update() bool - Delete() bool -} +import ( + argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) // Policies is a registry of available policies. -var Policies = map[string]Policy{ - "sync": &SyncPolicy{}, - "create-only": &CreateOnlyPolicy{}, - "create-update": &CreateUpdatePolicy{}, - "create-delete": &CreateDeletePolicy{}, -} - -type SyncPolicy struct{} - -func (p *SyncPolicy) Update() bool { - return true -} - -func (p *SyncPolicy) Delete() bool { - return true -} - -type CreateUpdatePolicy struct{} - -func (p *CreateUpdatePolicy) Update() bool { - return true -} - -func (p *CreateUpdatePolicy) Delete() bool { - return false -} - -type CreateOnlyPolicy struct{} - -func (p *CreateOnlyPolicy) Update() bool { - return false -} - -func (p *CreateOnlyPolicy) Delete() bool { - return false -} - -type CreateDeletePolicy struct{} - -func (p *CreateDeletePolicy) Update() bool { - return false -} - -func (p *CreateDeletePolicy) Delete() bool { - return true +var Policies = map[string]argov1alpha1.ApplicationsSyncPolicy{ + "create-only": argov1alpha1.ApplicationsSyncPolicyCreateOnly, + "create-update": argov1alpha1.ApplicationsSyncPolicyCreateUpdate, + "create-delete": argov1alpha1.ApplicationsSyncPolicyCreateDelete, + "sync": argov1alpha1.ApplicationsSyncPolicySync, + // Default is "sync" + "": argov1alpha1.ApplicationsSyncPolicySync, +} + +func DefaultPolicy(appSetSyncPolicy *argov1alpha1.ApplicationSetSyncPolicy, controllerPolicy argov1alpha1.ApplicationsSyncPolicy, enablePolicyOverride bool) argov1alpha1.ApplicationsSyncPolicy { + if appSetSyncPolicy == nil || appSetSyncPolicy.ApplicationsSync == nil || !enablePolicyOverride { + return controllerPolicy + } + return *appSetSyncPolicy.ApplicationsSync } diff --git a/applicationset/utils/selector.go b/applicationset/utils/selector.go new file mode 100644 index 0000000000000..53db73a5b3a48 --- /dev/null +++ b/applicationset/utils/selector.go @@ -0,0 +1,261 @@ +package utils + +import ( + "fmt" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" + "k8s.io/apimachinery/pkg/util/validation" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/klog/v2" + "sort" + "strconv" + "strings" +) + +var ( + unaryOperators = []string{ + string(selection.Exists), string(selection.DoesNotExist), + } + binaryOperators = []string{ + string(selection.In), string(selection.NotIn), + string(selection.Equals), string(selection.DoubleEquals), string(selection.NotEquals), + string(selection.GreaterThan), string(selection.LessThan), + } + validRequirementOperators = append(binaryOperators, unaryOperators...) +) + +// Selector represents a label selector. +type Selector interface { + // Matches returns true if this selector matches the given set of labels. + Matches(labels.Labels) bool + + // Add adds requirements to the Selector + Add(r ...Requirement) Selector +} + +type internalSelector []Requirement + +// ByKey sorts requirements by key to obtain deterministic parser +type ByKey []Requirement + +func (a ByKey) Len() int { return len(a) } + +func (a ByKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +func (a ByKey) Less(i, j int) bool { return a[i].key < a[j].key } + +// Matches for a internalSelector returns true if all +// its Requirements match the input Labels. If any +// Requirement does not match, false is returned. +func (s internalSelector) Matches(l labels.Labels) bool { + for ix := range s { + if matches := s[ix].Matches(l); !matches { + return false + } + } + return true +} + +// Add adds requirements to the selector. It copies the current selector returning a new one +func (s internalSelector) Add(reqs ...Requirement) Selector { + ret := make(internalSelector, 0, len(s)+len(reqs)) + ret = append(ret, s...) + ret = append(ret, reqs...) + sort.Sort(ByKey(ret)) + return ret +} + +type nothingSelector struct{} + +func (n nothingSelector) Matches(l labels.Labels) bool { + return false +} + +func (n nothingSelector) Add(r ...Requirement) Selector { + return n +} + +// Nothing returns a selector that matches no labels +func nothing() Selector { + return nothingSelector{} +} + +// Everything returns a selector that matches all labels. +func everything() Selector { + return internalSelector{} +} + +// LabelSelectorAsSelector converts the LabelSelector api type into a struct that implements +// labels.Selector +// Note: This function should be kept in sync with the selector methods in pkg/labels/selector.go +func LabelSelectorAsSelector(ps *v1.LabelSelector) (Selector, error) { + if ps == nil { + return nothing(), nil + } + if len(ps.MatchLabels)+len(ps.MatchExpressions) == 0 { + return everything(), nil + } + requirements := make([]Requirement, 0, len(ps.MatchLabels)+len(ps.MatchExpressions)) + for k, v := range ps.MatchLabels { + r, err := newRequirement(k, selection.Equals, []string{v}) + if err != nil { + return nil, err + } + requirements = append(requirements, *r) + } + for _, expr := range ps.MatchExpressions { + var op selection.Operator + switch expr.Operator { + case v1.LabelSelectorOpIn: + op = selection.In + case v1.LabelSelectorOpNotIn: + op = selection.NotIn + case v1.LabelSelectorOpExists: + op = selection.Exists + case v1.LabelSelectorOpDoesNotExist: + op = selection.DoesNotExist + default: + return nil, fmt.Errorf("%q is not a valid pod selector operator", expr.Operator) + } + r, err := newRequirement(expr.Key, op, append([]string(nil), expr.Values...)) + if err != nil { + return nil, err + } + requirements = append(requirements, *r) + } + selector := newSelector() + selector = selector.Add(requirements...) + return selector, nil +} + +// NewSelector returns a nil selector +func newSelector() Selector { + return internalSelector(nil) +} + +func validateLabelKey(k string, path *field.Path) *field.Error { + if errs := validation.IsQualifiedName(k); len(errs) != 0 { + return field.Invalid(path, k, strings.Join(errs, "; ")) + } + return nil +} + +// NewRequirement is the constructor for a Requirement. +// If any of these rules is violated, an error is returned: +// (1) The operator can only be In, NotIn, Equals, DoubleEquals, Gt, Lt, NotEquals, Exists, or DoesNotExist. +// (2) If the operator is In or NotIn, the values set must be non-empty. +// (3) If the operator is Equals, DoubleEquals, or NotEquals, the values set must contain one value. +// (4) If the operator is Exists or DoesNotExist, the value set must be empty. +// (5) If the operator is Gt or Lt, the values set must contain only one value, which will be interpreted as an integer. +// (6) The key is invalid due to its length, or sequence +// +// of characters. See validateLabelKey for more details. +// +// The empty string is a valid value in the input values set. +// Returned error, if not nil, is guaranteed to be an aggregated field.ErrorList +func newRequirement(key string, op selection.Operator, vals []string, opts ...field.PathOption) (*Requirement, error) { + var allErrs field.ErrorList + path := field.ToPath(opts...) + if err := validateLabelKey(key, path.Child("key")); err != nil { + allErrs = append(allErrs, err) + } + + valuePath := path.Child("values") + switch op { + case selection.In, selection.NotIn: + if len(vals) == 0 { + allErrs = append(allErrs, field.Invalid(valuePath, vals, "for 'in', 'notin' operators, values set can't be empty")) + } + case selection.Equals, selection.DoubleEquals, selection.NotEquals: + if len(vals) != 1 { + allErrs = append(allErrs, field.Invalid(valuePath, vals, "exact-match compatibility requires one single value")) + } + case selection.Exists, selection.DoesNotExist: + if len(vals) != 0 { + allErrs = append(allErrs, field.Invalid(valuePath, vals, "values set must be empty for exists and does not exist")) + } + case selection.GreaterThan, selection.LessThan: + if len(vals) != 1 { + allErrs = append(allErrs, field.Invalid(valuePath, vals, "for 'Gt', 'Lt' operators, exactly one value is required")) + } + for i := range vals { + if _, err := strconv.ParseInt(vals[i], 10, 64); err != nil { + allErrs = append(allErrs, field.Invalid(valuePath.Index(i), vals[i], "for 'Gt', 'Lt' operators, the value must be an integer")) + } + } + default: + allErrs = append(allErrs, field.NotSupported(path.Child("operator"), op, validRequirementOperators)) + } + + return &Requirement{key: key, operator: op, strValues: vals}, allErrs.ToAggregate() +} + +// Requirement contains values, a key, and an operator that relates the key and values. +// The zero value of Requirement is invalid. +// Requirement implements both set based match and exact match +// Requirement should be initialized via NewRequirement constructor for creating a valid Requirement. +// +k8s:deepcopy-gen=true +type Requirement struct { + key string + operator selection.Operator + // In the majority of cases we have at most one value here. + // It is generally faster to operate on a single-element slice + // than on a single-element map, so we have a slice here. + strValues []string +} + +func (r *Requirement) hasValue(value string) bool { + for i := range r.strValues { + if r.strValues[i] == value { + return true + } + } + return false +} + +func (r *Requirement) Matches(ls labels.Labels) bool { + switch r.operator { + case selection.In, selection.Equals, selection.DoubleEquals: + if !ls.Has(r.key) { + return false + } + return r.hasValue(ls.Get(r.key)) + case selection.NotIn, selection.NotEquals: + if !ls.Has(r.key) { + return true + } + return !r.hasValue(ls.Get(r.key)) + case selection.Exists: + return ls.Has(r.key) + case selection.DoesNotExist: + return !ls.Has(r.key) + case selection.GreaterThan, selection.LessThan: + if !ls.Has(r.key) { + return false + } + lsValue, err := strconv.ParseInt(ls.Get(r.key), 10, 64) + if err != nil { + klog.V(10).Infof("ParseInt failed for value %+v in label %+v, %+v", ls.Get(r.key), ls, err) + return false + } + + // There should be only one strValue in r.strValues, and can be converted to an integer. + if len(r.strValues) != 1 { + klog.V(10).Infof("Invalid values count %+v of requirement %#v, for 'Gt', 'Lt' operators, exactly one value is required", len(r.strValues), r) + return false + } + + var rValue int64 + for i := range r.strValues { + rValue, err = strconv.ParseInt(r.strValues[i], 10, 64) + if err != nil { + klog.V(10).Infof("ParseInt failed for value %+v in requirement %#v, for 'Gt', 'Lt' operators, the value must be an integer", r.strValues[i], r) + return false + } + } + return (r.operator == selection.GreaterThan && lsValue > rValue) || (r.operator == selection.LessThan && lsValue < rValue) + default: + return false + } +} diff --git a/applicationset/utils/template_functions.go b/applicationset/utils/template_functions.go new file mode 100644 index 0000000000000..84ab870404f1a --- /dev/null +++ b/applicationset/utils/template_functions.go @@ -0,0 +1,71 @@ +package utils + +import ( + "regexp" + "strings" + + "sigs.k8s.io/yaml" +) + +// SanitizeName sanitizes the name in accordance with the below rules +// 1. contain no more than 253 characters +// 2. contain only lowercase alphanumeric characters, '-' or '.' +// 3. start and end with an alphanumeric character +func SanitizeName(name string) string { + invalidDNSNameChars := regexp.MustCompile("[^-a-z0-9.]") + maxDNSNameLength := 253 + + name = strings.ToLower(name) + name = invalidDNSNameChars.ReplaceAllString(name, "-") + if len(name) > maxDNSNameLength { + name = name[:maxDNSNameLength] + } + + return strings.Trim(name, "-.") +} + +// This has been copied from helm and may be removed as soon as it is retrofited in sprig +// toYAML takes an interface, marshals it to yaml, and returns a string. It will +// always return a string, even on marshal error (empty string). +// +// This is designed to be called from a template. +func toYAML(v interface{}) (string, error) { + data, err := yaml.Marshal(v) + if err != nil { + // Swallow errors inside of a template. + return "", err + } + return strings.TrimSuffix(string(data), "\n"), nil +} + +// This has been copied from helm and may be removed as soon as it is retrofited in sprig +// fromYAML converts a YAML document into a map[string]interface{}. +// +// This is not a general-purpose YAML parser, and will not parse all valid +// YAML documents. Additionally, because its intended use is within templates +// it tolerates errors. It will insert the returned error message string into +// m["Error"] in the returned map. +func fromYAML(str string) (map[string]interface{}, error) { + m := map[string]interface{}{} + + if err := yaml.Unmarshal([]byte(str), &m); err != nil { + return nil, err + } + return m, nil +} + +// This has been copied from helm and may be removed as soon as it is retrofited in sprig +// fromYAMLArray converts a YAML array into a []interface{}. +// +// This is not a general-purpose YAML parser, and will not parse all valid +// YAML documents. Additionally, because its intended use is within templates +// it tolerates errors. It will insert the returned error message string as +// the first and only item in the returned array. +func fromYAMLArray(str string) ([]interface{}, error) { + a := []interface{}{} + + if err := yaml.Unmarshal([]byte(str), &a); err != nil { + return nil, err + } + return a, nil +} diff --git a/applicationset/utils/utils.go b/applicationset/utils/utils.go index 254478ee45c7f..2d128eb81a16c 100644 --- a/applicationset/utils/utils.go +++ b/applicationset/utils/utils.go @@ -2,9 +2,12 @@ package utils import ( "bytes" + "crypto/tls" + "crypto/x509" "encoding/json" "fmt" "io" + "os" "reflect" "regexp" "sort" @@ -13,7 +16,9 @@ import ( "unsafe" "github.com/Masterminds/sprig/v3" + "github.com/gosimple/slug" "github.com/valyala/fasttemplate" + "sigs.k8s.io/yaml" log "github.com/sirupsen/logrus" @@ -28,10 +33,15 @@ func init() { delete(sprigFuncMap, "expandenv") delete(sprigFuncMap, "getHostByName") sprigFuncMap["normalize"] = SanitizeName + sprigFuncMap["slugify"] = SlugifyName + sprigFuncMap["toYaml"] = toYAML + sprigFuncMap["fromYaml"] = fromYAML + sprigFuncMap["fromYamlArray"] = fromYAMLArray } type Renderer interface { - RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *argoappsv1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool) (*argoappsv1.Application, error) + RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *argoappsv1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (*argoappsv1.Application, error) + Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) } type Render struct { @@ -48,9 +58,25 @@ func copyUnexported(copy, original reflect.Value) { copyValueIntoUnexported(copy, unexported) } +func IsJSONStr(str string) bool { + str = strings.TrimSpace(str) + return len(str) > 0 && str[0] == '{' +} + +func ConvertYAMLToJSON(str string) (string, error) { + if !IsJSONStr(str) { + jsonStr, err := yaml.YAMLToJSON([]byte(str)) + if err != nil { + return str, err + } + return string(jsonStr), nil + } + return str, nil +} + // This function is in charge of searching all String fields of the object recursively and apply templating // thanks to https://gist.github.com/randallmlough/1fd78ec8a1034916ca52281e3b886dc7 -func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[string]interface{}, useGoTemplate bool) error { +func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) error { switch original.Kind() { // The first cases handle nested structures and translate them recursively // If it is a pointer we need to unwrap and call once again @@ -70,7 +96,8 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri copyUnexported(copy, original) } // Unwrap the newly created pointer - if err := r.deeplyReplace(copy.Elem(), originalValue, replaceMap, useGoTemplate); err != nil { + if err := r.deeplyReplace(copy.Elem(), originalValue, replaceMap, useGoTemplate, goTemplateOptions); err != nil { + // Not wrapping the error, since this is a recursive function. Avoids excessively long error messages. return err } @@ -83,11 +110,19 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri originalValue := original.Elem() // Create a new object. Now new gives us a pointer, but we want the value it // points to, so we have to call Elem() to unwrap it - copyValue := reflect.New(originalValue.Type()).Elem() - if err := r.deeplyReplace(copyValue, originalValue, replaceMap, useGoTemplate); err != nil { - return err + + if originalValue.IsValid() { + reflectType := originalValue.Type() + + reflectValue := reflect.New(reflectType) + + copyValue := reflectValue.Elem() + if err := r.deeplyReplace(copyValue, originalValue, replaceMap, useGoTemplate, goTemplateOptions); err != nil { + // Not wrapping the error, since this is a recursive function. Avoids excessively long error messages. + return err + } + copy.Set(copyValue) } - copy.Set(copyValue) // If it is a struct we translate each field case reflect.Struct: @@ -96,7 +131,31 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri // specific case time if currentType == "time.Time" { copy.Field(i).Set(original.Field(i)) - } else if err := r.deeplyReplace(copy.Field(i), original.Field(i), replaceMap, useGoTemplate); err != nil { + } else if currentType == "Raw.k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" || currentType == "Raw.k8s.io/apimachinery/pkg/runtime" { + var unmarshaled interface{} + originalBytes := original.Field(i).Bytes() + convertedToJson, err := ConvertYAMLToJSON(string(originalBytes)) + if err != nil { + return fmt.Errorf("error while converting template to json %q: %w", convertedToJson, err) + } + err = json.Unmarshal([]byte(convertedToJson), &unmarshaled) + if err != nil { + return fmt.Errorf("failed to unmarshal JSON field: %w", err) + } + jsonOriginal := reflect.ValueOf(&unmarshaled) + jsonCopy := reflect.New(jsonOriginal.Type()).Elem() + err = r.deeplyReplace(jsonCopy, jsonOriginal, replaceMap, useGoTemplate, goTemplateOptions) + if err != nil { + return fmt.Errorf("failed to deeply replace JSON field contents: %w", err) + } + jsonCopyInterface := jsonCopy.Interface().(*interface{}) + data, err := json.Marshal(jsonCopyInterface) + if err != nil { + return fmt.Errorf("failed to marshal templated JSON field: %w", err) + } + copy.Field(i).Set(reflect.ValueOf(data)) + } else if err := r.deeplyReplace(copy.Field(i), original.Field(i), replaceMap, useGoTemplate, goTemplateOptions); err != nil { + // Not wrapping the error, since this is a recursive function. Avoids excessively long error messages. return err } } @@ -110,7 +169,8 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri } for i := 0; i < original.Len(); i += 1 { - if err := r.deeplyReplace(copy.Index(i), original.Index(i), replaceMap, useGoTemplate); err != nil { + if err := r.deeplyReplace(copy.Index(i), original.Index(i), replaceMap, useGoTemplate, goTemplateOptions); err != nil { + // Not wrapping the error, since this is a recursive function. Avoids excessively long error messages. return err } } @@ -124,20 +184,22 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri } for _, key := range original.MapKeys() { originalValue := original.MapIndex(key) - if originalValue.Kind() != reflect.String && originalValue.IsNil() { + if originalValue.Kind() != reflect.String && isNillable(originalValue) && originalValue.IsNil() { continue } // New gives us a pointer, but again we want the value copyValue := reflect.New(originalValue.Type()).Elem() - if err := r.deeplyReplace(copyValue, originalValue, replaceMap, useGoTemplate); err != nil { + if err := r.deeplyReplace(copyValue, originalValue, replaceMap, useGoTemplate, goTemplateOptions); err != nil { + // Not wrapping the error, since this is a recursive function. Avoids excessively long error messages. return err } // Keys can be templated as well as values (e.g. to template something into an annotation). if key.Kind() == reflect.String { - templatedKey, err := r.Replace(key.String(), replaceMap, useGoTemplate) + templatedKey, err := r.Replace(key.String(), replaceMap, useGoTemplate, goTemplateOptions) if err != nil { + // Not wrapping the error, since this is a recursive function. Avoids excessively long error messages. return err } key = reflect.ValueOf(templatedKey) @@ -150,8 +212,9 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri // If it is a string translate it (yay finally we're doing what we came for) case reflect.String: strToTemplate := original.String() - templated, err := r.Replace(strToTemplate, replaceMap, useGoTemplate) + templated, err := r.Replace(strToTemplate, replaceMap, useGoTemplate, goTemplateOptions) if err != nil { + // Not wrapping the error, since this is a recursive function. Avoids excessively long error messages. return err } if copy.CanSet() { @@ -172,9 +235,19 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri return nil } -func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *argoappsv1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool) (*argoappsv1.Application, error) { +// isNillable returns true if the value is something which may be set to nil. This function is meant to guard against a +// panic from calling IsNil on a non-pointer type. +func isNillable(v reflect.Value) bool { + switch v.Kind() { + case reflect.Map, reflect.Pointer, reflect.UnsafePointer, reflect.Interface, reflect.Slice: + return true + } + return false +} + +func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *argoappsv1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (*argoappsv1.Application, error) { if tmpl == nil { - return nil, fmt.Errorf("application template is empty ") + return nil, fmt.Errorf("application template is empty") } if len(params) == 0 { @@ -184,7 +257,7 @@ func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy * original := reflect.ValueOf(tmpl) copy := reflect.New(original.Type()).Elem() - if err := r.deeplyReplace(copy, original, params, useGoTemplate); err != nil { + if err := r.deeplyReplace(copy, original, params, useGoTemplate, goTemplateOptions); err != nil { return nil, err } @@ -204,16 +277,40 @@ func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy * return replacedTmpl, nil } +func (r *Render) RenderGeneratorParams(gen *argoappsv1.ApplicationSetGenerator, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (*argoappsv1.ApplicationSetGenerator, error) { + if gen == nil { + return nil, fmt.Errorf("generator is empty") + } + + if len(params) == 0 { + return gen, nil + } + + original := reflect.ValueOf(gen) + copy := reflect.New(original.Type()).Elem() + + if err := r.deeplyReplace(copy, original, params, useGoTemplate, goTemplateOptions); err != nil { + return nil, fmt.Errorf("failed to replace parameters in generator: %w", err) + } + + replacedGen := copy.Interface().(*argoappsv1.ApplicationSetGenerator) + + return replacedGen, nil +} + var isTemplatedRegex = regexp.MustCompile(".*{{.*}}.*") // Replace executes basic string substitution of a template with replacement values. // remaining in the substituted template. -func (r *Render) Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool) (string, error) { +func (r *Render) Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) { if useGoTemplate { template, err := template.New("").Funcs(sprigFuncMap).Parse(tmpl) if err != nil { return "", fmt.Errorf("failed to parse template %s: %w", tmpl, err) } + for _, option := range goTemplateOptions { + template = template.Option(option) + } var replacedTmplBuffer bytes.Buffer if err = template.Execute(&replacedTmplBuffer, replaceMap); err != nil { @@ -227,7 +324,10 @@ func (r *Render) Replace(tmpl string, replaceMap map[string]interface{}, useGoTe return tmpl, nil } - fstTmpl := fasttemplate.New(tmpl, "{{", "}}") + fstTmpl, err := fasttemplate.NewTemplate(tmpl, "{{", "}}") + if err != nil { + return "", fmt.Errorf("invalid template: %w", err) + } replacedTmpl := fstTmpl.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { trimmedTag := strings.TrimSpace(tag) replacement, ok := replaceMap[trimmedTag].(string) @@ -337,19 +437,85 @@ func NormalizeBitbucketBasePath(basePath string) string { return basePath } -// SanitizeName sanitizes the name in accordance with the below rules -// 1. contain no more than 253 characters -// 2. contain only lowercase alphanumeric characters, '-' or '.' -// 3. start and end with an alphanumeric character -func SanitizeName(name string) string { - invalidDNSNameChars := regexp.MustCompile("[^-a-z0-9.]") - maxDNSNameLength := 253 - - name = strings.ToLower(name) - name = invalidDNSNameChars.ReplaceAllString(name, "-") - if len(name) > maxDNSNameLength { - name = name[:maxDNSNameLength] +// SlugifyName generates a URL-friendly slug from the provided name and additional options. +// The slug is generated in accordance with the following rules: +// 1. The generated slug will be URL-safe and suitable for use in URLs. +// 2. The maximum length of the slug can be specified using the `maxSize` argument. +// 3. Smart truncation can be enabled or disabled using the `EnableSmartTruncate` argument. +// 4. The input name can be any string value that needs to be converted into a slug. +// +// Args: +// - args: A variadic number of arguments where: +// - The first argument (if provided) is an integer specifying the maximum length of the slug. +// - The second argument (if provided) is a boolean indicating whether smart truncation is enabled. +// - The last argument (if provided) is the input name that needs to be slugified. +// If no name is provided, an empty string will be used. +// +// Returns: +// - string: The generated URL-friendly slug based on the input name and options. +func SlugifyName(args ...interface{}) string { + // Default values for arguments + maxSize := 50 + EnableSmartTruncate := true + name := "" + + // Process the arguments + for idx, arg := range args { + switch idx { + case len(args) - 1: + name = arg.(string) + case 0: + maxSize = arg.(int) + case 1: + EnableSmartTruncate = arg.(bool) + default: + log.Errorf("Bad 'slugify' arguments.") + } + } + + sanitizedName := SanitizeName(name) + + // Configure slug generation options + slug.EnableSmartTruncate = EnableSmartTruncate + slug.MaxLength = maxSize + + // Generate the slug from the input name + urlSlug := slug.Make(sanitizedName) + + return urlSlug +} + +func getTlsConfigWithCACert(scmRootCAPath string) *tls.Config { + + tlsConfig := &tls.Config{} + + if scmRootCAPath != "" { + _, err := os.Stat(scmRootCAPath) + if os.IsNotExist(err) { + log.Errorf("scmRootCAPath '%s' specified does not exist: %s", scmRootCAPath, err) + return tlsConfig + } + rootCA, err := os.ReadFile(scmRootCAPath) + if err != nil { + log.Errorf("error reading certificate from file '%s', proceeding without custom rootCA : %s", scmRootCAPath, err) + return tlsConfig + } + certPool := x509.NewCertPool() + ok := certPool.AppendCertsFromPEM([]byte(rootCA)) + if !ok { + log.Errorf("failed to append certificates from PEM: proceeding without custom rootCA") + } else { + tlsConfig.RootCAs = certPool + } } + return tlsConfig +} + +func GetTlsConfig(scmRootCAPath string, insecure bool) *tls.Config { + tlsConfig := getTlsConfigWithCACert(scmRootCAPath) - return strings.Trim(name, "-.") + if insecure { + tlsConfig.InsecureSkipVerify = true + } + return tlsConfig } diff --git a/applicationset/utils/utils_test.go b/applicationset/utils/utils_test.go index af36c18a39dbb..3b4702bc35c3f 100644 --- a/applicationset/utils/utils_test.go +++ b/applicationset/utils/utils_test.go @@ -1,6 +1,10 @@ package utils import ( + "crypto/x509" + "encoding/json" + "os" + "path" "testing" "time" @@ -8,11 +12,11 @@ import ( logtest "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - argoappsetv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" argoappsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -174,7 +178,7 @@ func TestRenderTemplateParams(t *testing.T) { // Render the cloned application, into a new application render := Render{} - newApplication, err := render.RenderTemplateParams(application, nil, test.params, false) + newApplication, err := render.RenderTemplateParams(application, nil, test.params, false, nil) // Retrieve the value of the target field from the newApplication, then verify that // the target field has been templated into the expected value @@ -195,6 +199,113 @@ func TestRenderTemplateParams(t *testing.T) { } +func TestRenderHelmValuesObjectJson(t *testing.T) { + + params := map[string]interface{}{ + "test": "Hello world", + } + + application := &argoappsv1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{"annotation-key": "annotation-value", "annotation-key2": "annotation-value2"}, + Labels: map[string]string{"label-key": "label-value", "label-key2": "label-value2"}, + CreationTimestamp: metav1.NewTime(time.Now()), + UID: types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"), + Name: "application-one", + Namespace: "default", + }, + Spec: argoappsv1.ApplicationSpec{ + Source: &argoappsv1.ApplicationSource{ + Path: "", + RepoURL: "", + TargetRevision: "", + Chart: "", + Helm: &argoappsv1.ApplicationSourceHelm{ + ValuesObject: &runtime.RawExtension{ + Raw: []byte(`{ + "some": { + "string": "{{.test}}" + } + }`), + }, + }, + }, + Destination: argoappsv1.ApplicationDestination{ + Server: "", + Namespace: "", + Name: "", + }, + Project: "", + }, + } + + // Render the cloned application, into a new application + render := Render{} + newApplication, err := render.RenderTemplateParams(application, nil, params, true, []string{}) + + assert.NoError(t, err) + assert.NotNil(t, newApplication) + + var unmarshaled interface{} + err = json.Unmarshal(newApplication.Spec.Source.Helm.ValuesObject.Raw, &unmarshaled) + + assert.NoError(t, err) + assert.Equal(t, unmarshaled.(map[string]interface{})["some"].(map[string]interface{})["string"], "Hello world") + +} + +func TestRenderHelmValuesObjectYaml(t *testing.T) { + + params := map[string]interface{}{ + "test": "Hello world", + } + + application := &argoappsv1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{"annotation-key": "annotation-value", "annotation-key2": "annotation-value2"}, + Labels: map[string]string{"label-key": "label-value", "label-key2": "label-value2"}, + CreationTimestamp: metav1.NewTime(time.Now()), + UID: types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"), + Name: "application-one", + Namespace: "default", + }, + Spec: argoappsv1.ApplicationSpec{ + Source: &argoappsv1.ApplicationSource{ + Path: "", + RepoURL: "", + TargetRevision: "", + Chart: "", + Helm: &argoappsv1.ApplicationSourceHelm{ + ValuesObject: &runtime.RawExtension{ + Raw: []byte(`some: + string: "{{.test}}"`), + }, + }, + }, + Destination: argoappsv1.ApplicationDestination{ + Server: "", + Namespace: "", + Name: "", + }, + Project: "", + }, + } + + // Render the cloned application, into a new application + render := Render{} + newApplication, err := render.RenderTemplateParams(application, nil, params, true, []string{}) + + assert.NoError(t, err) + assert.NotNil(t, newApplication) + + var unmarshaled interface{} + err = json.Unmarshal(newApplication.Spec.Source.Helm.ValuesObject.Raw, &unmarshaled) + + assert.NoError(t, err) + assert.Equal(t, unmarshaled.(map[string]interface{})["some"].(map[string]interface{})["string"], "Hello world") + +} + func TestRenderTemplateParamsGoTemplate(t *testing.T) { // Believe it or not, this is actually less complex than the equivalent solution using reflection @@ -236,11 +347,12 @@ func TestRenderTemplateParamsGoTemplate(t *testing.T) { } tests := []struct { - name string - fieldVal string - params map[string]interface{} - expectedVal string - errorMessage string + name string + fieldVal string + params map[string]interface{} + expectedVal string + errorMessage string + templateOptions []string }{ { name: "simple substitution", @@ -423,6 +535,84 @@ func TestRenderTemplateParamsGoTemplate(t *testing.T) { }, errorMessage: `failed to execute go template {{.data.test}}: template: :1:7: executing "" at <.data.test>: can't evaluate field test in type interface {}`, }, + { + name: "lookup missing value with missingkey=default", + fieldVal: `--> {{.doesnotexist}} <--`, + expectedVal: `--> <--`, + params: map[string]interface{}{ + // if no params are passed then for some reason templating is skipped + "unused": "this is not used", + }, + }, + { + name: "lookup missing value with missingkey=error", + fieldVal: `--> {{.doesnotexist}} <--`, + expectedVal: "", + params: map[string]interface{}{ + // if no params are passed then for some reason templating is skipped + "unused": "this is not used", + }, + templateOptions: []string{"missingkey=error"}, + errorMessage: `failed to execute go template --> {{.doesnotexist}} <--: template: :1:6: executing "" at <.doesnotexist>: map has no entry for key "doesnotexist"`, + }, + { + name: "toYaml", + fieldVal: `{{ toYaml . | indent 2 }}`, + expectedVal: " foo:\n bar:\n bool: true\n number: 2\n str: Hello world", + params: map[string]interface{}{ + "foo": map[string]interface{}{ + "bar": map[string]interface{}{ + "bool": true, + "number": 2, + "str": "Hello world", + }, + }, + }, + }, + { + name: "toYaml Error", + fieldVal: `{{ toYaml . | indent 2 }}`, + expectedVal: " foo:\n bar:\n bool: true\n number: 2\n str: Hello world", + errorMessage: "failed to execute go template {{ toYaml . | indent 2 }}: template: :1:3: executing \"\" at : error calling toYaml: error marshaling into JSON: json: unsupported type: func(*string)", + params: map[string]interface{}{ + "foo": func(test *string) { + }, + }, + }, + { + name: "fromYaml", + fieldVal: `{{ get (fromYaml .value) "hello" }}`, + expectedVal: "world", + params: map[string]interface{}{ + "value": "hello: world", + }, + }, + { + name: "fromYaml error", + fieldVal: `{{ get (fromYaml .value) "hello" }}`, + expectedVal: "world", + errorMessage: "failed to execute go template {{ get (fromYaml .value) \"hello\" }}: template: :1:8: executing \"\" at : error calling fromYaml: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}", + params: map[string]interface{}{ + "value": "non\n compliant\n yaml", + }, + }, + { + name: "fromYamlArray", + fieldVal: `{{ fromYamlArray .value | last }}`, + expectedVal: "bonjour tout le monde", + params: map[string]interface{}{ + "value": "- hello world\n- bonjour tout le monde", + }, + }, + { + name: "fromYamlArray error", + fieldVal: `{{ fromYamlArray .value | last }}`, + expectedVal: "bonjour tout le monde", + errorMessage: "failed to execute go template {{ fromYamlArray .value | last }}: template: :1:3: executing \"\" at : error calling fromYamlArray: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type []interface {}", + params: map[string]interface{}{ + "value": "non\n compliant\n yaml", + }, + }, } for _, test := range tests { @@ -439,7 +629,7 @@ func TestRenderTemplateParamsGoTemplate(t *testing.T) { // Render the cloned application, into a new application render := Render{} - newApplication, err := render.RenderTemplateParams(application, nil, test.params, true) + newApplication, err := render.RenderTemplateParams(application, nil, test.params, true, test.templateOptions) // Retrieve the value of the target field from the newApplication, then verify that // the target field has been templated into the expected value @@ -464,6 +654,34 @@ func TestRenderTemplateParamsGoTemplate(t *testing.T) { } } +func TestRenderGeneratorParams_does_not_panic(t *testing.T) { + // This test verifies that the RenderGeneratorParams function does not panic when the value in a map is a non- + // nillable type. This is a regression test. + render := Render{} + params := map[string]interface{}{ + "branch": "master", + } + generator := &argoappsv1.ApplicationSetGenerator{ + Plugin: &argoappsv1.PluginGenerator{ + ConfigMapRef: argoappsv1.PluginConfigMapRef{ + Name: "cm-plugin", + }, + Input: argoappsv1.PluginInput{ + Parameters: map[string]apiextensionsv1.JSON{ + "branch": { + Raw: []byte(`"{{.branch}}"`), + }, + "repo": { + Raw: []byte(`"argo-test"`), + }, + }, + }, + }, + } + _, err := render.RenderGeneratorParams(generator, params, true, []string{}) + assert.NoError(t, err) +} + func TestRenderTemplateKeys(t *testing.T) { t.Run("fasttemplate", func(t *testing.T) { application := &argoappsv1.Application{ @@ -480,7 +698,7 @@ func TestRenderTemplateKeys(t *testing.T) { } render := Render{} - newApplication, err := render.RenderTemplateParams(application, nil, params, false) + newApplication, err := render.RenderTemplateParams(application, nil, params, false, nil) require.NoError(t, err) require.Contains(t, newApplication.ObjectMeta.Annotations, "annotation-some-key") assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-some-key"], "annotation-some-value") @@ -500,13 +718,21 @@ func TestRenderTemplateKeys(t *testing.T) { } render := Render{} - newApplication, err := render.RenderTemplateParams(application, nil, params, true) + newApplication, err := render.RenderTemplateParams(application, nil, params, true, nil) require.NoError(t, err) require.Contains(t, newApplication.ObjectMeta.Annotations, "annotation-some-key") assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-some-key"], "annotation-some-value") }) } +func Test_Render_Replace_no_panic_on_missing_closing_brace(t *testing.T) { + r := &Render{} + assert.NotPanics(t, func() { + _, err := r.Replace("{{properly.closed}} {{improperly.closed}", nil, false, []string{}) + assert.Error(t, err) + }) +} + func TestRenderTemplateParamsFinalizers(t *testing.T) { emptyApplication := &argoappsv1.Application{ @@ -528,7 +754,7 @@ func TestRenderTemplateParamsFinalizers(t *testing.T) { for _, c := range []struct { testName string - syncPolicy *argoappsetv1.ApplicationSetSyncPolicy + syncPolicy *argoappsv1.ApplicationSetSyncPolicy existingFinalizers []string expectedFinalizers []string }{ @@ -567,13 +793,13 @@ func TestRenderTemplateParamsFinalizers(t *testing.T) { { testName: "non-nil sync policy should use standard finalizer", existingFinalizers: nil, - syncPolicy: &argoappsetv1.ApplicationSetSyncPolicy{}, + syncPolicy: &argoappsv1.ApplicationSetSyncPolicy{}, expectedFinalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, { testName: "preserveResourcesOnDeletion should not have a finalizer", existingFinalizers: nil, - syncPolicy: &argoappsetv1.ApplicationSetSyncPolicy{ + syncPolicy: &argoappsv1.ApplicationSetSyncPolicy{ PreserveResourcesOnDeletion: true, }, expectedFinalizers: nil, @@ -581,7 +807,7 @@ func TestRenderTemplateParamsFinalizers(t *testing.T) { { testName: "user-specified finalizer should overwrite preserveResourcesOnDeletion", existingFinalizers: []string{"resources-finalizer.argocd.argoproj.io/background"}, - syncPolicy: &argoappsetv1.ApplicationSetSyncPolicy{ + syncPolicy: &argoappsv1.ApplicationSetSyncPolicy{ PreserveResourcesOnDeletion: true, }, expectedFinalizers: []string{"resources-finalizer.argocd.argoproj.io/background"}, @@ -601,7 +827,7 @@ func TestRenderTemplateParamsFinalizers(t *testing.T) { // Render the cloned application, into a new application render := Render{} - res, err := render.RenderTemplateParams(application, c.syncPolicy, params, true) + res, err := render.RenderTemplateParams(application, c.syncPolicy, params, true, nil) assert.Nil(t, err) assert.ElementsMatch(t, res.Finalizers, c.expectedFinalizers) @@ -615,27 +841,27 @@ func TestRenderTemplateParamsFinalizers(t *testing.T) { func TestCheckInvalidGenerators(t *testing.T) { scheme := runtime.NewScheme() - err := argoappsetv1.AddToScheme(scheme) + err := argoappsv1.AddToScheme(scheme) assert.Nil(t, err) err = argoappsv1.AddToScheme(scheme) assert.Nil(t, err) for _, c := range []struct { testName string - appSet argoappsetv1.ApplicationSet + appSet argoappsv1.ApplicationSet expectedMsg string }{ { testName: "invalid generator, without annotation", - appSet: argoappsetv1.ApplicationSet{ + appSet: argoappsv1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-app-set", Namespace: "namespace", }, - Spec: argoappsetv1.ApplicationSetSpec{ - Generators: []argoappsetv1.ApplicationSetGenerator{ + Spec: argoappsv1.ApplicationSetSpec{ + Generators: []argoappsv1.ApplicationSetGenerator{ { - List: &argoappsetv1.ListGenerator{}, + List: &argoappsv1.ListGenerator{}, Clusters: nil, Git: nil, }, @@ -647,7 +873,7 @@ func TestCheckInvalidGenerators(t *testing.T) { { List: nil, Clusters: nil, - Git: &argoappsetv1.GitGenerator{}, + Git: &argoappsv1.GitGenerator{}, }, }, }, @@ -656,7 +882,7 @@ func TestCheckInvalidGenerators(t *testing.T) { }, { testName: "invalid generator, with annotation", - appSet: argoappsetv1.ApplicationSet{ + appSet: argoappsv1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-app-set", Namespace: "namespace", @@ -673,10 +899,10 @@ func TestCheckInvalidGenerators(t *testing.T) { }`, }, }, - Spec: argoappsetv1.ApplicationSetSpec{ - Generators: []argoappsetv1.ApplicationSetGenerator{ + Spec: argoappsv1.ApplicationSetSpec{ + Generators: []argoappsv1.ApplicationSetGenerator{ { - List: &argoappsetv1.ListGenerator{}, + List: &argoappsv1.ListGenerator{}, Clusters: nil, Git: nil, }, @@ -688,7 +914,7 @@ func TestCheckInvalidGenerators(t *testing.T) { { List: nil, Clusters: nil, - Git: &argoappsetv1.GitGenerator{}, + Git: &argoappsv1.GitGenerator{}, }, { List: nil, @@ -719,20 +945,20 @@ func TestCheckInvalidGenerators(t *testing.T) { func TestInvalidGenerators(t *testing.T) { scheme := runtime.NewScheme() - err := argoappsetv1.AddToScheme(scheme) + err := argoappsv1.AddToScheme(scheme) assert.Nil(t, err) err = argoappsv1.AddToScheme(scheme) assert.Nil(t, err) for _, c := range []struct { testName string - appSet argoappsetv1.ApplicationSet + appSet argoappsv1.ApplicationSet expectedInvalid bool expectedNames map[string]bool }{ { testName: "valid generators, with annotation", - appSet: argoappsetv1.ApplicationSet{ + appSet: argoappsv1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", @@ -748,22 +974,22 @@ func TestInvalidGenerators(t *testing.T) { }`, }, }, - Spec: argoappsetv1.ApplicationSetSpec{ - Generators: []argoappsetv1.ApplicationSetGenerator{ + Spec: argoappsv1.ApplicationSetSpec{ + Generators: []argoappsv1.ApplicationSetGenerator{ { - List: &argoappsetv1.ListGenerator{}, + List: &argoappsv1.ListGenerator{}, Clusters: nil, Git: nil, }, { List: nil, - Clusters: &argoappsetv1.ClusterGenerator{}, + Clusters: &argoappsv1.ClusterGenerator{}, Git: nil, }, { List: nil, Clusters: nil, - Git: &argoappsetv1.GitGenerator{}, + Git: &argoappsv1.GitGenerator{}, }, }, }, @@ -773,13 +999,13 @@ func TestInvalidGenerators(t *testing.T) { }, { testName: "invalid generators, no annotation", - appSet: argoappsetv1.ApplicationSet{ + appSet: argoappsv1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argoappsetv1.ApplicationSetSpec{ - Generators: []argoappsetv1.ApplicationSetGenerator{ + Spec: argoappsv1.ApplicationSetSpec{ + Generators: []argoappsv1.ApplicationSetGenerator{ { List: nil, Clusters: nil, @@ -798,16 +1024,16 @@ func TestInvalidGenerators(t *testing.T) { }, { testName: "valid and invalid generators, no annotation", - appSet: argoappsetv1.ApplicationSet{ + appSet: argoappsv1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", }, - Spec: argoappsetv1.ApplicationSetSpec{ - Generators: []argoappsetv1.ApplicationSetGenerator{ + Spec: argoappsv1.ApplicationSetSpec{ + Generators: []argoappsv1.ApplicationSetGenerator{ { List: nil, - Clusters: &argoappsetv1.ClusterGenerator{}, + Clusters: &argoappsv1.ClusterGenerator{}, Git: nil, }, { @@ -818,7 +1044,7 @@ func TestInvalidGenerators(t *testing.T) { { List: nil, Clusters: nil, - Git: &argoappsetv1.GitGenerator{}, + Git: &argoappsv1.GitGenerator{}, }, }, }, @@ -828,7 +1054,7 @@ func TestInvalidGenerators(t *testing.T) { }, { testName: "valid and invalid generators, with annotation", - appSet: argoappsetv1.ApplicationSet{ + appSet: argoappsv1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", @@ -845,11 +1071,11 @@ func TestInvalidGenerators(t *testing.T) { }`, }, }, - Spec: argoappsetv1.ApplicationSetSpec{ - Generators: []argoappsetv1.ApplicationSetGenerator{ + Spec: argoappsv1.ApplicationSetSpec{ + Generators: []argoappsv1.ApplicationSetGenerator{ { List: nil, - Clusters: &argoappsetv1.ClusterGenerator{}, + Clusters: &argoappsv1.ClusterGenerator{}, Git: nil, }, { @@ -860,7 +1086,7 @@ func TestInvalidGenerators(t *testing.T) { { List: nil, Clusters: nil, - Git: &argoappsetv1.GitGenerator{}, + Git: &argoappsv1.GitGenerator{}, }, { List: nil, @@ -878,7 +1104,7 @@ func TestInvalidGenerators(t *testing.T) { }, { testName: "invalid generator, annotation with missing spec", - appSet: argoappsetv1.ApplicationSet{ + appSet: argoappsv1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", @@ -887,8 +1113,8 @@ func TestInvalidGenerators(t *testing.T) { }`, }, }, - Spec: argoappsetv1.ApplicationSetSpec{ - Generators: []argoappsetv1.ApplicationSetGenerator{ + Spec: argoappsv1.ApplicationSetSpec{ + Generators: []argoappsv1.ApplicationSetGenerator{ { List: nil, Clusters: nil, @@ -902,7 +1128,7 @@ func TestInvalidGenerators(t *testing.T) { }, { testName: "invalid generator, annotation with missing generators array", - appSet: argoappsetv1.ApplicationSet{ + appSet: argoappsv1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", @@ -913,8 +1139,8 @@ func TestInvalidGenerators(t *testing.T) { }`, }, }, - Spec: argoappsetv1.ApplicationSetSpec{ - Generators: []argoappsetv1.ApplicationSetGenerator{ + Spec: argoappsv1.ApplicationSetSpec{ + Generators: []argoappsv1.ApplicationSetGenerator{ { List: nil, Clusters: nil, @@ -928,7 +1154,7 @@ func TestInvalidGenerators(t *testing.T) { }, { testName: "invalid generator, annotation with empty generators array", - appSet: argoappsetv1.ApplicationSet{ + appSet: argoappsv1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", @@ -941,8 +1167,8 @@ func TestInvalidGenerators(t *testing.T) { }`, }, }, - Spec: argoappsetv1.ApplicationSetSpec{ - Generators: []argoappsetv1.ApplicationSetGenerator{ + Spec: argoappsv1.ApplicationSetSpec{ + Generators: []argoappsv1.ApplicationSetGenerator{ { List: nil, Clusters: nil, @@ -956,7 +1182,7 @@ func TestInvalidGenerators(t *testing.T) { }, { testName: "invalid generator, annotation with empty generator", - appSet: argoappsetv1.ApplicationSet{ + appSet: argoappsv1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "namespace", @@ -970,8 +1196,8 @@ func TestInvalidGenerators(t *testing.T) { }`, }, }, - Spec: argoappsetv1.ApplicationSetSpec{ - Generators: []argoappsetv1.ApplicationSetGenerator{ + Spec: argoappsv1.ApplicationSetSpec{ + Generators: []argoappsv1.ApplicationSetGenerator{ { List: nil, Clusters: nil, @@ -1016,3 +1242,129 @@ func TestNormalizeBitbucketBasePath(t *testing.T) { assert.Equal(t, c.expectedBasePath, result, c.testName) } } + +func TestSlugify(t *testing.T) { + for _, c := range []struct { + branch string + smartTruncate bool + length int + expectedBasePath string + }{ + { + branch: "feat/a_really+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature", + smartTruncate: false, + length: 50, + expectedBasePath: "feat-a-really-long-pull-request-name-to-test-argo", + }, + { + branch: "feat/a_really+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature", + smartTruncate: true, + length: 53, + expectedBasePath: "feat-a-really-long-pull-request-name-to-test-argo", + }, + { + branch: "feat/areallylongpullrequestnametotestargoslugificationandbranchnameshorteningfeature", + smartTruncate: true, + length: 50, + expectedBasePath: "feat", + }, + { + branch: "feat/areallylongpullrequestnametotestargoslugificationandbranchnameshorteningfeature", + smartTruncate: false, + length: 50, + expectedBasePath: "feat-areallylongpullrequestnametotestargoslugifica", + }, + } { + result := SlugifyName(c.length, c.smartTruncate, c.branch) + assert.Equal(t, c.expectedBasePath, result, c.branch) + } +} + +func TestGetTLSConfig(t *testing.T) { + // certParsed, err := tls.X509KeyPair(test.Cert, test.PrivateKey) + // require.NoError(t, err) + + temppath := t.TempDir() + cert := ` +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- +` + + rootCAPath := path.Join(temppath, "foo.example.com") + err := os.WriteFile(rootCAPath, []byte(cert), 0666) + if err != nil { + panic(err) + } + + certPool := x509.NewCertPool() + ok := certPool.AppendCertsFromPEM([]byte(cert)) + assert.True(t, ok) + + testCases := []struct { + name string + scmRootCAPath string + insecure bool + validateCertInTlsConfig bool + }{ + { + name: "Insecure mode configured, SCM Root CA Path not set", + scmRootCAPath: "", + insecure: true, + validateCertInTlsConfig: false, + }, + { + name: "SCM Root CA Path set, Insecure mode set to false", + scmRootCAPath: rootCAPath, + insecure: false, + validateCertInTlsConfig: true, + }, + { + name: "SCM Root CA Path set, Insecure mode set to true", + scmRootCAPath: rootCAPath, + insecure: true, + validateCertInTlsConfig: true, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + tlsConfig := GetTlsConfig(testCase.scmRootCAPath, testCase.insecure) + assert.Equal(t, testCase.insecure, tlsConfig.InsecureSkipVerify) + if testCase.validateCertInTlsConfig { + assert.NotNil(t, tlsConfig) + assert.True(t, tlsConfig.RootCAs.Equal(certPool)) + } + }) + } +} diff --git a/applicationset/webhook/testdata/azuredevops-pull-request.json b/applicationset/webhook/testdata/azuredevops-pull-request.json new file mode 100644 index 0000000000000..80c5e7cb90822 --- /dev/null +++ b/applicationset/webhook/testdata/azuredevops-pull-request.json @@ -0,0 +1,85 @@ +{ + "id": "2ab4e3d3-b7a6-425e-92b1-5a9982c1269e", + "eventType": "git.pullrequest.created", + "publisherId": "tfs", + "scope": "all", + "message": { + "text": "Jamal Hartnett created a new pull request", + "html": "Jamal Hartnett created a new pull request", + "markdown": "Jamal Hartnett created a new pull request" + }, + "detailedMessage": { + "text": "Jamal Hartnett created a new pull request\r\n\r\n- Merge status: Succeeded\r\n- Merge commit: eef717(https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72)\r\n", + "html": "Jamal Hartnett created a new pull request\r\n
    \r\n
  • Merge status: Succeeded
  • \r\n
  • Merge commit: eef717
  • \r\n
", + "markdown": "Jamal Hartnett created a new pull request\r\n\r\n+ Merge status: Succeeded\r\n+ Merge commit: [eef717](https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72)\r\n" + }, + "resource": { + "repository": { + "id": "4bc14d40-c903-45e2-872e-0462c7748079", + "name": "Fabrikam", + "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079", + "project": { + "id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", + "name": "DefaultCollection", + "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", + "state": "wellFormed" + }, + "defaultBranch": "refs/heads/master", + "remoteUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam" + }, + "pullRequestId": 1, + "status": "active", + "createdBy": { + "id": "54d125f7-69f7-4191-904f-c5b96b6261c8", + "displayName": "Jamal Hartnett", + "uniqueName": "fabrikamfiber4@hotmail.com", + "url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/54d125f7-69f7-4191-904f-c5b96b6261c8", + "imageUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8" + }, + "creationDate": "2014-06-17T16:55:46.589889Z", + "title": "my first pull request", + "description": " - test2\r\n", + "sourceRefName": "refs/heads/mytopic", + "targetRefName": "refs/heads/master", + "mergeStatus": "succeeded", + "mergeId": "a10bb228-6ba6-4362-abd7-49ea21333dbd", + "lastMergeSourceCommit": { + "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c", + "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c" + }, + "lastMergeTargetCommit": { + "commitId": "a511f535b1ea495ee0c903badb68fbc83772c882", + "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/a511f535b1ea495ee0c903badb68fbc83772c882" + }, + "lastMergeCommit": { + "commitId": "eef717f69257a6333f221566c1c987dc94cc0d72", + "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72" + }, + "reviewers": [ + { + "reviewerUrl": null, + "vote": 0, + "id": "2ea2d095-48f9-4cd6-9966-62f6f574096c", + "displayName": "[Mobile]\\Mobile Team", + "uniqueName": "vstfs:///Classification/TeamProject/f0811a3b-8c8a-4e43-a3bf-9a049b4835bd\\Mobile Team", + "url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/2ea2d095-48f9-4cd6-9966-62f6f574096c", + "imageUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=2ea2d095-48f9-4cd6-9966-62f6f574096c", + "isContainer": true + } + ], + "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1" + }, + "resourceVersion": "1.0", + "resourceContainers": { + "collection": { + "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2" + }, + "account": { + "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e" + }, + "project": { + "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f" + } + }, + "createdDate": "2016-09-19T13:03:27.2879096Z" + } \ No newline at end of file diff --git a/applicationset/webhook/testdata/azuredevops-push.json b/applicationset/webhook/testdata/azuredevops-push.json new file mode 100644 index 0000000000000..41ee074892e39 --- /dev/null +++ b/applicationset/webhook/testdata/azuredevops-push.json @@ -0,0 +1,76 @@ +{ + "id": "03c164c2-8912-4d5e-8009-3707d5f83734", + "eventType": "git.push", + "publisherId": "tfs", + "scope": "all", + "message": { + "text": "Jamal Hartnett pushed updates to branch master of repository Fabrikam-Fiber-Git.", + "html": "Jamal Hartnett pushed updates to branch master of repository Fabrikam-Fiber-Git.", + "markdown": "Jamal Hartnett pushed updates to branch `master` of repository `Fabrikam-Fiber-Git`." + }, + "detailedMessage": { + "text": "Jamal Hartnett pushed 1 commit to branch master of repository Fabrikam-Fiber-Git.\n - Fixed bug in web.config file 33b55f7c", + "html": "Jamal Hartnett pushed 1 commit to branch master of repository Fabrikam-Fiber-Git.\n
    \n
  • Fixed bug in web.config file 33b55f7c\n
", + "markdown": "Jamal Hartnett pushed 1 commit to branch [master](https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/#version=GBmaster) of repository [Fabrikam-Fiber-Git](https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/).\n* Fixed bug in web.config file [33b55f7c](https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74)" + }, + "resource": { + "commits": [ + { + "commitId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74", + "author": { + "name": "Jamal Hartnett", + "email": "fabrikamfiber4@hotmail.com", + "date": "2015-02-25T19:01:00Z" + }, + "committer": { + "name": "Jamal Hartnett", + "email": "fabrikamfiber4@hotmail.com", + "date": "2015-02-25T19:01:00Z" + }, + "comment": "Fixed bug in web.config file", + "url": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74" + } + ], + "refUpdates": [ + { + "name": "refs/heads/master", + "oldObjectId": "aad331d8d3b131fa9ae03cf5e53965b51942618a", + "newObjectId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74" + } + ], + "repository": { + "id": "278d5cd2-584d-4b63-824a-2ba458937249", + "name": "Fabrikam-Fiber-Git", + "url": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_apis/repos/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249", + "project": { + "id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", + "name": "DefaultCollection", + "url": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", + "state": "wellFormed" + }, + "defaultBranch": "refs/heads/master", + "remoteUrl": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git" + }, + "pushedBy": { + "id": "00067FFED5C7AF52@Live.com", + "displayName": "Jamal Hartnett", + "uniqueName": "Windows Live ID\\fabrikamfiber4@hotmail.com" + }, + "pushId": 14, + "date": "2014-05-02T19:17:13.3309587Z", + "url": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_apis/repos/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249/pushes/14" + }, + "resourceVersion": "1.0", + "resourceContainers": { + "collection": { + "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2" + }, + "account": { + "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e" + }, + "project": { + "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f" + } + }, + "createdDate": "2016-09-19T13:03:27.0379153Z" + } \ No newline at end of file diff --git a/applicationset/webhook/testdata/github-pull-request-labeled-event.json b/applicationset/webhook/testdata/github-pull-request-labeled-event.json new file mode 100644 index 0000000000000..f912a2fdb4a97 --- /dev/null +++ b/applicationset/webhook/testdata/github-pull-request-labeled-event.json @@ -0,0 +1,473 @@ +{ + "action": "labeled", + "number": 2, + "label": { + "id": 6129306173, + "node_id": "LA_kwDOIqudU88AAAABbVXKPQ", + "url": "https://api.github.com/repos/SG60/backstage/labels/deploy-preview", + "name": "deploy-preview", + "color": "bfd4f2", + "default": false, + "description": "" + }, + "pull_request": { + "url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2", + "id": 279147437, + "node_id": "MDExOlB1bGxSZXF1ZXN0Mjc5MTQ3NDM3", + "html_url": "https://github.com/Codertocat/Hello-World/pull/2", + "diff_url": "https://github.com/Codertocat/Hello-World/pull/2.diff", + "patch_url": "https://github.com/Codertocat/Hello-World/pull/2.patch", + "issue_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/2", + "number": 2, + "state": "open", + "locked": false, + "title": "Update the README with new information.", + "user": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "body": "This is a pretty simple change that we need to pull into master.", + "created_at": "2019-05-15T15:20:33Z", + "updated_at": "2019-05-15T15:20:33Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": null, + "assignee": null, + "assignees": [], + "requested_reviewers": [], + "requested_teams": [], + "labels": [ + { + "id": 6129306173, + "node_id": "LA_kwDOIqudU88AAAABbVXKPQ", + "url": "https://api.github.com/repos/Codertocat/Hello-World/labels/deploy-preview", + "name": "deploy-preview", + "color": "bfd4f2", + "default": false, + "description": "" + } + ], + "milestone": null, + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2/commits", + "review_comments_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2/comments", + "review_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/2/comments", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "head": { + "label": "Codertocat:changes", + "ref": "changes", + "sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "user": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 186853002, + "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": false, + "owner": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/Codertocat/Hello-World", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks", + "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams", + "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks", + "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}", + "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events", + "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}", + "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}", + "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags", + "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages", + "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers", + "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors", + "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription", + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}", + "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges", + "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads", + "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}", + "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}", + "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}", + "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments", + "created_at": "2019-05-15T15:19:25Z", + "updated_at": "2019-05-15T15:19:27Z", + "pushed_at": "2019-05-15T15:20:32Z", + "git_url": "git://github.com/Codertocat/Hello-World.git", + "ssh_url": "git@github.com:Codertocat/Hello-World.git", + "clone_url": "https://github.com/Codertocat/Hello-World.git", + "svn_url": "https://github.com/Codertocat/Hello-World", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 2, + "license": null, + "forks": 0, + "open_issues": 2, + "watchers": 0, + "default_branch": "master", + "allow_squash_merge": true, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "delete_branch_on_merge": false + } + }, + "base": { + "label": "Codertocat:master", + "ref": "master", + "sha": "f95f852bd8fca8fcc58a9a2d6c842781e32a215e", + "user": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 186853002, + "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": false, + "owner": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/Codertocat/Hello-World", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks", + "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams", + "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks", + "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}", + "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events", + "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}", + "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}", + "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags", + "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages", + "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers", + "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors", + "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription", + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}", + "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges", + "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads", + "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}", + "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}", + "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}", + "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments", + "created_at": "2019-05-15T15:19:25Z", + "updated_at": "2019-05-15T15:19:27Z", + "pushed_at": "2019-05-15T15:20:32Z", + "git_url": "git://github.com/Codertocat/Hello-World.git", + "ssh_url": "git@github.com:Codertocat/Hello-World.git", + "clone_url": "https://github.com/Codertocat/Hello-World.git", + "svn_url": "https://github.com/Codertocat/Hello-World", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 2, + "license": null, + "forks": 0, + "open_issues": 2, + "watchers": 0, + "default_branch": "master", + "allow_squash_merge": true, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "delete_branch_on_merge": false + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2" + }, + "html": { + "href": "https://github.com/Codertocat/Hello-World/pull/2" + }, + "issue": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/issues/2" + }, + "comments": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/issues/2/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/statuses/ec26c3e57ca3a959ca5aad62de7213c562f8c821" + } + }, + "author_association": "OWNER", + "draft": false, + "merged": false, + "mergeable": null, + "rebaseable": null, + "mergeable_state": "unknown", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "maintainer_can_modify": false, + "commits": 1, + "additions": 1, + "deletions": 1, + "changed_files": 1 + }, + "repository": { + "id": 186853002, + "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": false, + "owner": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/Codertocat/Hello-World", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks", + "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams", + "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks", + "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}", + "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events", + "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}", + "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}", + "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags", + "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages", + "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers", + "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors", + "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription", + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}", + "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges", + "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads", + "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}", + "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}", + "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}", + "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments", + "created_at": "2019-05-15T15:19:25Z", + "updated_at": "2019-05-15T15:19:27Z", + "pushed_at": "2019-05-15T15:20:32Z", + "git_url": "git://github.com/Codertocat/Hello-World.git", + "ssh_url": "git@github.com:Codertocat/Hello-World.git", + "clone_url": "https://github.com/Codertocat/Hello-World.git", + "svn_url": "https://github.com/Codertocat/Hello-World", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 2, + "license": null, + "forks": 0, + "open_issues": 2, + "watchers": 0, + "default_branch": "master" + }, + "sender": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} diff --git a/applicationset/webhook/webhook.go b/applicationset/webhook/webhook.go index 617f84574a699..d55e63e064f5a 100644 --- a/applicationset/webhook/webhook.go +++ b/applicationset/webhook/webhook.go @@ -2,6 +2,7 @@ package webhook import ( "context" + "errors" "fmt" "html" "net/http" @@ -19,17 +20,24 @@ import ( "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" argosettings "github.com/argoproj/argo-cd/v2/util/settings" + "github.com/go-playground/webhooks/v6/azuredevops" + "github.com/go-playground/webhooks/v6/github" + "github.com/go-playground/webhooks/v6/gitlab" log "github.com/sirupsen/logrus" - "gopkg.in/go-playground/webhooks.v5/github" - "gopkg.in/go-playground/webhooks.v5/gitlab" +) + +var ( + errBasicAuthVerificationFailed = errors.New("basic auth verification failed") ) type WebhookHandler struct { - namespace string - github *github.Webhook - gitlab *gitlab.Webhook - client client.Client - generators map[string]generators.Generator + namespace string + github *github.Webhook + gitlab *gitlab.Webhook + azuredevops *azuredevops.Webhook + azuredevopsAuthHandler func(r *http.Request) error + client client.Client + generators map[string]generators.Generator } type gitGeneratorInfo struct { @@ -39,8 +47,14 @@ type gitGeneratorInfo struct { } type prGeneratorInfo struct { - Github *prGeneratorGithubInfo - Gitlab *prGeneratorGitlabInfo + Azuredevops *prGeneratorAzuredevopsInfo + Github *prGeneratorGithubInfo + Gitlab *prGeneratorGitlabInfo +} + +type prGeneratorAzuredevopsInfo struct { + Repo string + Project string } type prGeneratorGithubInfo struct { @@ -68,13 +82,28 @@ func NewWebhookHandler(namespace string, argocdSettingsMgr *argosettings.Setting if err != nil { return nil, fmt.Errorf("Unable to init GitLab webhook: %v", err) } + azuredevopsHandler, err := azuredevops.New() + if err != nil { + return nil, fmt.Errorf("Unable to init Azure DevOps webhook: %v", err) + } + azuredevopsAuthHandler := func(r *http.Request) error { + if argocdSettings.WebhookAzureDevOpsUsername != "" && argocdSettings.WebhookAzureDevOpsPassword != "" { + username, password, ok := r.BasicAuth() + if !ok || username != argocdSettings.WebhookAzureDevOpsUsername || password != argocdSettings.WebhookAzureDevOpsPassword { + return errBasicAuthVerificationFailed + } + } + return nil + } return &WebhookHandler{ - namespace: namespace, - github: githubHandler, - gitlab: gitlabHandler, - client: client, - generators: generators, + namespace: namespace, + github: githubHandler, + gitlab: gitlabHandler, + azuredevops: azuredevopsHandler, + azuredevopsAuthHandler: azuredevopsAuthHandler, + client: client, + generators: generators, }, nil } @@ -98,6 +127,7 @@ func (h *WebhookHandler) HandleEvent(payload interface{}) { // check if the ApplicationSet uses any generator that is relevant to the payload shouldRefresh = shouldRefreshGitGenerator(gen.Git, gitGenInfo) || shouldRefreshPRGenerator(gen.PullRequest, prGenInfo) || + shouldRefreshPluginGenerator(gen.Plugin) || h.shouldRefreshMatrixGenerator(gen.Matrix, &appSet, gitGenInfo, prGenInfo) || h.shouldRefreshMergeGenerator(gen.Merge, &appSet, gitGenInfo, prGenInfo) if shouldRefresh { @@ -124,6 +154,14 @@ func (h *WebhookHandler) Handler(w http.ResponseWriter, r *http.Request) { payload, err = h.github.Parse(r, github.PushEvent, github.PullRequestEvent, github.PingEvent) case r.Header.Get("X-Gitlab-Event") != "": payload, err = h.gitlab.Parse(r, gitlab.PushEvents, gitlab.TagEvents, gitlab.MergeRequestEvents) + case r.Header.Get("X-Vss-Activityid") != "": + if err = h.azuredevopsAuthHandler(r); err != nil { + if errors.Is(err, errBasicAuthVerificationFailed) { + log.WithField(common.SecurityField, common.SecurityHigh).Infof("Azure DevOps webhook basic auth verification failed") + } + } else { + payload, err = h.azuredevops.Parse(r, azuredevops.GitPushEventType, azuredevops.GitPullRequestCreatedEventType, azuredevops.GitPullRequestUpdatedEventType, azuredevops.GitPullRequestMergedEventType) + } default: log.Debug("Ignoring unknown webhook event") http.Error(w, "Unknown webhook event", http.StatusBadRequest) @@ -133,7 +171,7 @@ func (h *WebhookHandler) Handler(w http.ResponseWriter, r *http.Request) { if err != nil { log.Infof("Webhook processing failed: %s", err) status := http.StatusBadRequest - if r.Method != "POST" { + if r.Method != http.MethodPost { status = http.StatusMethodNotAllowed } http.Error(w, fmt.Sprintf("Webhook processing failed: %s", html.EscapeString(err.Error())), status) @@ -163,6 +201,12 @@ func getGitGeneratorInfo(payload interface{}) *gitGeneratorInfo { webURL = payload.Project.WebURL revision = parseRevision(payload.Ref) touchedHead = payload.Project.DefaultBranch == revision + case azuredevops.GitPushEvent: + // See: https://learn.microsoft.com/en-us/azure/devops/service-hooks/events?view=azure-devops#git.push + webURL = payload.Resource.Repository.RemoteURL + revision = parseRevision(payload.Resource.RefUpdates[0].Name) + touchedHead = payload.Resource.RefUpdates[0].Name == payload.Resource.Repository.DefaultBranch + // unfortunately, Azure DevOps doesn't provide a list of changed files default: return nil } @@ -228,6 +272,18 @@ func getPRGeneratorInfo(payload interface{}) *prGeneratorInfo { Project: strconv.FormatInt(payload.ObjectAttributes.TargetProjectID, 10), APIHostname: urlObj.Hostname(), } + case azuredevops.GitPullRequestEvent: + if !isAllowedAzureDevOpsPullRequestAction(string(payload.EventType)) { + return nil + } + + repo := payload.Resource.Repository.Name + project := payload.Resource.Repository.Project.Name + + info.Azuredevops = &prGeneratorAzuredevopsInfo{ + Repo: repo, + Project: project, + } default: return nil } @@ -255,6 +311,13 @@ var gitlabAllowedPullRequestActions = []string{ "merge", } +// azuredevopsAllowedPullRequestActions is a list of Azure DevOps actions that allow refresh +var azuredevopsAllowedPullRequestActions = []string{ + "git.pullrequest.created", + "git.pullrequest.merged", + "git.pullrequest.updated", +} + func isAllowedGithubPullRequestAction(action string) bool { for _, allow := range githubAllowedPullRequestActions { if allow == action { @@ -273,6 +336,15 @@ func isAllowedGitlabPullRequestAction(action string) bool { return false } +func isAllowedAzureDevOpsPullRequestAction(action string) bool { + for _, allow := range azuredevopsAllowedPullRequestActions { + if allow == action { + return true + } + } + return false +} + func shouldRefreshGitGenerator(gen *v1alpha1.GitGenerator, info *gitGeneratorInfo) bool { if gen == nil || info == nil { return false @@ -287,6 +359,10 @@ func shouldRefreshGitGenerator(gen *v1alpha1.GitGenerator, info *gitGeneratorInf return true } +func shouldRefreshPluginGenerator(gen *v1alpha1.PluginGenerator) bool { + return gen != nil +} + func genRevisionHasChanged(gen *v1alpha1.GitGenerator, revision string, touchedHead bool) bool { targetRev := parseRevision(gen.Revision) if targetRev == "HEAD" || targetRev == "" { // revision is head @@ -336,10 +412,12 @@ func shouldRefreshPRGenerator(gen *v1alpha1.PullRequestGenerator, info *prGenera } if gen.Github != nil && info.Github != nil { - if gen.Github.Owner != info.Github.Owner { + // repository owner and name are case-insensitive + // See https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests + if !strings.EqualFold(gen.Github.Owner, info.Github.Owner) { return false } - if gen.Github.Repo != info.Github.Repo { + if !strings.EqualFold(gen.Github.Repo, info.Github.Repo) { return false } api := gen.Github.API @@ -354,6 +432,16 @@ func shouldRefreshPRGenerator(gen *v1alpha1.PullRequestGenerator, info *prGenera return true } + if gen.AzureDevOps != nil && info.Azuredevops != nil { + if gen.AzureDevOps.Project != info.Azuredevops.Project { + return false + } + if gen.AzureDevOps.Repo != info.Azuredevops.Repo { + return false + } + return true + } + return false } @@ -417,6 +505,7 @@ func (h *WebhookHandler) shouldRefreshMatrixGenerator(gen *v1alpha1.MatrixGenera SCMProvider: g0.SCMProvider, ClusterDecisionResource: g0.ClusterDecisionResource, PullRequest: g0.PullRequest, + Plugin: g0.Plugin, Matrix: matrixGenerator0, Merge: mergeGenerator0, } @@ -471,6 +560,7 @@ func (h *WebhookHandler) shouldRefreshMatrixGenerator(gen *v1alpha1.MatrixGenera SCMProvider: g1.SCMProvider, ClusterDecisionResource: g1.ClusterDecisionResource, PullRequest: g1.PullRequest, + Plugin: g1.Plugin, Matrix: matrixGenerator1, Merge: mergeGenerator1, } @@ -478,7 +568,7 @@ func (h *WebhookHandler) shouldRefreshMatrixGenerator(gen *v1alpha1.MatrixGenera // Interpolate second child generator with params from first child generator, if there are any params if len(params) != 0 { for _, p := range params { - tempInterpolatedGenerator, err := generators.InterpolateGenerator(requestedGenerator1, p, appSet.Spec.GoTemplate) + tempInterpolatedGenerator, err := generators.InterpolateGenerator(requestedGenerator1, p, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) interpolatedGenerator := &tempInterpolatedGenerator if err != nil { log.Error(err) @@ -488,6 +578,7 @@ func (h *WebhookHandler) shouldRefreshMatrixGenerator(gen *v1alpha1.MatrixGenera // Check all interpolated child generators if shouldRefreshGitGenerator(interpolatedGenerator.Git, gitGenInfo) || shouldRefreshPRGenerator(interpolatedGenerator.PullRequest, prGenInfo) || + shouldRefreshPluginGenerator(interpolatedGenerator.Plugin) || h.shouldRefreshMatrixGenerator(interpolatedGenerator.Matrix, appSet, gitGenInfo, prGenInfo) || h.shouldRefreshMergeGenerator(requestedGenerator1.Merge, appSet, gitGenInfo, prGenInfo) { return true @@ -498,6 +589,7 @@ func (h *WebhookHandler) shouldRefreshMatrixGenerator(gen *v1alpha1.MatrixGenera // First child generator didn't return any params, just check the second child generator return shouldRefreshGitGenerator(requestedGenerator1.Git, gitGenInfo) || shouldRefreshPRGenerator(requestedGenerator1.PullRequest, prGenInfo) || + shouldRefreshPluginGenerator(requestedGenerator1.Plugin) || h.shouldRefreshMatrixGenerator(requestedGenerator1.Matrix, appSet, gitGenInfo, prGenInfo) || h.shouldRefreshMergeGenerator(requestedGenerator1.Merge, appSet, gitGenInfo, prGenInfo) } @@ -553,7 +645,7 @@ func refreshApplicationSet(c client.Client, appSet *v1alpha1.ApplicationSet) err return retry.RetryOnConflict(retry.DefaultBackoff, func() error { err := c.Get(context.Background(), types.NamespacedName{Name: appSet.Name, Namespace: appSet.Namespace}, appSet) if err != nil { - return err + return fmt.Errorf("error getting ApplicationSet: %w", err) } if appSet.Annotations == nil { appSet.Annotations = map[string]string{} diff --git a/applicationset/webhook/webhook_test.go b/applicationset/webhook/webhook_test.go index c1e0a7eba3e18..d22b1a07ca6f2 100644 --- a/applicationset/webhook/webhook_test.go +++ b/applicationset/webhook/webhook_test.go @@ -15,34 +15,33 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" corev1 "k8s.io/api/core/v1" - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" kubefake "k8s.io/client-go/kubernetes/fake" "sigs.k8s.io/controller-runtime/pkg/client/fake" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "github.com/argoproj/argo-cd/v2/applicationset/generators" "github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" argosettings "github.com/argoproj/argo-cd/v2/util/settings" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ) type generatorMock struct { mock.Mock } -func (g *generatorMock) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate { - return &argoprojiov1alpha1.ApplicationSetTemplate{} +func (g *generatorMock) GetTemplate(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate { + return &v1alpha1.ApplicationSetTemplate{} } -func (g *generatorMock) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, _ *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { +func (g *generatorMock) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, _ *v1alpha1.ApplicationSet) ([]map[string]interface{}, error) { return []map[string]interface{}{}, nil } -func (g *generatorMock) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration { +func (g *generatorMock) GetRequeueAfter(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration { d, _ := time.ParseDuration("10s") return d } @@ -62,7 +61,7 @@ func TestWebhookHandler(t *testing.T) { headerKey: "X-GitHub-Event", headerValue: "push", payloadFile: "github-commit-event.json", - effectedAppSets: []string{"git-github", "matrix-git-github", "merge-git-github", "matrix-scm-git-github", "matrix-nested-git-github", "merge-nested-git-github"}, + effectedAppSets: []string{"git-github", "matrix-git-github", "merge-git-github", "matrix-scm-git-github", "matrix-nested-git-github", "merge-nested-git-github", "plugin", "matrix-pull-request-github-plugin"}, expectedStatusCode: http.StatusOK, expectedRefresh: true, }, @@ -71,7 +70,7 @@ func TestWebhookHandler(t *testing.T) { headerKey: "X-GitHub-Event", headerValue: "push", payloadFile: "github-commit-branch-event.json", - effectedAppSets: []string{"git-github"}, + effectedAppSets: []string{"git-github", "plugin", "matrix-pull-request-github-plugin"}, expectedStatusCode: http.StatusOK, expectedRefresh: true, }, @@ -80,7 +79,7 @@ func TestWebhookHandler(t *testing.T) { headerKey: "X-GitHub-Event", headerValue: "ping", payloadFile: "github-ping-event.json", - effectedAppSets: []string{"git-github"}, + effectedAppSets: []string{"git-github", "plugin"}, expectedStatusCode: http.StatusOK, expectedRefresh: false, }, @@ -89,7 +88,7 @@ func TestWebhookHandler(t *testing.T) { headerKey: "X-Gitlab-Event", headerValue: "Push Hook", payloadFile: "gitlab-event.json", - effectedAppSets: []string{"git-gitlab"}, + effectedAppSets: []string{"git-gitlab", "plugin", "matrix-pull-request-github-plugin"}, expectedStatusCode: http.StatusOK, expectedRefresh: true, }, @@ -98,7 +97,7 @@ func TestWebhookHandler(t *testing.T) { headerKey: "X-Random-Event", headerValue: "Push Hook", payloadFile: "gitlab-event.json", - effectedAppSets: []string{"git-gitlab"}, + effectedAppSets: []string{"git-gitlab", "plugin"}, expectedStatusCode: http.StatusBadRequest, expectedRefresh: false, }, @@ -107,34 +106,43 @@ func TestWebhookHandler(t *testing.T) { headerKey: "X-Random-Event", headerValue: "Push Hook", payloadFile: "invalid-event.json", - effectedAppSets: []string{"git-gitlab"}, + effectedAppSets: []string{"git-gitlab", "plugin"}, expectedStatusCode: http.StatusBadRequest, expectedRefresh: false, }, { - desc: "WebHook from a GitHub repository via pull_reqeuest opened event", + desc: "WebHook from a GitHub repository via pull_request opened event", headerKey: "X-GitHub-Event", headerValue: "pull_request", payloadFile: "github-pull-request-opened-event.json", - effectedAppSets: []string{"pull-request-github", "matrix-pull-request-github", "matrix-scm-pull-request-github", "merge-pull-request-github"}, + effectedAppSets: []string{"pull-request-github", "matrix-pull-request-github", "matrix-scm-pull-request-github", "merge-pull-request-github", "plugin", "matrix-pull-request-github-plugin"}, expectedStatusCode: http.StatusOK, expectedRefresh: true, }, { - desc: "WebHook from a GitHub repository via pull_reqeuest assigned event", + desc: "WebHook from a GitHub repository via pull_request assigned event", headerKey: "X-GitHub-Event", headerValue: "pull_request", payloadFile: "github-pull-request-assigned-event.json", - effectedAppSets: []string{"pull-request-github", "matrix-pull-request-github", "matrix-scm-pull-request-github", "merge-pull-request-github"}, + effectedAppSets: []string{"pull-request-github", "matrix-pull-request-github", "matrix-scm-pull-request-github", "merge-pull-request-github", "plugin", "matrix-pull-request-github-plugin"}, expectedStatusCode: http.StatusOK, expectedRefresh: false, }, + { + desc: "WebHook from a GitHub repository via pull_request labeled event", + headerKey: "X-GitHub-Event", + headerValue: "pull_request", + payloadFile: "github-pull-request-labeled-event.json", + effectedAppSets: []string{"pull-request-github", "matrix-pull-request-github", "matrix-scm-pull-request-github", "merge-pull-request-github", "plugin", "matrix-pull-request-github-plugin"}, + expectedStatusCode: http.StatusOK, + expectedRefresh: true, + }, { desc: "WebHook from a GitLab repository via open merge request event", headerKey: "X-Gitlab-Event", headerValue: "Merge Request Hook", payloadFile: "gitlab-merge-request-open-event.json", - effectedAppSets: []string{"pull-request-gitlab"}, + effectedAppSets: []string{"pull-request-gitlab", "plugin", "matrix-pull-request-github-plugin"}, expectedStatusCode: http.StatusOK, expectedRefresh: true, }, @@ -143,10 +151,28 @@ func TestWebhookHandler(t *testing.T) { headerKey: "X-Gitlab-Event", headerValue: "Merge Request Hook", payloadFile: "gitlab-merge-request-approval-event.json", - effectedAppSets: []string{"pull-request-gitlab"}, + effectedAppSets: []string{"pull-request-gitlab", "plugin"}, expectedStatusCode: http.StatusOK, expectedRefresh: false, }, + { + desc: "WebHook from a Azure DevOps repository via Commit", + headerKey: "X-Vss-Activityid", + headerValue: "Push Hook", + payloadFile: "azuredevops-push.json", + effectedAppSets: []string{"git-azure-devops", "plugin", "matrix-pull-request-github-plugin"}, + expectedStatusCode: http.StatusOK, + expectedRefresh: true, + }, + { + desc: "WebHook from a Azure DevOps repository via pull request event", + headerKey: "X-Vss-Activityid", + headerValue: "Pull Request Hook", + payloadFile: "azuredevops-pull-request.json", + effectedAppSets: []string{"pull-request-azure-devops", "plugin", "matrix-pull-request-github-plugin"}, + expectedStatusCode: http.StatusOK, + expectedRefresh: true, + }, } namespace := "test" @@ -162,13 +188,17 @@ func TestWebhookHandler(t *testing.T) { fc := fake.NewClientBuilder().WithScheme(scheme).WithObjects( fakeAppWithGitGenerator("git-github", namespace, "https://github.com/org/repo"), fakeAppWithGitGenerator("git-gitlab", namespace, "https://gitlab/group/name"), - fakeAppWithGithubPullRequestGenerator("pull-request-github", namespace, "Codertocat", "Hello-World"), + fakeAppWithGitGenerator("git-azure-devops", namespace, "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git"), + fakeAppWithGithubPullRequestGenerator("pull-request-github", namespace, "CodErTOcat", "Hello-World"), fakeAppWithGitlabPullRequestGenerator("pull-request-gitlab", namespace, "100500"), + fakeAppWithAzureDevOpsPullRequestGenerator("pull-request-azure-devops", namespace, "DefaultCollection", "Fabrikam"), + fakeAppWithPluginGenerator("plugin", namespace), fakeAppWithMatrixAndGitGenerator("matrix-git-github", namespace, "https://github.com/org/repo"), fakeAppWithMatrixAndPullRequestGenerator("matrix-pull-request-github", namespace, "Codertocat", "Hello-World"), fakeAppWithMatrixAndScmWithGitGenerator("matrix-scm-git-github", namespace, "org"), fakeAppWithMatrixAndScmWithPullRequestGenerator("matrix-scm-pull-request-github", namespace, "Codertocat"), fakeAppWithMatrixAndNestedGitGenerator("matrix-nested-git-github", namespace, "https://github.com/org/repo"), + fakeAppWithMatrixAndPullRequestGeneratorWithPluginGenerator("matrix-pull-request-github-plugin", namespace, "coDErtoCat", "HeLLO-WorLD", "plugin-cm"), fakeAppWithMergeAndGitGenerator("merge-git-github", namespace, "https://github.com/org/repo"), fakeAppWithMergeAndPullRequestGenerator("merge-pull-request-github", namespace, "Codertocat", "Hello-World"), fakeAppWithMergeAndNestedGitGenerator("merge-nested-git-github", namespace, "https://github.com/org/repo"), @@ -177,7 +207,7 @@ func TestWebhookHandler(t *testing.T) { h, err := NewWebhookHandler(namespace, set, fc, mockGenerators()) assert.Nil(t, err) - req := httptest.NewRequest("POST", "/api/webhook", nil) + req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil) req.Header.Set(test.headerKey, test.headerValue) eventJSON, err := os.ReadFile(filepath.Join("testdata", test.payloadFile)) assert.NoError(t, err) @@ -216,6 +246,7 @@ func mockGenerators() map[string]generators.Generator { // generatorMockList := generatorMock{} generatorMockGit := &generatorMock{} generatorMockPR := &generatorMock{} + generatorMockPlugin := &generatorMock{} mockSCMProvider := &scm_provider.MockProvider{ Repos: []*scm_provider.Repository{ { @@ -241,6 +272,7 @@ func mockGenerators() map[string]generators.Generator { "Git": generatorMockGit, "SCMProvider": generatorMockSCM, "PullRequest": generatorMockPR, + "Plugin": generatorMockPlugin, } nestedGenerators := map[string]generators.Generator{ @@ -248,6 +280,7 @@ func mockGenerators() map[string]generators.Generator { "Git": terminalMockGenerators["Git"], "SCMProvider": terminalMockGenerators["SCMProvider"], "PullRequest": terminalMockGenerators["PullRequest"], + "Plugin": terminalMockGenerators["Plugin"], "Matrix": generators.NewMatrixGenerator(terminalMockGenerators), "Merge": generators.NewMergeGenerator(terminalMockGenerators), } @@ -257,20 +290,21 @@ func mockGenerators() map[string]generators.Generator { "Git": terminalMockGenerators["Git"], "SCMProvider": terminalMockGenerators["SCMProvider"], "PullRequest": terminalMockGenerators["PullRequest"], + "Plugin": terminalMockGenerators["Plugin"], "Matrix": generators.NewMatrixGenerator(nestedGenerators), "Merge": generators.NewMergeGenerator(nestedGenerators), } } func TestGenRevisionHasChanged(t *testing.T) { - assert.True(t, genRevisionHasChanged(&argoprojiov1alpha1.GitGenerator{}, "master", true)) - assert.False(t, genRevisionHasChanged(&argoprojiov1alpha1.GitGenerator{}, "master", false)) + assert.True(t, genRevisionHasChanged(&v1alpha1.GitGenerator{}, "master", true)) + assert.False(t, genRevisionHasChanged(&v1alpha1.GitGenerator{}, "master", false)) - assert.True(t, genRevisionHasChanged(&argoprojiov1alpha1.GitGenerator{Revision: "dev"}, "dev", true)) - assert.False(t, genRevisionHasChanged(&argoprojiov1alpha1.GitGenerator{Revision: "dev"}, "master", false)) + assert.True(t, genRevisionHasChanged(&v1alpha1.GitGenerator{Revision: "dev"}, "dev", true)) + assert.False(t, genRevisionHasChanged(&v1alpha1.GitGenerator{Revision: "dev"}, "master", false)) - assert.True(t, genRevisionHasChanged(&argoprojiov1alpha1.GitGenerator{Revision: "refs/heads/dev"}, "dev", true)) - assert.False(t, genRevisionHasChanged(&argoprojiov1alpha1.GitGenerator{Revision: "refs/heads/dev"}, "master", false)) + assert.True(t, genRevisionHasChanged(&v1alpha1.GitGenerator{Revision: "refs/heads/dev"}, "dev", true)) + assert.False(t, genRevisionHasChanged(&v1alpha1.GitGenerator{Revision: "refs/heads/dev"}, "master", false)) } func fakeAppWithGitGenerator(name, namespace, repo string) *v1alpha1.ApplicationSet { @@ -292,17 +326,17 @@ func fakeAppWithGitGenerator(name, namespace, repo string) *v1alpha1.Application } } -func fakeAppWithGitlabPullRequestGenerator(name, namespace, projectId string) *argoprojiov1alpha1.ApplicationSet { - return &argoprojiov1alpha1.ApplicationSet{ +func fakeAppWithGitlabPullRequestGenerator(name, namespace, projectId string) *v1alpha1.ApplicationSet { + return &v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: argoprojiov1alpha1.ApplicationSetSpec{ - Generators: []argoprojiov1alpha1.ApplicationSetGenerator{ + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ { - PullRequest: &argoprojiov1alpha1.PullRequestGenerator{ - GitLab: &argoprojiov1alpha1.PullRequestGeneratorGitLab{ + PullRequest: &v1alpha1.PullRequestGenerator{ + GitLab: &v1alpha1.PullRequestGeneratorGitLab{ Project: projectId, }, }, @@ -312,8 +346,8 @@ func fakeAppWithGitlabPullRequestGenerator(name, namespace, projectId string) *a } } -func fakeAppWithGithubPullRequestGenerator(name, namespace, owner, repo string) *argoprojiov1alpha1.ApplicationSet { - return &argoprojiov1alpha1.ApplicationSet{ +func fakeAppWithGithubPullRequestGenerator(name, namespace, owner, repo string) *v1alpha1.ApplicationSet { + return &v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, @@ -333,6 +367,27 @@ func fakeAppWithGithubPullRequestGenerator(name, namespace, owner, repo string) } } +func fakeAppWithAzureDevOpsPullRequestGenerator(name, namespace, project, repo string) *v1alpha1.ApplicationSet { + return &v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ + { + PullRequest: &v1alpha1.PullRequestGenerator{ + AzureDevOps: &v1alpha1.PullRequestGeneratorAzureDevOps{ + Project: project, + Repo: repo, + }, + }, + }, + }, + }, + } +} + func fakeAppWithMatrixAndGitGenerator(name, namespace, repo string) *v1alpha1.ApplicationSet { return &v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -345,10 +400,10 @@ func fakeAppWithMatrixAndGitGenerator(name, namespace, repo string) *v1alpha1.Ap Matrix: &v1alpha1.MatrixGenerator{ Generators: []v1alpha1.ApplicationSetNestedGenerator{ { - List: &argoprojiov1alpha1.ListGenerator{}, + List: &v1alpha1.ListGenerator{}, }, { - Git: &argoprojiov1alpha1.GitGenerator{ + Git: &v1alpha1.GitGenerator{ RepoURL: repo, }, }, @@ -372,11 +427,11 @@ func fakeAppWithMatrixAndPullRequestGenerator(name, namespace, owner, repo strin Matrix: &v1alpha1.MatrixGenerator{ Generators: []v1alpha1.ApplicationSetNestedGenerator{ { - List: &argoprojiov1alpha1.ListGenerator{}, + List: &v1alpha1.ListGenerator{}, }, { - PullRequest: &argoprojiov1alpha1.PullRequestGenerator{ - Github: &argoprojiov1alpha1.PullRequestGeneratorGithub{ + PullRequest: &v1alpha1.PullRequestGenerator{ + Github: &v1alpha1.PullRequestGeneratorGithub{ Owner: owner, Repo: repo, }, @@ -390,27 +445,27 @@ func fakeAppWithMatrixAndPullRequestGenerator(name, namespace, owner, repo strin } } -func fakeAppWithMatrixAndScmWithGitGenerator(name, namespace, owner string) *argoprojiov1alpha1.ApplicationSet { - return &argoprojiov1alpha1.ApplicationSet{ +func fakeAppWithMatrixAndScmWithGitGenerator(name, namespace, owner string) *v1alpha1.ApplicationSet { + return &v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: argoprojiov1alpha1.ApplicationSetSpec{ - Generators: []argoprojiov1alpha1.ApplicationSetGenerator{ + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ { - Matrix: &argoprojiov1alpha1.MatrixGenerator{ - Generators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{ + Matrix: &v1alpha1.MatrixGenerator{ + Generators: []v1alpha1.ApplicationSetNestedGenerator{ { - SCMProvider: &argoprojiov1alpha1.SCMProviderGenerator{ + SCMProvider: &v1alpha1.SCMProviderGenerator{ CloneProtocol: "ssh", - Github: &argoprojiov1alpha1.SCMProviderGeneratorGithub{ + Github: &v1alpha1.SCMProviderGeneratorGithub{ Organization: owner, }, }, }, { - Git: &argoprojiov1alpha1.GitGenerator{ + Git: &v1alpha1.GitGenerator{ RepoURL: "{{ url }}", }, }, @@ -422,28 +477,28 @@ func fakeAppWithMatrixAndScmWithGitGenerator(name, namespace, owner string) *arg } } -func fakeAppWithMatrixAndScmWithPullRequestGenerator(name, namespace, owner string) *argoprojiov1alpha1.ApplicationSet { - return &argoprojiov1alpha1.ApplicationSet{ +func fakeAppWithMatrixAndScmWithPullRequestGenerator(name, namespace, owner string) *v1alpha1.ApplicationSet { + return &v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: argoprojiov1alpha1.ApplicationSetSpec{ - Generators: []argoprojiov1alpha1.ApplicationSetGenerator{ + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ { - Matrix: &argoprojiov1alpha1.MatrixGenerator{ - Generators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{ + Matrix: &v1alpha1.MatrixGenerator{ + Generators: []v1alpha1.ApplicationSetNestedGenerator{ { - SCMProvider: &argoprojiov1alpha1.SCMProviderGenerator{ + SCMProvider: &v1alpha1.SCMProviderGenerator{ CloneProtocol: "https", - Github: &argoprojiov1alpha1.SCMProviderGeneratorGithub{ + Github: &v1alpha1.SCMProviderGeneratorGithub{ Organization: owner, }, }, }, { - PullRequest: &argoprojiov1alpha1.PullRequestGenerator{ - Github: &argoprojiov1alpha1.PullRequestGeneratorGithub{ + PullRequest: &v1alpha1.PullRequestGenerator{ + Github: &v1alpha1.PullRequestGeneratorGithub{ Owner: "{{ organization }}", Repo: "{{ repository }}", }, @@ -457,19 +512,19 @@ func fakeAppWithMatrixAndScmWithPullRequestGenerator(name, namespace, owner stri } } -func fakeAppWithMatrixAndNestedGitGenerator(name, namespace, repo string) *argoprojiov1alpha1.ApplicationSet { - return &argoprojiov1alpha1.ApplicationSet{ +func fakeAppWithMatrixAndNestedGitGenerator(name, namespace, repo string) *v1alpha1.ApplicationSet { + return &v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: argoprojiov1alpha1.ApplicationSetSpec{ - Generators: []argoprojiov1alpha1.ApplicationSetGenerator{ + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ { - Matrix: &argoprojiov1alpha1.MatrixGenerator{ - Generators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{ + Matrix: &v1alpha1.MatrixGenerator{ + Generators: []v1alpha1.ApplicationSetNestedGenerator{ { - List: &argoprojiov1alpha1.ListGenerator{}, + List: &v1alpha1.ListGenerator{}, }, { Matrix: &apiextensionsv1.JSON{ @@ -501,8 +556,8 @@ func fakeAppWithMatrixAndNestedGitGenerator(name, namespace, repo string) *argop } } -func fakeAppWithMergeAndGitGenerator(name, namespace, repo string) *argoprojiov1alpha1.ApplicationSet { - return &argoprojiov1alpha1.ApplicationSet{ +func fakeAppWithMergeAndGitGenerator(name, namespace, repo string) *v1alpha1.ApplicationSet { + return &v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, @@ -552,22 +607,22 @@ func fakeAppWithMergeAndPullRequestGenerator(name, namespace, owner, repo string } } -func fakeAppWithMergeAndNestedGitGenerator(name, namespace, repo string) *argoprojiov1alpha1.ApplicationSet { - return &argoprojiov1alpha1.ApplicationSet{ +func fakeAppWithMergeAndNestedGitGenerator(name, namespace, repo string) *v1alpha1.ApplicationSet { + return &v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: argoprojiov1alpha1.ApplicationSetSpec{ - Generators: []argoprojiov1alpha1.ApplicationSetGenerator{ + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ { - Merge: &argoprojiov1alpha1.MergeGenerator{ + Merge: &v1alpha1.MergeGenerator{ MergeKeys: []string{ "server", }, - Generators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{ + Generators: []v1alpha1.ApplicationSetNestedGenerator{ { - List: &argoprojiov1alpha1.ListGenerator{}, + List: &v1alpha1.ListGenerator{}, }, { Merge: &apiextensionsv1.JSON{ @@ -594,12 +649,66 @@ func fakeAppWithMergeAndNestedGitGenerator(name, namespace, repo string) *argopr } } +func fakeAppWithPluginGenerator(name, namespace string) *v1alpha1.ApplicationSet { + return &v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ + { + Plugin: &v1alpha1.PluginGenerator{ + ConfigMapRef: v1alpha1.PluginConfigMapRef{ + Name: "test", + }, + }, + }, + }, + }, + } +} + +func fakeAppWithMatrixAndPullRequestGeneratorWithPluginGenerator(name, namespace, owner, repo, configmapName string) *v1alpha1.ApplicationSet { + return &v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ + { + Matrix: &v1alpha1.MatrixGenerator{ + Generators: []v1alpha1.ApplicationSetNestedGenerator{ + { + PullRequest: &v1alpha1.PullRequestGenerator{ + Github: &v1alpha1.PullRequestGeneratorGithub{ + Owner: owner, + Repo: repo, + }, + }, + }, + { + Plugin: &v1alpha1.PluginGenerator{ + ConfigMapRef: v1alpha1.PluginConfigMapRef{ + Name: configmapName, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + func newFakeClient(ns string) *kubefake.Clientset { s := runtime.NewScheme() s.AddKnownTypes(v1alpha1.SchemeGroupVersion, &v1alpha1.ApplicationSet{}) return kubefake.NewSimpleClientset(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "argocd-cm", Namespace: ns, Labels: map[string]string{ "app.kubernetes.io/part-of": "argocd", - }}}, &v1.Secret{ + }}}, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: common.ArgoCDSecretName, Namespace: ns, diff --git a/assets/badge.svg b/assets/badge.svg index cc216ccdd1508..f1dab6b6cb711 100644 --- a/assets/badge.svg +++ b/assets/badge.svg @@ -5,6 +5,7 @@ + @@ -14,6 +15,7 @@ + diff --git a/assets/embed.go b/assets/embed.go index 8095b0f282df3..ac148cafd3de6 100644 --- a/assets/embed.go +++ b/assets/embed.go @@ -3,5 +3,6 @@ package assets import "embed" // Embedded contains embedded assets +// //go:embed * var Embedded embed.FS diff --git a/assets/swagger.json b/assets/swagger.json index bd38517972ded..31d771c52f398 100644 --- a/assets/swagger.json +++ b/assets/swagger.json @@ -234,7 +234,7 @@ }, { "type": "string", - "description": "forces application reconciliation if set to true.", + "description": "forces application reconciliation if set to 'hard'.", "name": "refresh", "in": "query" }, @@ -271,6 +271,16 @@ "description": "the application's namespace.", "name": "appNamespace", "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "the project names to restrict returned list applications (legacy name for backwards-compatibility).", + "name": "project", + "in": "query" } ], "responses": { @@ -391,6 +401,11 @@ "type": "boolean", "name": "validate", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -452,6 +467,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -513,6 +533,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -548,7 +573,7 @@ }, { "type": "string", - "description": "forces application reconciliation if set to true.", + "description": "forces application reconciliation if set to 'hard'.", "name": "refresh", "in": "query" }, @@ -585,6 +610,16 @@ "description": "the application's namespace.", "name": "appNamespace", "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "the project names to restrict returned list applications (legacy name for backwards-compatibility).", + "name": "project", + "in": "query" } ], "responses": { @@ -629,6 +664,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -717,6 +757,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -753,6 +798,11 @@ "type": "string", "name": "namespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -865,6 +915,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -915,6 +970,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -951,6 +1011,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -1064,6 +1129,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -1134,6 +1204,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -1206,6 +1281,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -1275,6 +1355,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -1336,6 +1421,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -1403,6 +1493,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -1464,6 +1559,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -1482,6 +1582,56 @@ } } }, + "/api/v1/applications/{name}/revisions/{revision}/chartdetails": { + "get": { + "tags": [ + "ApplicationService" + ], + "summary": "Get the chart metadata (description, maintainers, home) for a specific revision of the application", + "operationId": "ApplicationService_RevisionChartDetails", + "parameters": [ + { + "type": "string", + "description": "the application's name", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "the revision of the app", + "name": "revision", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "the application's namespace.", + "name": "appNamespace", + "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1alpha1ChartDetails" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + } + } + }, "/api/v1/applications/{name}/revisions/{revision}/metadata": { "get": { "tags": [ @@ -1509,6 +1659,11 @@ "description": "the application's namespace.", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -1597,6 +1752,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -1672,6 +1832,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -1713,6 +1878,12 @@ "description": "the selector to restrict returned list to applications only with matched labels.", "name": "selector", "in": "query" + }, + { + "type": "string", + "description": "The application set namespace. Default empty is argocd control plane namespace.", + "name": "appsetNamespace", + "in": "query" } ], "responses": { @@ -1781,6 +1952,12 @@ "name": "name", "in": "path", "required": true + }, + { + "type": "string", + "description": "The application set namespace. Default empty is argocd control plane namespace.", + "name": "appsetNamespace", + "in": "query" } ], "responses": { @@ -1810,6 +1987,12 @@ "name": "name", "in": "path", "required": true + }, + { + "type": "string", + "description": "The application set namespace. Default empty is argocd control plane namespace.", + "name": "appsetNamespace", + "in": "query" } ], "responses": { @@ -3430,6 +3613,12 @@ "description": "Google Cloud Platform service account key.", "name": "gcpServiceAccountKey", "in": "query" + }, + { + "type": "boolean", + "description": "Whether to force HTTP basic auth.", + "name": "forceHttpBasicAuth", + "in": "query" } ], "responses": { @@ -3588,6 +3777,29 @@ } } }, + "/api/v1/settings/plugins": { + "get": { + "tags": [ + "SettingsService" + ], + "summary": "Get returns Argo CD plugins", + "operationId": "SettingsService_GetPlugins", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/clusterSettingsPluginsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + } + } + }, "/api/v1/stream/applications": { "get": { "tags": [ @@ -3604,7 +3816,7 @@ }, { "type": "string", - "description": "forces application reconciliation if set to true.", + "description": "forces application reconciliation if set to 'hard'.", "name": "refresh", "in": "query" }, @@ -3641,6 +3853,16 @@ "description": "the application's namespace.", "name": "appNamespace", "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "the project names to restrict returned list applications (legacy name for backwards-compatibility).", + "name": "project", + "in": "query" } ], "responses": { @@ -3711,6 +3933,11 @@ "type": "string", "name": "appNamespace", "in": "query" + }, + { + "type": "string", + "name": "project", + "in": "query" } ], "responses": { @@ -3809,7 +4036,7 @@ "type": "object", "properties": { "expiresIn": { - "type": "string", + "type": "integer", "format": "int64", "title": "expiresIn represents a duration in seconds" }, @@ -3836,14 +4063,14 @@ "type": "object", "properties": { "expiresAt": { - "type": "string", + "type": "integer", "format": "int64" }, "id": { "type": "string" }, "issuedAt": { - "type": "string", + "type": "integer", "format": "int64" } } @@ -3876,6 +4103,9 @@ }, "name": { "type": "string" + }, + "project": { + "type": "string" } } }, @@ -3905,6 +4135,9 @@ }, "patchType": { "type": "string" + }, + "project": { + "type": "string" } } }, @@ -3929,12 +4162,15 @@ "type": "boolean" }, "id": { - "type": "string", + "type": "integer", "format": "int64" }, "name": { "type": "string" }, + "project": { + "type": "string" + }, "prune": { "type": "boolean" } @@ -3965,6 +4201,9 @@ "name": { "type": "string" }, + "project": { + "type": "string" + }, "prune": { "type": "boolean" }, @@ -4223,6 +4462,9 @@ "clientID": { "type": "string" }, + "enablePKCEAuthentication": { + "type": "boolean" + }, "idTokenClaims": { "type": "object", "additionalProperties": { @@ -4263,6 +4505,7 @@ "type": "boolean" }, "configManagementPlugins": { + "description": "Deprecated: use sidecar plugins instead.", "type": "array", "items": { "$ref": "#/definitions/v1alpha1ConfigManagementPlugin" @@ -4342,6 +4585,17 @@ } } }, + "clusterSettingsPluginsResponse": { + "type": "object", + "properties": { + "plugins": { + "type": "array", + "items": { + "$ref": "#/definitions/clusterPlugin" + } + } + } + }, "gpgkeyGnuPGPublicKeyCreateResponse": { "type": "object", "title": "Response to a public key creation request", @@ -4375,7 +4629,7 @@ "type": "string" }, "type": { - "type": "string", + "type": "integer", "format": "int64" } } @@ -4514,7 +4768,7 @@ "type": "string" }, "expiresIn": { - "type": "string", + "type": "integer", "format": "int64", "title": "expiresIn represents a duration in seconds" }, @@ -4837,6 +5091,17 @@ } } }, + "runtimeRawExtension": { + "description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned\nstruct, and Object in your internal struct. You also need to register your\nvarious plugin types.\n\n// Internal package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.Object `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// External package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// On the wire, the JSON will look something like this:\n\n\t{\n\t\t\"kind\":\"MyAPIObject\",\n\t\t\"apiVersion\":\"v1\",\n\t\t\"myPlugin\": {\n\t\t\t\"kind\":\"PluginA\",\n\t\t\t\"aOption\":\"foo\",\n\t\t},\n\t}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into\nyour external MyAPIObject. That causes the raw JSON to be stored, but not unpacked.\nThe next step is to copy (using pkg/conversion) into the internal struct. The runtime\npackage's DefaultScheme has conversion functions installed which will unpack the\nJSON stored in RawExtension, turning it into the correct object type, and storing it\nin the Object. (TODO: In the case where the object is of an unknown type, a\nruntime.Unknown object will be created and stored.)\n\n+k8s:deepcopy-gen=true\n+protobuf=true\n+k8s:openapi-gen=true", + "type": "object", + "properties": { + "raw": { + "description": "Raw is the underlying serialization of this object.\n\nTODO: Determine how to detect ContentType and ContentEncoding of 'Raw' data.", + "type": "string", + "format": "byte" + } + } + }, "runtimeStreamError": { "type": "object", "properties": { @@ -5094,7 +5359,7 @@ "type": "string" }, "remainingItemCount": { - "type": "string", + "type": "integer", "format": "int64", "title": "remainingItemCount is the number of subsequent items in the list which are not included in this\nlist response. If the list request contained label or field selectors, then the number of\nremaining items is unknown and the field will be left unset and omitted during serialization.\nIf the list is complete (either because it is not chunking or because this is the last chunk),\nthen there are no more remaining items and this field will be left unset and omitted during\nserialization.\nServers older than v1.15 do not set this field.\nThe intended use of the remainingItemCount is *estimating* the size of a collection. Clients\nshould not rely on the remainingItemCount to be set or to be exact.\n+optional" }, @@ -5172,7 +5437,7 @@ }, "seconds": { "description": "Represents seconds of UTC time since Unix epoch\n1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to\n9999-12-31T23:59:59Z inclusive.", - "type": "string", + "type": "integer", "format": "int64" } } @@ -5234,15 +5499,11 @@ "type": "string" } }, - "clusterName": { - "description": "Deprecated: ClusterName is a legacy field that was always cleared by\nthe system and never used; it will be removed completely in 1.25.\n\nThe name in the go struct is changed to help clients detect\naccidental use.\n\n+optional", - "type": "string" - }, "creationTimestamp": { "$ref": "#/definitions/v1Time" }, "deletionGracePeriodSeconds": { - "type": "string", + "type": "integer", "format": "int64", "title": "Number of seconds allowed for this object to gracefully terminate before\nit will be removed from the system. Only set when deletionTimestamp is also set.\nMay only be shortened.\nRead-only.\n+optional" }, @@ -5261,7 +5522,7 @@ "type": "string" }, "generation": { - "type": "string", + "type": "integer", "format": "int64", "title": "A sequence number representing a specific generation of the desired state.\nPopulated by the system. Read-only.\n+optional" }, @@ -5309,8 +5570,8 @@ } }, "v1ObjectReference": { + "description": "ObjectReference contains enough information to let you inspect or modify the referred object.\n---\nNew uses of this type are discouraged because of difficulty describing its usage when embedded in APIs.\n 1. Ignored fields. It includes many fields which are not generally honored. For instance, ResourceVersion and FieldPath are both very rarely valid in actual usage.\n 2. Invalid usage help. It is impossible to add specific help for individual usage. In most embedded usages, there are particular\n restrictions like, \"must refer only to types A and B\" or \"UID not honored\" or \"name must be restricted\".\n Those cannot be well described when embedded.\n 3. Inconsistent validation. Because the usages are different, the validation rules are different by usage, which makes it hard for users to predict what will happen.\n 4. The fields are both imprecise and overly precise. Kind is not a precise mapping to a URL. This can produce ambiguity\n during interpretation and require a REST mapping. In most cases, the dependency is on the group,resource tuple\n and the version of the actual struct is irrelevant.\n 5. We cannot easily change it. Because this type is embedded in many locations, updates to this type\n will affect numerous schemas. Don't make new APIs embed an underspecified API type they do not control.\n\nInstead of using this type, create a locally provided and used type that is well-focused on your reference.\nFor example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 .\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+structType=atomic", "type": "object", - "title": "ObjectReference contains enough information to let you inspect or modify the referred object.\n---\nNew uses of this type are discouraged because of difficulty describing its usage when embedded in APIs.\n 1. Ignored fields. It includes many fields which are not generally honored. For instance, ResourceVersion and FieldPath are both very rarely valid in actual usage.\n 2. Invalid usage help. It is impossible to add specific help for individual usage. In most embedded usages, there are particular\n restrictions like, \"must refer only to types A and B\" or \"UID not honored\" or \"name must be restricted\".\n Those cannot be well described when embedded.\n 3. Inconsistent validation. Because the usages are different, the validation rules are different by usage, which makes it hard for users to predict what will happen.\n 4. The fields are both imprecise and overly precise. Kind is not a precise mapping to a URL. This can produce ambiguity\n during interpretation and require a REST mapping. In most cases, the dependency is on the group,resource tuple\n and the version of the actual struct is irrelevant.\n 5. We cannot easily change it. Because this type is embedded in many locations, updates to this type\n will affect numerous schemas. Don't make new APIs embed an underspecified API type they do not control.\nInstead of using this type, create a locally provided and used type that is well-focused on your reference.\nFor example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 .\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+structType=atomic", "properties": { "apiVersion": { "type": "string", @@ -5392,19 +5653,8 @@ }, "v1Time": { "description": "Time is a wrapper around time.Time which supports correct\nmarshaling to YAML and JSON. Wrappers are provided for many\nof the factory methods that the time package offers.\n\n+protobuf.options.marshal=false\n+protobuf.as=Timestamp\n+protobuf.options.(gogoproto.goproto_stringer)=false", - "type": "object", - "properties": { - "nanos": { - "description": "Non-negative fractions of a second at nanosecond resolution. Negative\nsecond values with fractions must still have non-negative nanos values\nthat count forward in time. Must be from 0 to 999,999,999\ninclusive. This field may be limited in precision depending on context.", - "type": "integer", - "format": "int32" - }, - "seconds": { - "description": "Represents seconds of UTC time since Unix epoch\n1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to\n9999-12-31T23:59:59Z inclusive.", - "type": "string", - "format": "int64" - } - } + "type": "string", + "format": "date-time" }, "v1alpha1AWSAuthConfig": { "type": "object", @@ -5414,6 +5664,10 @@ "type": "string", "title": "ClusterName contains AWS cluster name" }, + "profile": { + "description": "Profile contains optional role ARN. If set then AWS IAM Authenticator uses the profile to perform cluster operations instead of the default AWS credential provider chain.", + "type": "string" + }, "roleARN": { "description": "RoleARN contains optional role ARN. If set then AWS IAM Authenticator assume a role to perform cluster operations instead of the default AWS credential provider chain.", "type": "string" @@ -5570,7 +5824,7 @@ }, "v1alpha1ApplicationCondition": { "type": "object", - "title": "ApplicationCondition contains details about an application condition, which is usally an error or warning", + "title": "ApplicationCondition contains details about an application condition, which is usually an error or warning", "properties": { "lastTransitionTime": { "$ref": "#/definitions/v1Time" @@ -5590,16 +5844,16 @@ "title": "ApplicationDestination holds information about the application's destination", "properties": { "name": { - "type": "string", - "title": "Name is an alternate way of specifying the target cluster by its symbolic name" + "description": "Name is an alternate way of specifying the target cluster by its symbolic name. This must be set if Server is not set.", + "type": "string" }, "namespace": { "type": "string", "title": "Namespace specifies the target namespace for the application's resources.\nThe namespace will only be set for namespace-scoped resources that have not set a value for .metadata.namespace" }, "server": { - "type": "string", - "title": "Server specifies the URL of the target cluster and must be set to the Kubernetes control plane API" + "description": "Server specifies the URL of the target cluster's Kubernetes control plane API. This must be set if Name is not set.", + "type": "string" } } }, @@ -5635,6 +5889,23 @@ } } }, + "v1alpha1ApplicationPreservedFields": { + "type": "object", + "properties": { + "annotations": { + "type": "array", + "items": { + "type": "string" + } + }, + "labels": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "v1alpha1ApplicationSet": { "type": "object", "title": "ApplicationSet is a set of Application resources\n+genclient\n+genclient:noStatus\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+kubebuilder:resource:path=applicationsets,shortName=appset;appsets\n+kubebuilder:subresource:status", @@ -5668,6 +5939,10 @@ "status": { "type": "string", "title": "Status contains the AppSet's perceived status of the managed Application resource: (Waiting, Pending, Progressing, Healthy)" + }, + "step": { + "type": "string", + "title": "Step tracks which step this Application should be updated in" } } }, @@ -5718,6 +5993,9 @@ "merge": { "$ref": "#/definitions/v1alpha1MergeGenerator" }, + "plugin": { + "$ref": "#/definitions/v1alpha1PluginGenerator" + }, "pullRequest": { "$ref": "#/definitions/v1alpha1PullRequestGenerator" }, @@ -5766,6 +6044,9 @@ "merge": { "$ref": "#/definitions/v1JSON" }, + "plugin": { + "$ref": "#/definitions/v1alpha1PluginGenerator" + }, "pullRequest": { "$ref": "#/definitions/v1alpha1PullRequestGenerator" }, @@ -5777,6 +6058,30 @@ } } }, + "v1alpha1ApplicationSetResourceIgnoreDifferences": { + "description": "ApplicationSetResourceIgnoreDifferences configures how the ApplicationSet controller will ignore differences in live\napplications when applying changes from generated applications.", + "type": "object", + "properties": { + "jqPathExpressions": { + "description": "JQPathExpressions is a list of JQ path expressions to fields to ignore differences for.", + "type": "array", + "items": { + "type": "string" + } + }, + "jsonPointers": { + "description": "JSONPointers is a list of JSON pointers to fields to ignore differences for.", + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "description": "Name is the name of the application to ignore differences for. If not specified, the rule applies to all applications.", + "type": "string" + } + } + }, "v1alpha1ApplicationSetRolloutStep": { "type": "object", "properties": { @@ -5806,6 +6111,10 @@ "description": "ApplicationSetSpec represents a class of application set state.", "type": "object", "properties": { + "applyNestedSelectors": { + "type": "boolean", + "title": "ApplyNestedSelectors enables selectors defined within the generators of two level-nested matrix or merge generators" + }, "generators": { "type": "array", "items": { @@ -5815,6 +6124,21 @@ "goTemplate": { "type": "boolean" }, + "goTemplateOptions": { + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreApplicationDifferences": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha1ApplicationSetResourceIgnoreDifferences" + } + }, + "preservedFields": { + "$ref": "#/definitions/v1alpha1ApplicationPreservedFields" + }, "strategy": { "$ref": "#/definitions/v1alpha1ApplicationSetStrategy" }, @@ -5823,6 +6147,9 @@ }, "template": { "$ref": "#/definitions/v1alpha1ApplicationSetTemplate" + }, + "templatePatch": { + "type": "string" } } }, @@ -5861,6 +6188,10 @@ "description": "ApplicationSetSyncPolicy configures how generated Applications will relate to their\nApplicationSet.", "type": "object", "properties": { + "applicationsSync": { + "type": "string", + "title": "ApplicationsSync represents the policy applied on the generated applications. Possible values are create-only, create-update, create-delete, sync\n+kubebuilder:validation:Optional\n+kubebuilder:validation:Enum=create-only;create-update;create-delete;sync" + }, "preserveResourcesOnDeletion": { "description": "PreserveResourcesOnDeletion will preserve resources on deletion. If PreserveResourcesOnDeletion is set to true, these Applications will not be deleted.", "type": "boolean" @@ -6011,7 +6342,10 @@ }, "values": { "type": "string", - "title": "Values specifies Helm values to be passed to helm template, typically defined as a block" + "title": "Values specifies Helm values to be passed to helm template, typically defined as a block. ValuesObject takes precedence over Values, so use one or the other.\n+patchStrategy=replace" + }, + "valuesObject": { + "$ref": "#/definitions/runtimeRawExtension" }, "version": { "type": "string", @@ -6057,6 +6391,10 @@ "type": "string" } }, + "commonAnnotationsEnvsubst": { + "type": "boolean", + "title": "CommonAnnotationsEnvsubst specifies whether to apply env variables substitution for annotation values" + }, "commonLabels": { "type": "object", "title": "CommonLabels is a list of additional labels to add to rendered manifests", @@ -6064,6 +6402,13 @@ "type": "string" } }, + "components": { + "type": "array", + "title": "Components specifies a list of kustomize components to add to the kustomization before building", + "items": { + "type": "string" + } + }, "forceCommonAnnotations": { "type": "boolean", "title": "ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize apps" @@ -6079,6 +6424,10 @@ "type": "string" } }, + "labelWithoutSelector": { + "type": "boolean", + "title": "LabelWithoutSelector specifies whether to apply common labels to resource selectors or not" + }, "namePrefix": { "type": "string", "title": "NamePrefix is a prefix appended to resources for Kustomize apps" @@ -6087,6 +6436,24 @@ "type": "string", "title": "NameSuffix is a suffix appended to resources for Kustomize apps" }, + "namespace": { + "type": "string", + "title": "Namespace sets the namespace that Kustomize adds to all resources" + }, + "patches": { + "type": "array", + "title": "Patches is a list of Kustomize patches", + "items": { + "$ref": "#/definitions/v1alpha1KustomizePatch" + } + }, + "replicas": { + "type": "array", + "title": "Replicas is a list of Kustomize Replicas override specifications", + "items": { + "$ref": "#/definitions/v1alpha1KustomizeReplica" + } + }, "version": { "type": "string", "title": "Version controls which version of Kustomize to use for rendering manifests" @@ -6168,7 +6535,7 @@ }, "revisionHistoryLimit": { "description": "RevisionHistoryLimit limits the number of items kept in the application's revision history, which is used for informational purposes as well as for rollbacks to previous versions.\nThis should only be changed in exceptional circumstances.\nSetting to zero will store no history. This will reduce storage used.\nIncreasing will increase the space used to store the history, so we do not recommend increasing it.\nDefault is 10.", - "type": "string", + "type": "integer", "format": "int64" }, "source": { @@ -6197,6 +6564,10 @@ "$ref": "#/definitions/v1alpha1ApplicationCondition" } }, + "controllerNamespace": { + "type": "string", + "title": "ControllerNamespace indicates the namespace in which the application controller is located" + }, "health": { "$ref": "#/definitions/v1alpha1HealthStatus" }, @@ -6314,7 +6685,7 @@ "title": "Duration is the amount to back off. Default unit is seconds, but could also be a duration (e.g. \"2m\", \"1h\")" }, "factor": { - "type": "string", + "type": "integer", "format": "int64", "title": "Factor is a factor to multiply the base duration after each failed retry" }, @@ -6337,6 +6708,35 @@ } } }, + "v1alpha1BearerTokenBitbucketCloud": { + "description": "BearerTokenBitbucketCloud defines the Bearer token for BitBucket AppToken auth.", + "type": "object", + "properties": { + "tokenRef": { + "$ref": "#/definitions/v1alpha1SecretRef" + } + } + }, + "v1alpha1ChartDetails": { + "type": "object", + "title": "ChartDetails contains helm chart metadata for a specific version", + "properties": { + "description": { + "type": "string" + }, + "home": { + "type": "string", + "title": "The URL of this projects home page, e.g. \"http://example.com\"" + }, + "maintainers": { + "type": "array", + "title": "List of maintainer details, name and email, e.g. [\"John Doe \"]", + "items": { + "type": "string" + } + } + } + }, "v1alpha1Cluster": { "type": "object", "title": "Cluster is the definition of a cluster resource", @@ -6396,7 +6796,7 @@ }, "shard": { "description": "Shard contains optional shard number. Calculated on the fly by the application controller if not specified.", - "type": "string", + "type": "integer", "format": "int64" } } @@ -6406,7 +6806,7 @@ "title": "ClusterCacheInfo contains information about the cluster cache", "properties": { "apisCount": { - "type": "string", + "type": "integer", "format": "int64", "title": "APIsCount holds number of observed Kubernetes API count" }, @@ -6414,7 +6814,7 @@ "$ref": "#/definitions/v1Time" }, "resourcesCount": { - "type": "string", + "type": "integer", "format": "int64", "title": "ResourcesCount holds number of observed Kubernetes resources" } @@ -6477,7 +6877,7 @@ } }, "applicationsCount": { - "type": "string", + "type": "integer", "format": "int64", "title": "ApplicationsCount is the number of applications managed by Argo CD on the cluster" }, @@ -6533,6 +6933,13 @@ "destination": { "$ref": "#/definitions/v1alpha1ApplicationDestination" }, + "ignoreDifferences": { + "type": "array", + "title": "IgnoreDifferences is a reference to the application's ignored differences used for comparison", + "items": { + "$ref": "#/definitions/v1alpha1ResourceIgnoreDifferences" + } + }, "source": { "$ref": "#/definitions/v1alpha1ApplicationSource" }, @@ -6595,7 +7002,7 @@ "type": "string" }, "requeueAfterSeconds": { - "type": "string", + "type": "integer", "format": "int64" }, "template": { @@ -6683,7 +7090,7 @@ "type": "string" }, "requeueAfterSeconds": { - "type": "string", + "type": "integer", "format": "int64" }, "revision": { @@ -6691,6 +7098,13 @@ }, "template": { "$ref": "#/definitions/v1alpha1ApplicationSetTemplate" + }, + "values": { + "type": "object", + "title": "Values contains key/value pairs which are passed directly as parameters to the template", + "additionalProperties": { + "type": "string" + } } } }, @@ -6808,15 +7222,15 @@ "title": "TODO: describe this type", "properties": { "capacity": { - "type": "string", + "type": "integer", "format": "int64" }, "requestedByApp": { - "type": "string", + "type": "integer", "format": "int64" }, "requestedByNeighbors": { - "type": "string", + "type": "integer", "format": "int64" }, "resourceName": { @@ -6854,11 +7268,11 @@ "title": "JWTToken holds the issuedAt and expiresAt values of a token", "properties": { "exp": { - "type": "string", + "type": "integer", "format": "int64" }, "iat": { - "type": "string", + "type": "integer", "format": "int64" }, "id": { @@ -6905,6 +7319,20 @@ } } }, + "v1alpha1KustomizeGvk": { + "type": "object", + "properties": { + "group": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, "v1alpha1KustomizeOptions": { "type": "object", "title": "KustomizeOptions are options for kustomize to use when building manifests", @@ -6919,16 +7347,80 @@ } } }, + "v1alpha1KustomizePatch": { + "type": "object", + "properties": { + "options": { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, + "patch": { + "type": "string" + }, + "path": { + "type": "string" + }, + "target": { + "$ref": "#/definitions/v1alpha1KustomizeSelector" + } + } + }, + "v1alpha1KustomizeReplica": { + "type": "object", + "properties": { + "count": { + "$ref": "#/definitions/intstrIntOrString" + }, + "name": { + "type": "string", + "title": "Name of Deployment or StatefulSet" + } + } + }, + "v1alpha1KustomizeResId": { + "type": "object", + "properties": { + "gvk": { + "$ref": "#/definitions/v1alpha1KustomizeGvk" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + } + } + }, + "v1alpha1KustomizeSelector": { + "type": "object", + "properties": { + "annotationSelector": { + "type": "string" + }, + "labelSelector": { + "type": "string" + }, + "resId": { + "$ref": "#/definitions/v1alpha1KustomizeResId" + } + } + }, "v1alpha1ListGenerator": { "type": "object", "title": "ListGenerator include items info", "properties": { "elements": { "type": "array", + "title": "+kubebuilder:validation:Optional", "items": { "$ref": "#/definitions/v1JSON" } }, + "elementsYaml": { + "type": "string" + }, "template": { "$ref": "#/definitions/v1alpha1ApplicationSetTemplate" } @@ -7042,7 +7534,7 @@ "title": "Phase is the current phase of the operation" }, "retryCount": { - "type": "string", + "type": "integer", "format": "int64", "title": "RetryCount contains time of operation retries" }, @@ -7113,6 +7605,54 @@ } } }, + "v1alpha1PluginConfigMapRef": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name of the ConfigMap" + } + } + }, + "v1alpha1PluginGenerator": { + "description": "PluginGenerator defines connection info specific to Plugin.", + "type": "object", + "properties": { + "configMapRef": { + "$ref": "#/definitions/v1alpha1PluginConfigMapRef" + }, + "input": { + "$ref": "#/definitions/v1alpha1PluginInput" + }, + "requeueAfterSeconds": { + "description": "RequeueAfterSeconds determines how long the ApplicationSet controller will wait before reconciling the ApplicationSet again.", + "type": "integer", + "format": "int64" + }, + "template": { + "$ref": "#/definitions/v1alpha1ApplicationSetTemplate" + }, + "values": { + "description": "Values contains key/value pairs which are passed directly as parameters to the template. These values will not be\nsent as parameters to the plugin.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "v1alpha1PluginInput": { + "type": "object", + "properties": { + "parameters": { + "description": "Parameters contains the information to pass to the plugin. It is a map. The keys must be strings, and the\nvalues can be any type.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1JSON" + } + } + } + }, "v1alpha1ProjectRole": { "type": "object", "title": "ProjectRole represents a role that has access to a project", @@ -7152,6 +7692,12 @@ "description": "PullRequestGenerator defines a generator that scrapes a PullRequest API to find candidate pull requests.", "type": "object", "properties": { + "azuredevops": { + "$ref": "#/definitions/v1alpha1PullRequestGeneratorAzureDevOps" + }, + "bitbucket": { + "$ref": "#/definitions/v1alpha1PullRequestGeneratorBitbucket" + }, "bitbucketServer": { "$ref": "#/definitions/v1alpha1PullRequestGeneratorBitbucketServer" }, @@ -7173,7 +7719,7 @@ }, "requeueAfterSeconds": { "description": "Standard parameters.", - "type": "string", + "type": "integer", "format": "int64" }, "template": { @@ -7181,8 +7727,64 @@ } } }, + "v1alpha1PullRequestGeneratorAzureDevOps": { + "description": "PullRequestGeneratorAzureDevOps defines connection info specific to AzureDevOps.", + "type": "object", + "properties": { + "api": { + "description": "The Azure DevOps API URL to talk to. If blank, use https://dev.azure.com/.", + "type": "string" + }, + "labels": { + "type": "array", + "title": "Labels is used to filter the PRs that you want to target", + "items": { + "type": "string" + } + }, + "organization": { + "description": "Azure DevOps org to scan. Required.", + "type": "string" + }, + "project": { + "description": "Azure DevOps project name to scan. Required.", + "type": "string" + }, + "repo": { + "description": "Azure DevOps repo name to scan. Required.", + "type": "string" + }, + "tokenRef": { + "$ref": "#/definitions/v1alpha1SecretRef" + } + } + }, + "v1alpha1PullRequestGeneratorBitbucket": { + "description": "PullRequestGeneratorBitbucket defines connection info specific to Bitbucket.", + "type": "object", + "properties": { + "api": { + "description": "The Bitbucket REST API URL to talk to. If blank, uses https://api.bitbucket.org/2.0.", + "type": "string" + }, + "basicAuth": { + "$ref": "#/definitions/v1alpha1BasicAuthBitbucketServer" + }, + "bearerToken": { + "$ref": "#/definitions/v1alpha1BearerTokenBitbucketCloud" + }, + "owner": { + "description": "Workspace to scan. Required.", + "type": "string" + }, + "repo": { + "description": "Repo name to scan. Required.", + "type": "string" + } + } + }, "v1alpha1PullRequestGeneratorBitbucketServer": { - "description": "PullRequestGenerator defines connection info specific to BitbucketServer.", + "description": "PullRequestGeneratorBitbucketServer defines connection info specific to BitbucketServer.", "type": "object", "properties": { "api": { @@ -7208,6 +7810,9 @@ "properties": { "branchMatch": { "type": "string" + }, + "targetBranchMatch": { + "type": "string" } } }, @@ -7219,6 +7824,10 @@ "description": "The GitLab API URL to talk to. If blank, uses https://gitlab.com/.", "type": "string" }, + "insecure": { + "type": "boolean", + "title": "Skips validating the SCM provider's TLS certificate - useful for self-signed certificates.; default: false" + }, "labels": { "type": "array", "title": "Labels is used to filter the MRs that you want to target", @@ -7240,7 +7849,7 @@ } }, "v1alpha1PullRequestGeneratorGitea": { - "description": "PullRequestGenerator defines connection info specific to Gitea.", + "description": "PullRequestGeneratorGitea defines connection info specific to Gitea.", "type": "object", "properties": { "api": { @@ -7304,6 +7913,10 @@ "type": "boolean", "title": "EnableOCI specifies whether helm-oci support should be enabled for this repo" }, + "forceHttpBasicAuth": { + "type": "boolean", + "title": "ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections" + }, "gcpServiceAccountKey": { "type": "string", "title": "GCPServiceAccountKey specifies the service account key in JSON format to be used for getting credentials to Google Cloud Source repos" @@ -7313,12 +7926,12 @@ "title": "GithubAppEnterpriseBaseURL specifies the GitHub API URL for GitHub app authentication. If empty will default to https://api.github.com" }, "githubAppID": { - "type": "string", + "type": "integer", "format": "int64", "title": "GithubAppId specifies the Github App ID of the app used to access the repo for GitHub app authentication" }, "githubAppInstallationID": { - "type": "string", + "type": "integer", "format": "int64", "title": "GithubAppInstallationId specifies the ID of the installed GitHub App for GitHub app authentication" }, @@ -7390,6 +8003,10 @@ "type": "boolean", "title": "EnableOCI specifies whether helm-oci support should be enabled for this repo" }, + "forceHttpBasicAuth": { + "type": "boolean", + "title": "ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections" + }, "gcpServiceAccountKey": { "type": "string", "title": "GCPServiceAccountKey specifies the service account key in JSON format to be used for getting credentials to Google Cloud Source repos" @@ -7399,12 +8016,12 @@ "title": "GithubAppEnterpriseBaseURL specifies the base URL of GitHub Enterprise installation. If empty will default to https://api.github.com" }, "githubAppID": { - "type": "string", + "type": "integer", "format": "int64", "title": "GithubAppId specifies the ID of the GitHub app used to access the repo" }, "githubAppInstallationID": { - "type": "string", + "type": "integer", "format": "int64", "title": "GithubAppInstallationId specifies the installation ID of the GitHub App used to access the repo" }, @@ -7531,6 +8148,12 @@ "disabled": { "type": "boolean" }, + "displayName": { + "type": "string" + }, + "iconClass": { + "type": "string" + }, "name": { "type": "string" }, @@ -7712,13 +8335,15 @@ "$ref": "#/definitions/v1alpha1ResourceRef" } }, - "resourceRef": { - "$ref": "#/definitions/v1alpha1ResourceRef" - }, "resourceVersion": { "type": "string" } - } + }, + "allOf": [ + { + "$ref": "#/definitions/v1alpha1ResourceRef" + } + ] }, "v1alpha1ResourceOverride": { "type": "object", @@ -7733,6 +8358,9 @@ "ignoreDifferences": { "$ref": "#/definitions/v1alpha1OverrideIgnoreDiff" }, + "ignoreResourceUpdates": { + "$ref": "#/definitions/v1alpha1OverrideIgnoreDiff" + }, "knownTypeFields": { "type": "array", "items": { @@ -7843,7 +8471,7 @@ "type": "string" }, "syncWave": { - "type": "string", + "type": "integer", "format": "int64" }, "version": { @@ -7860,7 +8488,7 @@ }, "limit": { "description": "Limit is the maximum number of attempts for retrying a failed sync. If set to 0, no retries will be performed.", - "type": "string", + "type": "integer", "format": "int64" } } @@ -7876,10 +8504,13 @@ "$ref": "#/definitions/v1Time" }, "id": { - "type": "string", + "type": "integer", "format": "int64", "title": "ID is an auto incrementing identifier of the RevisionHistory" }, + "initiatedBy": { + "$ref": "#/definitions/v1alpha1OperationInitiator" + }, "revision": { "type": "string", "title": "Revision holds the revision the sync was performed against" @@ -7935,6 +8566,9 @@ "description": "SCMProviderGenerator defines a generator that scrapes a SCMaaS API to find candidate repos.", "type": "object", "properties": { + "awsCodeCommit": { + "$ref": "#/definitions/v1alpha1SCMProviderGeneratorAWSCodeCommit" + }, "azureDevOps": { "$ref": "#/definitions/v1alpha1SCMProviderGeneratorAzureDevOps" }, @@ -7966,11 +8600,43 @@ }, "requeueAfterSeconds": { "description": "Standard parameters.", - "type": "string", + "type": "integer", "format": "int64" }, "template": { "$ref": "#/definitions/v1alpha1ApplicationSetTemplate" + }, + "values": { + "type": "object", + "title": "Values contains key/value pairs which are passed directly as parameters to the template", + "additionalProperties": { + "type": "string" + } + } + } + }, + "v1alpha1SCMProviderGeneratorAWSCodeCommit": { + "description": "SCMProviderGeneratorAWSCodeCommit defines connection info specific to AWS CodeCommit.", + "type": "object", + "properties": { + "allBranches": { + "description": "Scan all branches instead of just the default branch.", + "type": "boolean" + }, + "region": { + "description": "Region provides the AWS region to discover repos.\nif not provided, AppSet controller will infer the current region from environment.", + "type": "string" + }, + "role": { + "description": "Role provides the AWS IAM role to assume, for cross-account repo discovery\nif not provided, AppSet controller will use its pod/node identity to discover.", + "type": "string" + }, + "tagFilters": { + "type": "array", + "title": "TagFilters provides the tag filter(s) for repo discovery", + "items": { + "$ref": "#/definitions/v1alpha1TagFilter" + } } } }, @@ -8139,12 +8805,24 @@ "description": "Gitlab group to scan. Required. You can use either the project id (recommended) or the full namespaced path.", "type": "string" }, + "includeSharedProjects": { + "type": "boolean", + "title": "When recursing through subgroups, also include shared Projects (true) or scan only the subgroups under same path (false). Defaults to \"true\"" + }, "includeSubgroups": { "type": "boolean", "title": "Recurse through subgroups (true) or scan only the base group (false). Defaults to \"false\"" }, + "insecure": { + "type": "boolean", + "title": "Skips validating the SCM provider's TLS certificate - useful for self-signed certificates.; default: false" + }, "tokenRef": { "$ref": "#/definitions/v1alpha1SecretRef" + }, + "topic": { + "description": "Filter repos list based on Gitlab Topic.", + "type": "string" } } }, @@ -8251,6 +8929,9 @@ "type": "object", "title": "SyncOperationResult represent result of sync operation", "properties": { + "managedNamespaceMetadata": { + "$ref": "#/definitions/v1alpha1ManagedNamespaceMetadata" + }, "resources": { "type": "array", "title": "Resources contains a list of sync result items for each individual resource in a sync operation", @@ -8317,7 +8998,7 @@ }, "selfHeal": { "type": "boolean", - "title": "SelfHeal specifes whether to revert resources back to their desired state upon modification in the cluster (default: false)" + "title": "SelfHeal specifies whether to revert resources back to their desired state upon modification in the cluster (default: false)" } } }, @@ -8452,6 +9133,17 @@ } } }, + "v1alpha1TagFilter": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, "versionVersionMessage": { "type": "object", "title": "VersionMessage represents version of the Argo CD API server", @@ -8462,6 +9154,9 @@ "Compiler": { "type": "string" }, + "ExtraBuildInfo": { + "type": "string" + }, "GitCommit": { "type": "string" }, diff --git a/cmd/argocd-application-controller/commands/argocd_application_controller.go b/cmd/argocd-application-controller/commands/argocd_application_controller.go index 1d94fa64d42d2..3c7fe8bbac107 100644 --- a/cmd/argocd-application-controller/commands/argocd_application_controller.go +++ b/cmd/argocd-application-controller/commands/argocd_application_controller.go @@ -7,7 +7,7 @@ import ( "time" "github.com/argoproj/pkg/stats" - "github.com/go-redis/redis/v8" + "github.com/redis/go-redis/v9" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "k8s.io/client-go/kubernetes" @@ -19,6 +19,7 @@ import ( "github.com/argoproj/argo-cd/v2/controller/sharding" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" + "github.com/argoproj/argo-cd/v2/pkg/ratelimiter" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" cacheutil "github.com/argoproj/argo-cd/v2/util/cache" appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate" @@ -33,7 +34,7 @@ import ( const ( // CLIName is the name of the CLI - cliName = "argocd-application-controller" + cliName = common.ApplicationController // Default time in seconds for application resync period defaultAppResyncPeriod = 180 // Default time in seconds for application hard resync period @@ -42,26 +43,35 @@ const ( func NewCommand() *cobra.Command { var ( - clientConfig clientcmd.ClientConfig - appResyncPeriod int64 - appHardResyncPeriod int64 - repoServerAddress string - repoServerTimeoutSeconds int - selfHealTimeoutSeconds int - statusProcessors int - operationProcessors int - glogLevel int - metricsPort int - metricsCacheExpiration time.Duration - metricsAplicationLabels []string - kubectlParallelismLimit int64 - cacheSrc func() (*appstatecache.Cache, error) - redisClient *redis.Client - repoServerPlaintext bool - repoServerStrictTLS bool - otlpAddress string - applicationNamespaces []string - persistResourceHealth bool + workqueueRateLimit ratelimiter.AppControllerRateLimiterConfig + clientConfig clientcmd.ClientConfig + appResyncPeriod int64 + appHardResyncPeriod int64 + appResyncJitter int64 + repoErrorGracePeriod int64 + repoServerAddress string + repoServerTimeoutSeconds int + selfHealTimeoutSeconds int + statusProcessors int + operationProcessors int + glogLevel int + metricsPort int + metricsCacheExpiration time.Duration + metricsAplicationLabels []string + kubectlParallelismLimit int64 + cacheSource func() (*appstatecache.Cache, error) + redisClient *redis.Client + repoServerPlaintext bool + repoServerStrictTLS bool + otlpAddress string + otlpInsecure bool + otlpHeaders map[string]string + otlpAttrs []string + applicationNamespaces []string + persistResourceHealth bool + shardingAlgorithm string + enableDynamicClusterDistribution bool + serverSideDiff bool ) var command = cobra.Command{ Use: cliName, @@ -89,7 +99,7 @@ func NewCommand() *cobra.Command { config, err := clientConfig.ClientConfig() errors.CheckError(err) errors.CheckError(v1alpha1.SetK8SConfigDefaults(config)) - config.UserAgent = fmt.Sprintf("argocd-application-controller/%s (%s)", vers.Version, vers.Platform) + config.UserAgent = fmt.Sprintf("%s/%s (%s)", common.DefaultApplicationControllerName, vers.Version, vers.Platform) kubeClient := kubernetes.NewForConfigOrDie(config) appClient := appclientset.NewForConfigOrDie(config) @@ -124,7 +134,7 @@ func NewCommand() *cobra.Command { repoClientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds, tlsConfig) - cache, err := cacheSrc() + cache, err := cacheSource() errors.CheckError(err) cache.Cache.SetClient(cacheutil.NewTwoLevelClient(cache.Cache.GetClient(), 10*time.Minute)) @@ -134,7 +144,8 @@ func NewCommand() *cobra.Command { appController.InvalidateProjectsCache() })) kubectl := kubeutil.NewKubectl() - clusterFilter := getClusterFilter() + clusterSharding, err := sharding.GetClusterSharding(kubeClient, settingsMgr, shardingAlgorithm, enableDynamicClusterDistribution) + errors.CheckError(err) appController, err = controller.NewApplicationController( namespace, settingsMgr, @@ -145,14 +156,20 @@ func NewCommand() *cobra.Command { kubectl, resyncDuration, hardResyncDuration, + time.Duration(appResyncJitter)*time.Second, time.Duration(selfHealTimeoutSeconds)*time.Second, + time.Duration(repoErrorGracePeriod)*time.Second, metricsPort, metricsCacheExpiration, metricsAplicationLabels, kubectlParallelismLimit, persistResourceHealth, - clusterFilter, - applicationNamespaces) + clusterSharding, + applicationNamespaces, + &workqueueRateLimit, + serverSideDiff, + enableDynamicClusterDistribution, + ) errors.CheckError(err) cacheutil.CollectMetrics(redisClient, appController.GetMetricsServer()) @@ -161,7 +178,7 @@ func NewCommand() *cobra.Command { stats.RegisterHeapDumper("memprofile") if otlpAddress != "" { - closeTracer, err := trace.InitTracer(ctx, "argocd-controller", otlpAddress) + closeTracer, err := trace.InitTracer(ctx, "argocd-controller", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs) if err != nil { log.Fatalf("failed to initialize tracing: %v", err) } @@ -178,6 +195,8 @@ func NewCommand() *cobra.Command { clientConfig = cli.AddKubectlFlagsToCmd(&command) command.Flags().Int64Var(&appResyncPeriod, "app-resync", int64(env.ParseDurationFromEnv("ARGOCD_RECONCILIATION_TIMEOUT", defaultAppResyncPeriod*time.Second, 0, math.MaxInt64).Seconds()), "Time period in seconds for application resync.") command.Flags().Int64Var(&appHardResyncPeriod, "app-hard-resync", int64(env.ParseDurationFromEnv("ARGOCD_HARD_RECONCILIATION_TIMEOUT", defaultAppHardResyncPeriod*time.Second, 0, math.MaxInt64).Seconds()), "Time period in seconds for application hard resync.") + command.Flags().Int64Var(&appResyncJitter, "app-resync-jitter", int64(env.ParseDurationFromEnv("ARGOCD_RECONCILIATION_JITTER", 0*time.Second, 0, math.MaxInt64).Seconds()), "Maximum time period in seconds to add as a delay jitter for application resync.") + command.Flags().Int64Var(&repoErrorGracePeriod, "repo-error-grace-period-seconds", int64(env.ParseDurationFromEnv("ARGOCD_REPO_ERROR_GRACE_PERIOD_SECONDS", defaultAppResyncPeriod*time.Second, 0, math.MaxInt64).Seconds()), "Grace period in seconds for ignoring consecutive errors while communicating with repo server.") command.Flags().StringVar(&repoServerAddress, "repo-server", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER", common.DefaultRepoServerAddr), "Repo server address.") command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.") command.Flags().IntVar(&statusProcessors, "status-processors", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_STATUS_PROCESSORS", 20, 0, math.MaxInt32), "Number of application status processors") @@ -188,33 +207,32 @@ func NewCommand() *cobra.Command { command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDMetrics, "Start metrics server on given port") command.Flags().DurationVar(&metricsCacheExpiration, "metrics-cache-expiration", env.ParseDurationFromEnv("ARGOCD_APPLICATION_CONTROLLER_METRICS_CACHE_EXPIRATION", 0*time.Second, 0, math.MaxInt64), "Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)") command.Flags().IntVar(&selfHealTimeoutSeconds, "self-heal-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS", 5, 0, math.MaxInt32), "Specifies timeout between application self heal attempts") - command.Flags().Int64Var(&kubectlParallelismLimit, "kubectl-parallelism-limit", 20, "Number of allowed concurrent kubectl fork/execs. Any value less the 1 means no limit.") + command.Flags().Int64Var(&kubectlParallelismLimit, "kubectl-parallelism-limit", env.ParseInt64FromEnv("ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT", 20, 0, math.MaxInt64), "Number of allowed concurrent kubectl fork/execs. Any value less than 1 means no limit.") command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT", false), "Disable TLS on connections to repo server") command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS", false), "Whether to use strict validation of the TLS cert presented by the repo server") command.Flags().StringSliceVar(&metricsAplicationLabels, "metrics-application-labels", []string{}, "List of Application labels that will be added to the argocd_application_labels metric") command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to") + command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode") + command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)") + command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)") command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces that applications are allowed to be reconciled from") command.Flags().BoolVar(&persistResourceHealth, "persist-resource-health", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH", true), "Enables storing the managed resources health in the Application CRD") - cacheSrc = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) { - redisClient = client + command.Flags().StringVar(&shardingAlgorithm, "sharding-method", env.StringFromEnv(common.EnvControllerShardingAlgorithm, common.DefaultShardingAlgorithm), "Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] ") + // global queue rate limit config + command.Flags().Int64Var(&workqueueRateLimit.BucketSize, "wq-bucket-size", env.ParseInt64FromEnv("WORKQUEUE_BUCKET_SIZE", 500, 1, math.MaxInt64), "Set Workqueue Rate Limiter Bucket Size, default 500") + command.Flags().Float64Var(&workqueueRateLimit.BucketQPS, "wq-bucket-qps", env.ParseFloat64FromEnv("WORKQUEUE_BUCKET_QPS", math.MaxFloat64, 1, math.MaxFloat64), "Set Workqueue Rate Limiter Bucket QPS, default set to MaxFloat64 which disables the bucket limiter") + // individual item rate limit config + // when WORKQUEUE_FAILURE_COOLDOWN is 0 per item rate limiting is disabled(default) + command.Flags().DurationVar(&workqueueRateLimit.FailureCoolDown, "wq-cooldown-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_FAILURE_COOLDOWN_NS", 0, 0, (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Cooldown duration in ns, default 0(per item rate limiter disabled)") + command.Flags().DurationVar(&workqueueRateLimit.BaseDelay, "wq-basedelay-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_BASE_DELAY_NS", time.Millisecond.Nanoseconds(), time.Nanosecond.Nanoseconds(), (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Base Delay duration in nanoseconds, default 1000000 (1ms)") + command.Flags().DurationVar(&workqueueRateLimit.MaxDelay, "wq-maxdelay-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_MAX_DELAY_NS", time.Second.Nanoseconds(), 1*time.Millisecond.Nanoseconds(), (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Max Delay duration in nanoseconds, default 1000000000 (1s)") + command.Flags().Float64Var(&workqueueRateLimit.BackoffFactor, "wq-backoff-factor", env.ParseFloat64FromEnv("WORKQUEUE_BACKOFF_FACTOR", 1.5, 0, math.MaxFloat64), "Set Workqueue Per Item Rate Limiter Backoff Factor, default is 1.5") + command.Flags().BoolVar(&enableDynamicClusterDistribution, "dynamic-cluster-distribution-enabled", env.ParseBoolFromEnv(common.EnvEnableDynamicClusterDistribution, false), "Enables dynamic cluster distribution.") + command.Flags().BoolVar(&serverSideDiff, "server-side-diff-enabled", env.ParseBoolFromEnv(common.EnvServerSideDiff, false), "Feature flag to enable ServerSide diff. Default (\"false\")") + cacheSource = appstatecache.AddCacheFlagsToCmd(&command, cacheutil.Options{ + OnClientCreated: 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 -} diff --git a/cmd/argocd-applicationset-controller/commands/applicationset_controller.go b/cmd/argocd-applicationset-controller/commands/applicationset_controller.go index fa64a0f680940..9adbc3e64a685 100644 --- a/cmd/argocd-applicationset-controller/commands/applicationset_controller.go +++ b/cmd/argocd-applicationset-controller/commands/applicationset_controller.go @@ -2,6 +2,7 @@ package command import ( "fmt" + "math" "net/http" "os" "time" @@ -9,7 +10,9 @@ import ( "github.com/argoproj/pkg/stats" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/cache" + + "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + "github.com/argoproj/argo-cd/v2/util/tls" "github.com/argoproj/argo-cd/v2/applicationset/controllers" "github.com/argoproj/argo-cd/v2/applicationset/generators" @@ -17,7 +20,6 @@ import ( "github.com/argoproj/argo-cd/v2/applicationset/webhook" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" "github.com/argoproj/argo-cd/v2/common" - "github.com/argoproj/argo-cd/v2/reposerver/askpass" "github.com/argoproj/argo-cd/v2/util/env" "github.com/argoproj/argo-cd/v2/util/github_app" @@ -30,7 +32,6 @@ import ( "k8s.io/client-go/tools/clientcmd" "github.com/argoproj/argo-cd/v2/applicationset/services" - appsetv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" "github.com/argoproj/argo-cd/v2/util/cli" @@ -39,28 +40,35 @@ import ( argosettings "github.com/argoproj/argo-cd/v2/util/settings" ) -// TODO: load this using Cobra. -func getSubmoduleEnabled() bool { - return env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true) -} +var gitSubmoduleEnabled = env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true) func NewCommand() *cobra.Command { var ( - clientConfig clientcmd.ClientConfig - metricsAddr string - probeBindAddr string - webhookAddr string - enableLeaderElection bool - namespace string - argocdRepoServer string - policy string - debugLog bool - dryRun bool - enableProgressiveRollouts bool + clientConfig clientcmd.ClientConfig + metricsAddr string + probeBindAddr string + webhookAddr string + enableLeaderElection bool + applicationSetNamespaces []string + argocdRepoServer string + policy string + enablePolicyOverride bool + debugLog bool + dryRun bool + enableProgressiveSyncs bool + enableNewGitFileGlobbing bool + repoServerPlaintext bool + repoServerStrictTLS bool + repoServerTimeoutSeconds int + maxConcurrentReconciliations int + scmRootCAPath string + allowedScmProviders []string + globalPreservedAnnotations []string + globalPreservedLabels []string + enableScmProviders bool ) scheme := runtime.NewScheme() _ = clientgoscheme.AddToScheme(scheme) - _ = appsetv1alpha1.AddToScheme(scheme) _ = appv1alpha1.AddToScheme(scheme) var command = cobra.Command{ Use: "controller", @@ -70,6 +78,8 @@ func NewCommand() *cobra.Command { vers := common.GetVersion() namespace, _, err := clientConfig.Namespace() + applicationSetNamespaces = append(applicationSetNamespaces, namespace) + errors.CheckError(err) vers.LogStartupInfo( "ArgoCD ApplicationSet Controller", @@ -82,58 +92,81 @@ func NewCommand() *cobra.Command { cli.SetLogLevel(cmdutil.LogLevel) restConfig, err := clientConfig.ClientConfig() - if err != nil { - return err - } + errors.CheckError(err) restConfig.UserAgent = fmt.Sprintf("argocd-applicationset-controller/%s (%s)", vers.Version, vers.Platform) policyObj, exists := utils.Policies[policy] if !exists { - log.Info("Policy value can be: sync, create-only, create-update, create-delete") + log.Error("Policy value can be: sync, create-only, create-update, create-delete, default value: sync") + os.Exit(1) + } + + // By default watch all namespace + var watchedNamespace string = "" + + // If the applicationset-namespaces contains only one namespace it corresponds to the current namespace + if len(applicationSetNamespaces) == 1 { + watchedNamespace = (applicationSetNamespaces)[0] + } else if enableScmProviders && len(allowedScmProviders) == 0 { + log.Error("When enabling applicationset in any namespace using applicationset-namespaces, you must either set --enable-scm-providers=false or specify --allowed-scm-providers") os.Exit(1) } mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: metricsAddr, - // Our cache and thus watches and client queries are restricted to the namespace we're running in. This assumes - // the applicationset controller is in the same namespace as argocd, which should be the same namespace of - // all cluster Secrets and Applications we interact with. - NewCache: cache.MultiNamespacedCacheBuilder([]string{namespace}), + Scheme: scheme, + MetricsBindAddress: metricsAddr, + Namespace: watchedNamespace, HealthProbeBindAddress: probeBindAddr, Port: 9443, LeaderElection: enableLeaderElection, LeaderElectionID: "58ac56fa.applicationsets.argoproj.io", DryRunClient: dryRun, }) + if err != nil { log.Error(err, "unable to start manager") os.Exit(1) } dynamicClient, err := dynamic.NewForConfig(mgr.GetConfig()) - if err != nil { - return err - } + errors.CheckError(err) k8sClient, err := kubernetes.NewForConfig(mgr.GetConfig()) - if err != nil { - return err - } + errors.CheckError(err) + argoSettingsMgr := argosettings.NewSettingsManager(ctx, k8sClient, namespace) appSetConfig := appclientset.NewForConfigOrDie(mgr.GetConfig()) argoCDDB := db.NewDB(namespace, argoSettingsMgr, k8sClient) - askPassServer := askpass.NewServer() scmAuth := generators.SCMAuthProviders{ GitHubApps: github_app.NewAuthCredentials(argoCDDB.(db.RepoCredsDB)), } + + tlsConfig := apiclient.TLSConfiguration{ + DisableTLS: repoServerPlaintext, + StrictValidation: repoServerPlaintext, + } + + if !repoServerPlaintext && repoServerStrictTLS { + pool, err := tls.LoadX509CertPool( + fmt.Sprintf("%s/reposerver/tls/tls.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)), + fmt.Sprintf("%s/reposerver/tls/ca.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)), + ) + errors.CheckError(err) + tlsConfig.Certificates = pool + } + + repoClientset := apiclient.NewRepoServerClientset(argocdRepoServer, repoServerTimeoutSeconds, tlsConfig) + argoCDService, err := services.NewArgoCDService(argoCDDB, gitSubmoduleEnabled, repoClientset, enableNewGitFileGlobbing) + errors.CheckError(err) + terminalGenerators := map[string]generators.Generator{ "List": generators.NewListGenerator(), "Clusters": generators.NewClusterGenerator(mgr.GetClient(), ctx, k8sClient, namespace), - "Git": generators.NewGitGenerator(services.NewArgoCDService(argoCDDB, askPassServer, getSubmoduleEnabled())), - "SCMProvider": generators.NewSCMProviderGenerator(mgr.GetClient(), scmAuth), + "Git": generators.NewGitGenerator(argoCDService), + "SCMProvider": generators.NewSCMProviderGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders, enableScmProviders), "ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace), - "PullRequest": generators.NewPullRequestGenerator(mgr.GetClient(), scmAuth), + "PullRequest": generators.NewPullRequestGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders, enableScmProviders), + "Plugin": generators.NewPluginGenerator(mgr.GetClient(), ctx, k8sClient, namespace), } nestedGenerators := map[string]generators.Generator{ @@ -143,6 +176,7 @@ func NewCommand() *cobra.Command { "SCMProvider": terminalGenerators["SCMProvider"], "ClusterDecisionResource": terminalGenerators["ClusterDecisionResource"], "PullRequest": terminalGenerators["PullRequest"], + "Plugin": terminalGenerators["Plugin"], "Matrix": generators.NewMatrixGenerator(terminalGenerators), "Merge": generators.NewMergeGenerator(terminalGenerators), } @@ -154,6 +188,7 @@ func NewCommand() *cobra.Command { "SCMProvider": terminalGenerators["SCMProvider"], "ClusterDecisionResource": terminalGenerators["ClusterDecisionResource"], "PullRequest": terminalGenerators["PullRequest"], + "Plugin": terminalGenerators["Plugin"], "Matrix": generators.NewMatrixGenerator(nestedGenerators), "Merge": generators.NewMergeGenerator(nestedGenerators), } @@ -167,19 +202,25 @@ func NewCommand() *cobra.Command { startWebhookServer(webhookHandler, webhookAddr) } - go func() { errors.CheckError(askPassServer.Run(askpass.SocketPath)) }() if err = (&controllers.ApplicationSetReconciler{ - Generators: topLevelGenerators, - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("applicationset-controller"), - Renderer: &utils.Render{}, - Policy: policyObj, - ArgoAppClientset: appSetConfig, - KubeClientset: k8sClient, - ArgoDB: argoCDDB, - EnableProgressiveRollouts: enableProgressiveRollouts, - }).SetupWithManager(mgr); err != nil { + Generators: topLevelGenerators, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("applicationset-controller"), + Renderer: &utils.Render{}, + Policy: policyObj, + EnablePolicyOverride: enablePolicyOverride, + ArgoAppClientset: appSetConfig, + KubeClientset: k8sClient, + ArgoDB: argoCDDB, + ArgoCDNamespace: namespace, + ApplicationSetNamespaces: applicationSetNamespaces, + EnableProgressiveSyncs: enableProgressiveSyncs, + SCMRootCAPath: scmRootCAPath, + GlobalPreservedAnnotations: globalPreservedAnnotations, + GlobalPreservedLabels: globalPreservedLabels, + Cache: mgr.GetCache(), + }).SetupWithManager(mgr, enableProgressiveSyncs, maxConcurrentReconciliations); err != nil { log.Error(err, "unable to create controller", "controller", "ApplicationSet") os.Exit(1) } @@ -200,14 +241,25 @@ func NewCommand() *cobra.Command { command.Flags().BoolVar(&enableLeaderElection, "enable-leader-election", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_LEADER_ELECTION", false), "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") - command.Flags().StringVar(&namespace, "namespace", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACE", ""), "Argo CD repo namespace (default: argocd)") + command.Flags().StringSliceVar(&applicationSetNamespaces, "applicationset-namespaces", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES", []string{}, ","), "Argo CD applicationset namespaces") command.Flags().StringVar(&argocdRepoServer, "argocd-repo-server", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER", common.DefaultRepoServerAddr), "Argo CD repo server address") - command.Flags().StringVar(&policy, "policy", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_POLICY", "sync"), "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion), 'create-delete' (no update)") + command.Flags().StringVar(&policy, "policy", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_POLICY", ""), "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion), 'create-delete' (no update)") + command.Flags().BoolVar(&enablePolicyOverride, "enable-policy-override", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_POLICY_OVERRIDE", policy == ""), "For security reason if 'policy' is set, it is not possible to override it at applicationSet level. 'allow-policy-override' allows user to define their own policy") command.Flags().BoolVar(&debugLog, "debug", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG", false), "Print debug logs. Takes precedence over loglevel") command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json") command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error") + command.Flags().StringSliceVar(&allowedScmProviders, "allowed-scm-providers", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS", []string{}, ","), "The list of allowed custom SCM provider API URLs. This restriction does not apply to SCM or PR generators which do not accept a custom API URL. (Default: Empty = all)") + command.Flags().BoolVar(&enableScmProviders, "enable-scm-providers", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS", true), "Enable retrieving information from SCM providers, used by the SCM and PR generators (Default: true)") command.Flags().BoolVar(&dryRun, "dry-run", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DRY_RUN", false), "Enable dry run mode") - command.Flags().BoolVar(&enableProgressiveRollouts, "enable-progressive-rollouts", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_ROLLOUTS", false), "Enable use of the experimental progressive rollouts feature.") + command.Flags().BoolVar(&enableProgressiveSyncs, "enable-progressive-syncs", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS", false), "Enable use of the experimental progressive syncs feature.") + command.Flags().BoolVar(&enableNewGitFileGlobbing, "enable-new-git-file-globbing", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING", false), "Enable new globbing in Git files generator.") + command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_PLAINTEXT", false), "Disable TLS on connections to repo server") + command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_STRICT_TLS", false), "Whether to use strict validation of the TLS cert presented by the repo server") + command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.") + command.Flags().IntVar(&maxConcurrentReconciliations, "concurrent-reconciliations", env.ParseNumFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_CONCURRENT_RECONCILIATIONS", 10, 1, 100), "Max concurrent reconciliations limit for the controller") + command.Flags().StringVar(&scmRootCAPath, "scm-root-ca-path", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH", ""), "Provide Root CA Path for self-signed TLS Certificates") + command.Flags().StringSliceVar(&globalPreservedAnnotations, "preserved-annotations", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_ANNOTATIONS", []string{}, ","), "Sets global preserved field values for annotations") + command.Flags().StringSliceVar(&globalPreservedLabels, "preserved-labels", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_LABELS", []string{}, ","), "Sets global preserved field values for labels") return &command } diff --git a/cmd/argocd-cmp-server/commands/argocd_cmp_server.go b/cmd/argocd-cmp-server/commands/argocd_cmp_server.go index ffb5eccc2f450..526a199cb5490 100644 --- a/cmd/argocd-cmp-server/commands/argocd_cmp_server.go +++ b/cmd/argocd-cmp-server/commands/argocd_cmp_server.go @@ -26,11 +26,14 @@ func NewCommand() *cobra.Command { var ( configFilePath string otlpAddress string + otlpInsecure bool + otlpHeaders map[string]string + otlpAttrs []string ) var command = cobra.Command{ Use: cliName, Short: "Run ArgoCD ConfigManagementPlugin Server", - Long: "ArgoCD ConfigManagementPlugin Server is an internal service which runs as sidecar container in reposerver deployment. It can be configured by following options.", + Long: "ArgoCD ConfigManagementPlugin Server is an internal service which runs as sidecar container in reposerver deployment. The following configuration options are available:", DisableAutoGenTag: true, RunE: func(c *cobra.Command, args []string) error { ctx := c.Context() @@ -44,10 +47,18 @@ func NewCommand() *cobra.Command { config, err := plugin.ReadPluginConfig(configFilePath) errors.CheckError(err) + if !config.Spec.Discover.IsDefined() { + name := config.Metadata.Name + if config.Spec.Version != "" { + name = name + "-" + config.Spec.Version + } + log.Infof("No discovery configuration is defined for plugin %s. To use this plugin, specify %q in the Application's spec.source.plugin.name field.", config.Metadata.Name, name) + } + if otlpAddress != "" { var closer func() var err error - closer, err = traceutil.InitTracer(ctx, "argocd-cmp-server", otlpAddress) + closer, err = traceutil.InitTracer(ctx, "argocd-cmp-server", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs) if err != nil { log.Fatalf("failed to initialize tracing: %v", err) } @@ -74,5 +85,8 @@ func NewCommand() *cobra.Command { command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error") command.Flags().StringVar(&configFilePath, "config-dir-path", common.DefaultPluginConfigFilePath, "Config management plugin configuration file location, Default is '/home/argocd/cmp-server/config/'") command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_CMP_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to") + command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_CMP_SERVER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode") + command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_CMP_SERVER_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)") + command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_CMP_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)") return &command } diff --git a/cmd/argocd-dex/commands/argocd_dex.go b/cmd/argocd-dex/commands/argocd_dex.go index 7d9f04c31f553..2b070ec895e41 100644 --- a/cmd/argocd-dex/commands/argocd_dex.go +++ b/cmd/argocd-dex/commands/argocd_dex.go @@ -8,11 +8,11 @@ import ( "github.com/argoproj/argo-cd/v2/common" - "github.com/ghodss/yaml" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/yaml" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" "github.com/argoproj/argo-cd/v2/util/cli" diff --git a/cmd/argocd-k8s-auth/commands/argocd_k8s_auth.go b/cmd/argocd-k8s-auth/commands/argocd_k8s_auth.go index 410364e6429df..ce0f3ee3a2f49 100644 --- a/cmd/argocd-k8s-auth/commands/argocd_k8s_auth.go +++ b/cmd/argocd-k8s-auth/commands/argocd_k8s_auth.go @@ -20,6 +20,7 @@ func NewCommand() *cobra.Command { command.AddCommand(newAWSCommand()) command.AddCommand(newGCPCommand()) + command.AddCommand(newAzureCommand()) return command } diff --git a/cmd/argocd-k8s-auth/commands/aws.go b/cmd/argocd-k8s-auth/commands/aws.go index 79a118d2653a3..9b750ac5f92f8 100644 --- a/cmd/argocd-k8s-auth/commands/aws.go +++ b/cmd/argocd-k8s-auth/commands/aws.go @@ -37,13 +37,14 @@ func newAWSCommand() *cobra.Command { var ( clusterName string roleARN string + profile string ) var command = &cobra.Command{ Use: "aws", Run: func(c *cobra.Command, args []string) { ctx := c.Context() - presignedURLString, err := getSignedRequestWithRetry(ctx, time.Minute, 5*time.Second, clusterName, roleARN, getSignedRequest) + presignedURLString, err := getSignedRequestWithRetry(ctx, time.Minute, 5*time.Second, clusterName, roleARN, profile, getSignedRequest) errors.CheckError(err) token := v1Prefix + base64.RawURLEncoding.EncodeToString([]byte(presignedURLString)) // Set token expiration to 1 minute before the presigned URL expires for some cushion @@ -53,16 +54,17 @@ func newAWSCommand() *cobra.Command { } command.Flags().StringVar(&clusterName, "cluster-name", "", "AWS Cluster name") command.Flags().StringVar(&roleARN, "role-arn", "", "AWS Role ARN") + command.Flags().StringVar(&profile, "profile", "", "AWS Profile") return command } -type getSignedRequestFunc func(clusterName, roleARN string) (string, error) +type getSignedRequestFunc func(clusterName, roleARN string, profile string) (string, error) -func getSignedRequestWithRetry(ctx context.Context, timeout, interval time.Duration, clusterName, roleARN string, fn getSignedRequestFunc) (string, error) { +func getSignedRequestWithRetry(ctx context.Context, timeout, interval time.Duration, clusterName, roleARN string, profile string, fn getSignedRequestFunc) (string, error) { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() for { - signed, err := fn(clusterName, roleARN) + signed, err := fn(clusterName, roleARN, profile) if err == nil { return signed, nil } @@ -74,8 +76,10 @@ func getSignedRequestWithRetry(ctx context.Context, timeout, interval time.Durat } } -func getSignedRequest(clusterName, roleARN string) (string, error) { - sess, err := session.NewSession() +func getSignedRequest(clusterName, roleARN string, profile string) (string, error) { + sess, err := session.NewSessionWithOptions(session.Options{ + Profile: profile, + }) if err != nil { return "", fmt.Errorf("error creating new AWS session: %s", err) } diff --git a/cmd/argocd-k8s-auth/commands/aws_test.go b/cmd/argocd-k8s-auth/commands/aws_test.go index c22449eba42be..578aae71a2c29 100644 --- a/cmd/argocd-k8s-auth/commands/aws_test.go +++ b/cmd/argocd-k8s-auth/commands/aws_test.go @@ -22,7 +22,7 @@ func TestGetSignedRequestWithRetry(t *testing.T) { } // when - signed, err := getSignedRequestWithRetry(ctx, time.Second, time.Millisecond, "cluster-name", "", mock.getSignedRequestMock) + signed, err := getSignedRequestWithRetry(ctx, time.Second, time.Millisecond, "cluster-name", "", "", mock.getSignedRequestMock) // then assert.NoError(t, err) @@ -41,7 +41,7 @@ func TestGetSignedRequestWithRetry(t *testing.T) { } // when - signed, err := getSignedRequestWithRetry(ctx, time.Second, time.Millisecond, "cluster-name", "", mock.getSignedRequestMock) + signed, err := getSignedRequestWithRetry(ctx, time.Second, time.Millisecond, "cluster-name", "", "", mock.getSignedRequestMock) // then assert.NoError(t, err) @@ -57,7 +57,7 @@ func TestGetSignedRequestWithRetry(t *testing.T) { } // when - signed, err := getSignedRequestWithRetry(ctx, time.Second, time.Millisecond, "cluster-name", "", mock.getSignedRequestMock) + signed, err := getSignedRequestWithRetry(ctx, time.Second, time.Millisecond, "cluster-name", "", "", mock.getSignedRequestMock) // then assert.Error(t, err) @@ -70,7 +70,7 @@ type signedRequestMock struct { returnFunc func(m *signedRequestMock) (string, error) } -func (m *signedRequestMock) getSignedRequestMock(clusterName, roleARN string) (string, error) { +func (m *signedRequestMock) getSignedRequestMock(clusterName, roleARN string, profile string) (string, error) { m.getSignedRequestCalls++ return m.returnFunc(m) } diff --git a/cmd/argocd-k8s-auth/commands/azure.go b/cmd/argocd-k8s-auth/commands/azure.go new file mode 100644 index 0000000000000..bc45bbacef48b --- /dev/null +++ b/cmd/argocd-k8s-auth/commands/azure.go @@ -0,0 +1,43 @@ +package commands + +import ( + "os" + + "github.com/Azure/kubelogin/pkg/token" + "github.com/spf13/cobra" + + "github.com/argoproj/argo-cd/v2/util/errors" +) + +var ( + envServerApplicationID = "AAD_SERVER_APPLICATION_ID" + envEnvironmentName = "AAD_ENVIRONMENT_NAME" +) + +const ( + DEFAULT_AAD_SERVER_APPLICATION_ID = "6dae42f8-4368-4678-94ff-3960e28e3630" +) + +func newAzureCommand() *cobra.Command { + o := token.NewOptions() + //we'll use default of WorkloadIdentityLogin for the login flow + o.LoginMethod = token.WorkloadIdentityLogin + o.ServerID = DEFAULT_AAD_SERVER_APPLICATION_ID + var command = &cobra.Command{ + Use: "azure", + Run: func(c *cobra.Command, args []string) { + o.UpdateFromEnv() + if v, ok := os.LookupEnv(envServerApplicationID); ok { + o.ServerID = v + } + if v, ok := os.LookupEnv(envEnvironmentName); ok { + o.Environment = v + } + plugin, err := token.New(&o) + errors.CheckError(err) + err = plugin.Do() + errors.CheckError(err) + }, + } + return command +} diff --git a/cmd/argocd-notification/commands/controller.go b/cmd/argocd-notification/commands/controller.go index d77271f05eab4..cb30fd5277d4b 100644 --- a/cmd/argocd-notification/commands/controller.go +++ b/cmd/argocd-notification/commands/controller.go @@ -43,18 +43,20 @@ func addK8SFlagsToCmd(cmd *cobra.Command) clientcmd.ClientConfig { func NewCommand() *cobra.Command { var ( - clientConfig clientcmd.ClientConfig - processorsCount int - namespace string - appLabelSelector string - logLevel string - logFormat string - metricsPort int - argocdRepoServer string - argocdRepoServerPlaintext bool - argocdRepoServerStrictTLS bool - configMapName string - secretName string + clientConfig clientcmd.ClientConfig + processorsCount int + namespace string + appLabelSelector string + logLevel string + logFormat string + metricsPort int + argocdRepoServer string + argocdRepoServerPlaintext bool + argocdRepoServerStrictTLS bool + configMapName string + secretName string + applicationNamespaces []string + selfServiceNotificationEnabled bool ) var command = cobra.Command{ Use: "controller", @@ -74,26 +76,26 @@ func NewCommand() *cobra.Command { restConfig, err := clientConfig.ClientConfig() if err != nil { - return err + return fmt.Errorf("failed to create REST client config: %w", err) } restConfig.UserAgent = fmt.Sprintf("argocd-notifications-controller/%s (%s)", vers.Version, vers.Platform) dynamicClient, err := dynamic.NewForConfig(restConfig) if err != nil { - return err + return fmt.Errorf("failed to create dynamic client: %w", err) } k8sClient, err := kubernetes.NewForConfig(restConfig) if err != nil { - return err + return fmt.Errorf("failed to create Kubernetes client: %w", err) } if namespace == "" { namespace, _, err = clientConfig.Namespace() if err != nil { - return err + return fmt.Errorf("failed to determine controller's host namespace: %w", err) } } level, err := log.ParseLevel(logLevel) if err != nil { - return err + return fmt.Errorf("failed to parse log level: %w", err) } log.SetLevel(level) @@ -105,7 +107,7 @@ func NewCommand() *cobra.Command { log.SetFormatter(&log.TextFormatter{ForceColors: true}) } default: - return fmt.Errorf("Unknown log format '%s'", logFormat) + return fmt.Errorf("unknown log format '%s'", logFormat) } tlsConfig := apiclient.TLSConfiguration{ @@ -118,14 +120,14 @@ func NewCommand() *cobra.Command { fmt.Sprintf("%s/reposerver/tls/ca.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)), ) if err != nil { - return err + return fmt.Errorf("failed to load repo-server certificate pool: %w", err) } tlsConfig.Certificates = pool } repoClientset := apiclient.NewRepoServerClientset(argocdRepoServer, 5, tlsConfig) argocdService, err := service.NewArgoCDService(k8sClient, namespace, repoClientset) if err != nil { - return err + return fmt.Errorf("failed to initialize Argo CD service: %w", err) } defer argocdService.Close() @@ -138,10 +140,10 @@ func NewCommand() *cobra.Command { log.Infof("serving metrics on port %d", metricsPort) log.Infof("loading configuration %d", metricsPort) - ctrl := notificationscontroller.NewController(k8sClient, dynamicClient, argocdService, namespace, appLabelSelector, registry, secretName, configMapName) + ctrl := notificationscontroller.NewController(k8sClient, dynamicClient, argocdService, namespace, applicationNamespaces, appLabelSelector, registry, secretName, configMapName, selfServiceNotificationEnabled) err = ctrl.Init(ctx) if err != nil { - return err + return fmt.Errorf("failed to initialize controller: %w", err) } go ctrl.Run(ctx, processorsCount) @@ -153,13 +155,15 @@ func NewCommand() *cobra.Command { command.Flags().IntVar(&processorsCount, "processors-count", 1, "Processors count.") command.Flags().StringVar(&appLabelSelector, "app-label-selector", "", "App label selector.") command.Flags().StringVar(&namespace, "namespace", "", "Namespace which controller handles. Current namespace if empty.") - command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error") - command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json") + command.Flags().StringVar(&logLevel, "loglevel", env.StringFromEnv("ARGOCD_NOTIFICATIONS_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error") + command.Flags().StringVar(&logFormat, "logformat", env.StringFromEnv("ARGOCD_NOTIFICATIONS_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json") command.Flags().IntVar(&metricsPort, "metrics-port", defaultMetricsPort, "Metrics port") - command.Flags().StringVar(&argocdRepoServer, "argocd-repo-server", "argocd-repo-server:8081", "Argo CD repo server address") + command.Flags().StringVar(&argocdRepoServer, "argocd-repo-server", common.DefaultRepoServerAddr, "Argo CD repo server address") command.Flags().BoolVar(&argocdRepoServerPlaintext, "argocd-repo-server-plaintext", false, "Use a plaintext client (non-TLS) to connect to repository server") command.Flags().BoolVar(&argocdRepoServerStrictTLS, "argocd-repo-server-strict-tls", false, "Perform strict validation of TLS certificates when connecting to repo server") command.Flags().StringVar(&configMapName, "config-map-name", "argocd-notifications-cm", "Set notifications ConfigMap name") command.Flags().StringVar(&secretName, "secret-name", "argocd-notifications-secret", "Set notifications Secret name") + command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces that this controller should send notifications for") + command.Flags().BoolVar(&selfServiceNotificationEnabled, "self-service-notification-enabled", env.ParseBoolFromEnv("ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED", false), "Allows the Argo CD notification controller to pull notification config from the namespace that the resource is in. This is useful for self-service notification.") return &command } diff --git a/cmd/argocd-repo-server/commands/argocd_repo_server.go b/cmd/argocd-repo-server/commands/argocd_repo_server.go index e5ca1db44ad2e..2ba17cd9b64ba 100644 --- a/cmd/argocd-repo-server/commands/argocd_repo_server.go +++ b/cmd/argocd-repo-server/commands/argocd_repo_server.go @@ -5,11 +5,10 @@ import ( "math" "net" "net/http" - "os" "time" "github.com/argoproj/pkg/stats" - "github.com/go-redis/redis/v8" + "github.com/redis/go-redis/v9" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "google.golang.org/grpc/health/grpc_health_v1" @@ -36,44 +35,28 @@ import ( const ( // CLIName is the name of the CLI - cliName = "argocd-repo-server" - gnuPGSourcePath = "/app/config/gpg/source" - - defaultPauseGenerationAfterFailedGenerationAttempts = 3 - defaultPauseGenerationOnFailureForMinutes = 60 - defaultPauseGenerationOnFailureForRequests = 0 + cliName = "argocd-repo-server" ) -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 getSubmoduleEnabled() bool { - return env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true) -} +var ( + gnuPGSourcePath = env.StringFromEnv(common.EnvGPGDataPath, "/app/config/gpg/source") + pauseGenerationAfterFailedGenerationAttempts = env.ParseNumFromEnv(common.EnvPauseGenerationAfterFailedAttempts, 3, 0, math.MaxInt32) + pauseGenerationOnFailureForMinutes = env.ParseNumFromEnv(common.EnvPauseGenerationMinutes, 60, 0, math.MaxInt32) + pauseGenerationOnFailureForRequests = env.ParseNumFromEnv(common.EnvPauseGenerationRequests, 0, 0, math.MaxInt32) + gitSubmoduleEnabled = env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true) +) func NewCommand() *cobra.Command { var ( parallelismLimit int64 listenPort int + listenHost string metricsPort int + metricsHost string otlpAddress string + otlpInsecure bool + otlpHeaders map[string]string + otlpAttrs []string cacheSrc func() (*reposervercache.Cache, error) tlsConfigCustomizer tls.ConfigCustomizer tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error) @@ -84,6 +67,9 @@ func NewCommand() *cobra.Command { allowOutOfBoundsSymlinks bool streamedManifestMaxTarSize string streamedManifestMaxExtractedSize string + helmManifestMaxExtractedSize string + helmRegistryMaxIndexSize string + disableManifestMaxExtractedSize bool ) var command = cobra.Command{ Use: cliName, @@ -122,27 +108,35 @@ func NewCommand() *cobra.Command { streamedManifestMaxExtractedSizeQuantity, err := resource.ParseQuantity(streamedManifestMaxExtractedSize) errors.CheckError(err) + helmManifestMaxExtractedSizeQuantity, err := resource.ParseQuantity(helmManifestMaxExtractedSize) + errors.CheckError(err) + + helmRegistryMaxIndexSizeQuantity, err := resource.ParseQuantity(helmRegistryMaxIndexSize) + errors.CheckError(err) + askPassServer := askpass.NewServer() metricsServer := metrics.NewMetricsServer() cacheutil.CollectMetrics(redisClient, metricsServer) server, err := reposerver.NewServer(metricsServer, cache, tlsConfigCustomizer, repository.RepoServerInitConstants{ ParallelismLimit: parallelismLimit, - PauseGenerationAfterFailedGenerationAttempts: getPauseGenerationAfterFailedGenerationAttempts(), - PauseGenerationOnFailureForMinutes: getPauseGenerationOnFailureForMinutes(), - PauseGenerationOnFailureForRequests: getPauseGenerationOnFailureForRequests(), - SubmoduleEnabled: getSubmoduleEnabled(), + PauseGenerationAfterFailedGenerationAttempts: pauseGenerationAfterFailedGenerationAttempts, + PauseGenerationOnFailureForMinutes: pauseGenerationOnFailureForMinutes, + PauseGenerationOnFailureForRequests: pauseGenerationOnFailureForRequests, + SubmoduleEnabled: gitSubmoduleEnabled, MaxCombinedDirectoryManifestsSize: maxCombinedDirectoryManifestsQuantity, CMPTarExcludedGlobs: cmpTarExcludedGlobs, AllowOutOfBoundsSymlinks: allowOutOfBoundsSymlinks, StreamedManifestMaxExtractedSize: streamedManifestMaxExtractedSizeQuantity.ToDec().Value(), StreamedManifestMaxTarSize: streamedManifestMaxTarSizeQuantity.ToDec().Value(), + HelmManifestMaxExtractedSize: helmManifestMaxExtractedSizeQuantity.ToDec().Value(), + HelmRegistryMaxIndexSize: helmRegistryMaxIndexSizeQuantity.ToDec().Value(), }, askPassServer) errors.CheckError(err) if otlpAddress != "" { var closer func() var err error - closer, err = traceutil.InitTracer(ctx, "argocd-repo-server", otlpAddress) + closer, err = traceutil.InitTracer(ctx, "argocd-repo-server", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs) if err != nil { log.Fatalf("failed to initialize tracing: %v", err) } @@ -150,7 +144,7 @@ func NewCommand() *cobra.Command { } grpc := server.CreateGRPC() - listener, err := net.Listen("tcp", fmt.Sprintf(":%d", listenPort)) + listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", listenHost, listenPort)) errors.CheckError(err) healthz.ServeHealthCheck(http.DefaultServeMux, func(r *http.Request) error { @@ -176,7 +170,7 @@ func NewCommand() *cobra.Command { return nil }) http.Handle("/metrics", metricsServer.GetHandler()) - go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf(":%d", metricsPort), nil)) }() + go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf("%s:%d", metricsHost, metricsPort), nil)) }() go func() { errors.CheckError(askPassServer.Run(askpass.SocketPath)) }() if gpg.IsGPGEnabled() { @@ -184,12 +178,12 @@ func NewCommand() *cobra.Command { err = gpg.InitializeGnuPG() errors.CheckError(err) - log.Infof("Populating GnuPG keyring with keys from %s", getGnuPGSourcePath()) - added, removed, err := gpg.SyncKeyRingFromDirectory(getGnuPGSourcePath()) + log.Infof("Populating GnuPG keyring with keys from %s", gnuPGSourcePath) + added, removed, err := gpg.SyncKeyRingFromDirectory(gnuPGSourcePath) errors.CheckError(err) log.Infof("Loaded %d (and removed %d) keys from keyring", len(added), len(removed)) - go func() { errors.CheckError(reposerver.StartGPGWatcher(getGnuPGSourcePath())) }() + go func() { errors.CheckError(reposerver.StartGPGWatcher(gnuPGSourcePath)) }() } log.Infof("argocd-repo-server is listening on %s", listener.Addr()) @@ -201,24 +195,31 @@ func NewCommand() *cobra.Command { return nil }, } - if cmdutil.LogFormat == "" { - cmdutil.LogFormat = os.Getenv("ARGOCD_REPO_SERVER_LOGLEVEL") - } command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_REPO_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json") command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_REPO_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error") command.Flags().Int64Var(¶llelismLimit, "parallelismlimit", int64(env.ParseNumFromEnv("ARGOCD_REPO_SERVER_PARALLELISM_LIMIT", 0, 0, math.MaxInt32)), "Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit.") + command.Flags().StringVar(&listenHost, "address", env.StringFromEnv("ARGOCD_REPO_SERVER_LISTEN_ADDRESS", common.DefaultAddressRepoServer), "Listen on given address for incoming connections") command.Flags().IntVar(&listenPort, "port", common.DefaultPortRepoServer, "Listen on given port for incoming connections") + command.Flags().StringVar(&metricsHost, "metrics-address", env.StringFromEnv("ARGOCD_REPO_SERVER_METRICS_LISTEN_ADDRESS", common.DefaultAddressRepoServerMetrics), "Listen on given address for metrics") command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortRepoServerMetrics, "Start metrics server on given port") command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_REPO_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to") + command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode") + command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_REPO_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)") + command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_REPO_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)") command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_TLS", false), "Disable TLS on the gRPC endpoint") command.Flags().StringVar(&maxCombinedDirectoryManifestsSize, "max-combined-directory-manifests-size", env.StringFromEnv("ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE", "10M"), "Max combined size of manifest files in a directory-type Application") command.Flags().StringArrayVar(&cmpTarExcludedGlobs, "plugin-tar-exclude", env.StringsFromEnv("ARGOCD_REPO_SERVER_PLUGIN_TAR_EXCLUSIONS", []string{}, ";"), "Globs to filter when sending tarballs to plugins.") command.Flags().BoolVar(&allowOutOfBoundsSymlinks, "allow-oob-symlinks", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_ALLOW_OUT_OF_BOUNDS_SYMLINKS", false), "Allow out-of-bounds symlinks in repositories (not recommended)") command.Flags().StringVar(&streamedManifestMaxTarSize, "streamed-manifest-max-tar-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_TAR_SIZE", "100M"), "Maximum size of streamed manifest archives") command.Flags().StringVar(&streamedManifestMaxExtractedSize, "streamed-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of streamed manifest archives when extracted") + command.Flags().StringVar(&helmManifestMaxExtractedSize, "helm-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of helm manifest archives when extracted") + command.Flags().StringVar(&helmRegistryMaxIndexSize, "helm-registry-max-index-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_INDEX_SIZE", "1G"), "Maximum size of registry index file") + command.Flags().BoolVar(&disableManifestMaxExtractedSize, "disable-helm-manifest-max-extracted-size", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE", false), "Disable maximum size of helm manifest archives when extracted") tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command) - cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) { - redisClient = client + cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, cacheutil.Options{ + OnClientCreated: func(client *redis.Client) { + redisClient = client + }, }) return &command } diff --git a/cmd/argocd-server/commands/argocd_server.go b/cmd/argocd-server/commands/argocd_server.go index df417e71f5e3e..27a2db34189b4 100644 --- a/cmd/argocd-server/commands/argocd_server.go +++ b/cmd/argocd-server/commands/argocd_server.go @@ -4,10 +4,11 @@ import ( "context" "fmt" "math" + "strings" "time" "github.com/argoproj/pkg/stats" - "github.com/go-redis/redis/v8" + "github.com/redis/go-redis/v9" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "k8s.io/client-go/kubernetes" @@ -18,13 +19,16 @@ import ( "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + reposervercache "github.com/argoproj/argo-cd/v2/reposerver/cache" "github.com/argoproj/argo-cd/v2/server" servercache "github.com/argoproj/argo-cd/v2/server/cache" + cacheutil "github.com/argoproj/argo-cd/v2/util/cache" "github.com/argoproj/argo-cd/v2/util/cli" "github.com/argoproj/argo-cd/v2/util/dex" "github.com/argoproj/argo-cd/v2/util/env" "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/kube" + "github.com/argoproj/argo-cd/v2/util/templates" "github.com/argoproj/argo-cd/v2/util/tls" traceutil "github.com/argoproj/argo-cd/v2/util/trace" ) @@ -35,23 +39,23 @@ const ( ) var ( - failureRetryCount = 0 - failureRetryPeriodMilliSeconds = 100 + failureRetryCount = env.ParseNumFromEnv(failureRetryCountEnv, 0, 0, 10) + failureRetryPeriodMilliSeconds = env.ParseNumFromEnv(failureRetryPeriodMilliSecondsEnv, 100, 0, 1000) ) -func init() { - failureRetryCount = env.ParseNumFromEnv(failureRetryCountEnv, failureRetryCount, 0, 10) - failureRetryPeriodMilliSeconds = env.ParseNumFromEnv(failureRetryPeriodMilliSecondsEnv, failureRetryPeriodMilliSeconds, 0, 1000) -} - // NewCommand returns a new instance of an argocd command func NewCommand() *cobra.Command { var ( redisClient *redis.Client insecure bool + listenHost string listenPort int + metricsHost string metricsPort int otlpAddress string + otlpInsecure bool + otlpHeaders map[string]string + otlpAttrs []string glogLevel int clientConfig clientcmd.ClientConfig repoServerTimeoutSeconds int @@ -60,9 +64,11 @@ func NewCommand() *cobra.Command { repoServerAddress string dexServerAddress string disableAuth bool + contentTypes string enableGZip bool tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error) cacheSrc func() (*servercache.Cache, error) + repoServerCacheSrc func() (*reposervercache.Cache, error) frameOptions string contentSecurityPolicy string repoServerPlaintext bool @@ -104,6 +110,8 @@ func NewCommand() *cobra.Command { errors.CheckError(err) cache, err := cacheSrc() errors.CheckError(err) + repoServerCache, err := repoServerCacheSrc() + errors.CheckError(err) kubeclientset := kubernetes.NewForConfigOrDie(config) @@ -164,10 +172,17 @@ func NewCommand() *cobra.Command { baseHRef = rootPath } + var contentTypesList []string + if contentTypes != "" { + contentTypesList = strings.Split(contentTypes, ";") + } + argoCDOpts := server.ArgoCDServerOpts{ Insecure: insecure, ListenPort: listenPort, + ListenHost: listenHost, MetricsPort: metricsPort, + MetricsHost: metricsHost, Namespace: namespace, BaseHRef: baseHRef, RootPath: rootPath, @@ -177,9 +192,11 @@ func NewCommand() *cobra.Command { DexServerAddr: dexServerAddress, DexTLSConfig: dexTlsConfig, DisableAuth: disableAuth, + ContentTypes: contentTypesList, EnableGZip: enableGZip, TLSConfigCustomizer: tlsConfigCustomizer, Cache: cache, + RepoServerCache: repoServerCache, XFrameOptions: frameOptions, ContentSecurityPolicy: contentSecurityPolicy, RedisClient: redisClient, @@ -199,7 +216,7 @@ func NewCommand() *cobra.Command { var closer func() ctx, cancel := context.WithCancel(ctx) if otlpAddress != "" { - closer, err = traceutil.InitTracer(ctx, "argocd-server", otlpAddress) + closer, err = traceutil.InitTracer(ctx, "argocd-server", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs) if err != nil { log.Fatalf("failed to initialize tracing: %v", err) } @@ -211,6 +228,13 @@ func NewCommand() *cobra.Command { } } }, + Example: templates.Examples(` + # Start the Argo CD API server with default settings + $ argocd-server + + # Start the Argo CD API server on a custom port and enable tracing + $ argocd-server --port 8888 --otlp-address localhost:4317 + `), } clientConfig = cli.AddKubectlFlagsToCmd(command) @@ -224,11 +248,17 @@ func NewCommand() *cobra.Command { command.Flags().StringVar(&repoServerAddress, "repo-server", env.StringFromEnv("ARGOCD_SERVER_REPO_SERVER", common.DefaultRepoServerAddr), "Repo server address") command.Flags().StringVar(&dexServerAddress, "dex-server", env.StringFromEnv("ARGOCD_SERVER_DEX_SERVER", common.DefaultDexServerAddr), "Dex server address") command.Flags().BoolVar(&disableAuth, "disable-auth", env.ParseBoolFromEnv("ARGOCD_SERVER_DISABLE_AUTH", false), "Disable client authentication") - command.Flags().BoolVar(&enableGZip, "enable-gzip", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_GZIP", false), "Enable GZIP compression") + command.Flags().StringVar(&contentTypes, "api-content-types", env.StringFromEnv("ARGOCD_API_CONTENT_TYPES", "application/json", env.StringFromEnvOpts{AllowEmpty: true}), "Semicolon separated list of allowed content types for non GET api requests. Any content type is allowed if empty.") + command.Flags().BoolVar(&enableGZip, "enable-gzip", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_GZIP", true), "Enable GZIP compression") command.AddCommand(cli.NewVersionCmd(cliName)) + command.Flags().StringVar(&listenHost, "address", env.StringFromEnv("ARGOCD_SERVER_LISTEN_ADDRESS", common.DefaultAddressAPIServer), "Listen on given address") command.Flags().IntVar(&listenPort, "port", common.DefaultPortAPIServer, "Listen on given port") + command.Flags().StringVar(&metricsHost, env.StringFromEnv("ARGOCD_SERVER_METRICS_LISTEN_ADDRESS", "metrics-address"), common.DefaultAddressAPIServerMetrics, "Listen for metrics on given address") command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port") command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to") + command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_SERVER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode") + command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_SERVER_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)") + command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)") command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_SERVER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.") command.Flags().StringVar(&frameOptions, "x-frame-options", env.StringFromEnv("ARGOCD_SERVER_X_FRAME_OPTIONS", "sameorigin"), "Set X-Frame-Options header in HTTP responses to `value`. To disable, set to \"\".") command.Flags().StringVar(&contentSecurityPolicy, "content-security-policy", env.StringFromEnv("ARGOCD_SERVER_CONTENT_SECURITY_POLICY", "frame-ancestors 'self';"), "Set Content-Security-Policy header in HTTP responses to `value`. To disable, set to \"\".") @@ -239,8 +269,11 @@ func NewCommand() *cobra.Command { command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces where application resources can be managed in") command.Flags().BoolVar(&enableProxyExtension, "enable-proxy-extension", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_PROXY_EXTENSION", false), "Enable Proxy Extension feature") tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command) - cacheSrc = servercache.AddCacheFlagsToCmd(command, func(client *redis.Client) { - redisClient = client + cacheSrc = servercache.AddCacheFlagsToCmd(command, cacheutil.Options{ + OnClientCreated: func(client *redis.Client) { + redisClient = client + }, }) + repoServerCacheSrc = reposervercache.AddCacheFlagsToCmd(command, cacheutil.Options{FlagPrefix: "repo-server-"}) return command } diff --git a/cmd/argocd/commands/account.go b/cmd/argocd/commands/account.go index 948687efb377e..5472859551f75 100644 --- a/cmd/argocd/commands/account.go +++ b/cmd/argocd/commands/account.go @@ -11,10 +11,10 @@ import ( "time" timeutil "github.com/argoproj/pkg/time" - "github.com/ghodss/yaml" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "golang.org/x/term" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" @@ -26,12 +26,26 @@ import ( "github.com/argoproj/argo-cd/v2/util/io" "github.com/argoproj/argo-cd/v2/util/localconfig" sessionutil "github.com/argoproj/argo-cd/v2/util/session" + "github.com/argoproj/argo-cd/v2/util/templates" ) func NewAccountCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var command = &cobra.Command{ Use: "account", Short: "Manage account settings", + Example: templates.Examples(` + # List accounts + argocd account list + + # Update the current user's password + argocd account update-password + + # Can I sync any app? + argocd account can-i sync applications '*' + + # Get User information + argocd account get-user-info + `), Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) os.Exit(1) @@ -130,9 +144,9 @@ has appropriate RBAC permissions to change other accounts. }, } - command.Flags().StringVar(¤tPassword, "current-password", "", "password of the currently logged on user") - command.Flags().StringVar(&newPassword, "new-password", "", "new password you want to update to") - command.Flags().StringVar(&account, "account", "", "an account name that should be updated. Defaults to current user account") + command.Flags().StringVar(¤tPassword, "current-password", "", "Password of the currently logged on user") + command.Flags().StringVar(&newPassword, "new-password", "", "New password you want to update to") + command.Flags().StringVar(&account, "account", "", "An account name that should be updated. Defaults to current user account") return command } @@ -143,6 +157,13 @@ func NewAccountGetUserInfoCommand(clientOpts *argocdclient.ClientOptions) *cobra var command = &cobra.Command{ Use: "get-user-info", Short: "Get user info", + Example: templates.Examples(` + # Get User information for the currently logged-in user (see 'argocd login') + argocd account get-user-info + + # Get User information in yaml format + argocd account get-user-info -o yaml + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() diff --git a/cmd/argocd/commands/admin/admin.go b/cmd/argocd/commands/admin/admin.go index 90bbe7e65241e..49c81e4da4bfe 100644 --- a/cmd/argocd/commands/admin/admin.go +++ b/cmd/argocd/commands/admin/admin.go @@ -3,7 +3,6 @@ package admin import ( "reflect" - "github.com/ghodss/yaml" "github.com/spf13/cobra" apiv1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -12,11 +11,15 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/yaml" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" "github.com/argoproj/argo-cd/v2/common" + argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/settings" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) const ( @@ -27,13 +30,13 @@ const ( 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"} - appplicationSetResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applicationsets"} + applicationsResource = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.ApplicationPlural} + appprojectsResource = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.AppProjectPlural} + appplicationSetResource = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.ApplicationSetPlural} ) // NewAdminCommand returns a new instance of an argocd command -func NewAdminCommand() *cobra.Command { +func NewAdminCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( pathOpts = clientcmd.NewDefaultPathOptions() ) @@ -45,16 +48,97 @@ func NewAdminCommand() *cobra.Command { Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) }, + Example: `# List all clusters +$ argocd admin cluster list + +# Add a new cluster +$ argocd admin cluster add my-cluster --name my-cluster --in-cluster-context + +# Remove a cluster +argocd admin cluster remove my-cluster + +# List all projects +$ argocd admin project list + +# Create a new project +$argocd admin project create my-project --src-namespace my-source-namespace --dest-namespace my-dest-namespace + +# Update a project +$ argocd admin project update my-project --src-namespace my-updated-source-namespace --dest-namespace my-updated-dest-namespace + +# Delete a project +$ argocd admin project delete my-project + +# List all settings +$ argocd admin settings list + +# Get the current settings +$ argocd admin settings get + +# Update settings +$ argocd admin settings update --repository.resync --value 15 + +# List all applications +$ argocd admin app list + +# Get application details +$ argocd admin app get my-app + +# Sync an application +$ argocd admin app sync my-app + +# Pause an application +$ argocd admin app pause my-app + +# Resume an application +$ argocd admin app resume my-app + +# List all repositories +$ argocd admin repo list + +# Add a repository +$ argocd admin repo add https://github.com/argoproj/my-repo.git + +# Remove a repository +$ argocd admin repo remove https://github.com/argoproj/my-repo.git + +# Import an application from a YAML file +$ argocd admin app import -f my-app.yaml + +# Export an application to a YAML file +$ argocd admin app export my-app -o my-exported-app.yaml + +# Access the Argo CD web UI +$ argocd admin dashboard + +# List notifications +$ argocd admin notification list + +# Get notification details +$ argocd admin notification get my-notification + +# Create a new notification +$ argocd admin notification create my-notification -f notification-config.yaml + +# Update a notification +$ argocd admin notification update my-notification -f updated-notification-config.yaml + +# Delete a notification +$ argocd admin notification delete my-notification + +# Reset the initial admin password +$ argocd admin initial-password reset +`, } - command.AddCommand(NewClusterCommand(pathOpts)) + command.AddCommand(NewClusterCommand(clientOpts, pathOpts)) command.AddCommand(NewProjectsCommand()) command.AddCommand(NewSettingsCommand()) - command.AddCommand(NewAppCommand()) + command.AddCommand(NewAppCommand(clientOpts)) command.AddCommand(NewRepoCommand()) command.AddCommand(NewImportCommand()) command.AddCommand(NewExportCommand()) - command.AddCommand(NewDashboardCommand()) + command.AddCommand(NewDashboardCommand(clientOpts)) command.AddCommand(NewNotificationsCommand()) command.AddCommand(NewInitialPasswordCommand()) @@ -193,11 +277,11 @@ func specsEqual(left, right unstructured.Unstructured) bool { leftData, _, _ := unstructured.NestedMap(left.Object, "data") rightData, _, _ := unstructured.NestedMap(right.Object, "data") return reflect.DeepEqual(leftData, rightData) - case "AppProject": + case application.AppProjectKind: leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec") rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec") return reflect.DeepEqual(leftSpec, rightSpec) - case "Application": + case application.ApplicationKind: leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec") rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec") leftStatus, _, _ := unstructured.NestedMap(left.Object, "status") diff --git a/cmd/argocd/commands/admin/app.go b/cmd/argocd/commands/admin/app.go index e2e2103e75439..ebdec7f261ffc 100644 --- a/cmd/argocd/commands/admin/app.go +++ b/cmd/argocd/commands/admin/app.go @@ -9,7 +9,6 @@ import ( "sort" "time" - "github.com/ghodss/yaml" "github.com/spf13/cobra" apiv1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -18,15 +17,19 @@ import ( "k8s.io/client-go/kubernetes" kubecache "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/yaml" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" + "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/controller" "github.com/argoproj/argo-cd/v2/controller/cache" "github.com/argoproj/argo-cd/v2/controller/metrics" + "github.com/argoproj/argo-cd/v2/controller/sharding" + argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" appinformers "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions" - argocdclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + reposerverclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/util/argo" cacheutil "github.com/argoproj/argo-cd/v2/util/cache" appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate" @@ -39,17 +42,27 @@ import ( "github.com/argoproj/argo-cd/v2/util/settings" ) -func NewAppCommand() *cobra.Command { +func NewAppCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var command = &cobra.Command{ Use: "app", Short: "Manage applications configuration", + Example: ` +# Compare results of two reconciliations and print diff +argocd admin app diff-reconcile-results APPNAME [flags] + +# Generate declarative config for an application +argocd admin app generate-spec APPNAME + +# Reconcile all applications and store reconciliation summary in the specified file +argocd admin app get-reconcile-results APPNAME +`, Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) }, } command.AddCommand(NewGenAppSpecCommand()) - command.AddCommand(NewReconcileCommand()) + command.AddCommand(NewReconcileCommand(clientOpts)) command.AddCommand(NewDiffReconcileResults()) return command } @@ -193,14 +206,14 @@ func diffReconcileResults(res1 reconcileResults, res2 reconcileResults) error { for k, v := range resMap1 { firstUn, err := toUnstructured(v) if err != nil { - return err + return fmt.Errorf("error converting first resource to unstructured: %w", err) } var secondUn *unstructured.Unstructured second, ok := resMap2[k] if ok { secondUn, err = toUnstructured(second) if err != nil { - return err + return fmt.Errorf("error converting second resource to unstructured: %w", err) } delete(resMap2, k) } @@ -224,13 +237,14 @@ func diffReconcileResults(res1 reconcileResults, res2 reconcileResults) error { return nil } -func NewReconcileCommand() *cobra.Command { +func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( clientConfig clientcmd.ClientConfig selector string repoServerAddress string outputFormat string refresh bool + serverSideDiff bool ) var command = &cobra.Command{ @@ -256,18 +270,27 @@ func NewReconcileCommand() *cobra.Command { var result []appReconcileResult if refresh { + appClientset := appclientset.NewForConfigOrDie(cfg) + kubeClientset := kubernetes.NewForConfigOrDie(cfg) if repoServerAddress == "" { printLine("Repo server is not provided, trying to port-forward to argocd-repo-server pod.") overrides := clientcmd.ConfigOverrides{} - repoServerPort, err := kubeutil.PortForward(8081, namespace, &overrides, "app.kubernetes.io/name=argocd-repo-server") + repoServerName := clientOpts.RepoServerName + repoServerServiceLabelSelector := common.LabelKeyComponentRepoServer + "=" + common.LabelValueComponentRepoServer + repoServerServices, err := kubeClientset.CoreV1().Services(namespace).List(context.Background(), v1.ListOptions{LabelSelector: repoServerServiceLabelSelector}) + errors.CheckError(err) + if len(repoServerServices.Items) > 0 { + if repoServerServicelabel, ok := repoServerServices.Items[0].Labels[common.LabelKeyAppName]; ok && repoServerServicelabel != "" { + repoServerName = repoServerServicelabel + } + } + repoServerPodLabelSelector := common.LabelKeyAppName + "=" + repoServerName + repoServerPort, err := kubeutil.PortForward(8081, namespace, &overrides, repoServerPodLabelSelector) errors.CheckError(err) repoServerAddress = fmt.Sprintf("localhost:%d", repoServerPort) } - repoServerClient := argocdclient.NewRepoServerClientset(repoServerAddress, 60, argocdclient.TLSConfiguration{DisableTLS: false, StrictValidation: false}) - - appClientset := appclientset.NewForConfigOrDie(cfg) - kubeClientset := kubernetes.NewForConfigOrDie(cfg) - result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache) + repoServerClient := reposerverclient.NewRepoServerClientset(repoServerAddress, 60, reposerverclient.TLSConfiguration{DisableTLS: false, StrictValidation: false}) + result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache, serverSideDiff) errors.CheckError(err) } else { appClientset := appclientset.NewForConfigOrDie(cfg) @@ -282,6 +305,7 @@ func NewReconcileCommand() *cobra.Command { command.Flags().StringVar(&selector, "l", "", "Label selector") command.Flags().StringVar(&outputFormat, "o", "yaml", "Output format (yaml|json)") command.Flags().BoolVar(&refresh, "refresh", false, "If set to true then recalculates apps reconciliation") + command.Flags().BoolVar(&serverSideDiff, "server-side-diff", false, "If set to \"true\" will use server-side diff while comparing resources. Default (\"false\")") return command } @@ -328,9 +352,10 @@ func reconcileApplications( kubeClientset kubernetes.Interface, appClientset appclientset.Interface, namespace string, - repoServerClient argocdclient.Clientset, + repoServerClient reposerverclient.Clientset, selector string, createLiveStateCache func(argoDB db.ArgoDB, appInformer kubecache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) cache.LiveStateCache, + serverSideDiff bool, ) ([]appReconcileResult, error) { settingsMgr := settings.NewSettingsManager(ctx, kubeClientset, namespace) argoDB := db.NewDB(namespace, settingsMgr, kubeClientset) @@ -371,7 +396,7 @@ func reconcileApplications( ) appStateManager := controller.NewAppStateManager( - argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking(), false) + argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking(), false, 0, serverSideDiff) appsList, err := appClientset.ArgoprojV1alpha1().Applications(namespace).List(ctx, v1.ListOptions{LabelSelector: selector}) if err != nil { @@ -406,7 +431,10 @@ func reconcileApplications( sources = append(sources, app.Spec.GetSource()) revisions = append(revisions, app.Spec.GetSource().TargetRevision) - res := appStateManager.CompareAppState(&app, proj, revisions, sources, false, false, nil, false) + res, err := appStateManager.CompareAppState(&app, proj, revisions, sources, false, false, nil, false) + if err != nil { + return nil, err + } items = append(items, appReconcileResult{ Name: app.Name, Conditions: app.Status.Conditions, @@ -418,5 +446,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, kubeutil.NewKubectl(), server, func(managedByApp map[string]bool, ref apiv1.ObjectReference) {}, nil, argo.NewResourceTracking()) + return cache.NewLiveStateCache(argoDB, appInformer, settingsMgr, kubeutil.NewKubectl(), server, func(managedByApp map[string]bool, ref apiv1.ObjectReference) {}, &sharding.ClusterSharding{}, argo.NewResourceTracking()) } diff --git a/cmd/argocd/commands/admin/app_test.go b/cmd/argocd/commands/admin/app_test.go index 0cad2485e6696..a0284fe8ffa09 100644 --- a/cmd/argocd/commands/admin/app_test.go +++ b/cmd/argocd/commands/admin/app_test.go @@ -113,6 +113,7 @@ func TestGetReconcileResults_Refresh(t *testing.T) { func(argoDB db.ArgoDB, appInformer cache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) statecache.LiveStateCache { return &liveStateCache }, + false, ) if !assert.NoError(t, err) { diff --git a/cmd/argocd/commands/admin/backup.go b/cmd/argocd/commands/admin/backup.go index 65090ccb9cf71..49e0615c64ba4 100644 --- a/cmd/argocd/commands/admin/backup.go +++ b/cmd/argocd/commands/admin/backup.go @@ -7,7 +7,6 @@ import ( "os" "github.com/argoproj/gitops-engine/pkg/utils/kube" - "github.com/ghodss/yaml" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" apierr "k8s.io/apimachinery/pkg/api/errors" @@ -15,8 +14,10 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/client-go/dynamic" "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/common" + "github.com/argoproj/argo-cd/v2/pkg/apis/application" "github.com/argoproj/argo-cd/v2/util/cli" "github.com/argoproj/argo-cd/v2/util/errors" ) @@ -131,7 +132,6 @@ func NewImportCommand() *cobra.Command { errors.CheckError(err) config.QPS = 100 config.Burst = 50 - errors.CheckError(err) namespace, _, err := clientConfig.Namespace() errors.CheckError(err) acdClients := newArgoCDClientsets(config, namespace) @@ -176,12 +176,12 @@ func NewImportCommand() *cobra.Command { applications, err := acdClients.applications.List(ctx, v1.ListOptions{}) errors.CheckError(err) for _, app := range applications.Items { - pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "Application", Name: app.GetName()}] = app + pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationKind, Name: app.GetName()}] = app } projects, err := acdClients.projects.List(ctx, v1.ListOptions{}) errors.CheckError(err) for _, proj := range projects.Items { - pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "AppProject", Name: proj.GetName()}] = proj + pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.AppProjectKind, Name: proj.GetName()}] = proj } applicationSets, err := acdClients.applicationSets.List(ctx, v1.ListOptions{}) if apierr.IsForbidden(err) || apierr.IsNotFound(err) { @@ -191,7 +191,7 @@ func NewImportCommand() *cobra.Command { } if applicationSets != nil { for _, appSet := range applicationSets.Items { - pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "ApplicationSet", Name: appSet.GetName()}] = appSet + pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationSetKind, Name: appSet.GetName()}] = appSet } } @@ -209,11 +209,11 @@ func NewImportCommand() *cobra.Command { dynClient = acdClients.secrets case "ConfigMap": dynClient = acdClients.configMaps - case "AppProject": + case application.AppProjectKind: dynClient = acdClients.projects - case "Application": + case application.ApplicationKind: dynClient = acdClients.applications - case "ApplicationSet": + case application.ApplicationSetKind: dynClient = acdClients.applicationSets } if !exists { @@ -260,9 +260,9 @@ func NewImportCommand() *cobra.Command { switch key.Kind { case "Secret": dynClient = acdClients.secrets - case "AppProject": + case application.AppProjectKind: dynClient = acdClients.projects - case "Application": + case application.ApplicationKind: dynClient = acdClients.applications if !dryRun { if finalizers := liveObj.GetFinalizers(); len(finalizers) > 0 { @@ -274,7 +274,7 @@ func NewImportCommand() *cobra.Command { } } } - case "ApplicationSet": + case application.ApplicationSetKind: dynClient = acdClients.applicationSets default: log.Fatalf("Unexpected kind '%s' in prune list", key.Kind) @@ -314,7 +314,7 @@ func checkAppHasNoNeedToStopOperation(liveObj unstructured.Unstructured, stopOpe return true } switch liveObj.GetKind() { - case "Application": + case application.ApplicationKind: return liveObj.Object["operation"] == nil } return true @@ -353,9 +353,9 @@ func updateLive(bak, live *unstructured.Unstructured, stopOperation bool) *unstr switch live.GetKind() { case "Secret", "ConfigMap": newLive.Object["data"] = bak.Object["data"] - case "AppProject": + case application.AppProjectKind: newLive.Object["spec"] = bak.Object["spec"] - case "Application": + case application.ApplicationKind: newLive.Object["spec"] = bak.Object["spec"] if _, ok := bak.Object["status"]; ok { newLive.Object["status"] = bak.Object["status"] diff --git a/cmd/argocd/commands/admin/cluster.go b/cmd/argocd/commands/admin/cluster.go index ce59d5401c63b..2e833a68927f4 100644 --- a/cmd/argocd/commands/admin/cluster.go +++ b/cmd/argocd/commands/admin/cluster.go @@ -11,7 +11,7 @@ import ( "time" "github.com/argoproj/gitops-engine/pkg/utils/kube" - "github.com/go-redis/redis/v8" + "github.com/redis/go-redis/v9" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -19,11 +19,13 @@ import ( "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "k8s.io/utils/pointer" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/controller/sharding" - argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" "github.com/argoproj/argo-cd/v2/util/argo" cacheutil "github.com/argoproj/argo-cd/v2/util/cache" @@ -38,10 +40,19 @@ import ( "github.com/argoproj/argo-cd/v2/util/text/label" ) -func NewClusterCommand(pathOpts *clientcmd.PathOptions) *cobra.Command { +func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientcmd.PathOptions) *cobra.Command { var command = &cobra.Command{ Use: "cluster", Short: "Manage clusters configuration", + Example: ` +#Generate declarative config for a cluster +argocd admin cluster generate-spec my-cluster -o yaml + +#Generate a kubeconfig for a cluster named "my-cluster" and display it in the console +argocd admin cluster kubeconfig my-cluster + +#Print information namespaces which Argo CD manages in each cluster +argocd admin cluster namespaces my-cluster `, Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) }, @@ -49,8 +60,8 @@ func NewClusterCommand(pathOpts *clientcmd.PathOptions) *cobra.Command { command.AddCommand(NewClusterConfig()) command.AddCommand(NewGenClusterConfigCommand(pathOpts)) - command.AddCommand(NewClusterStatsCommand()) - command.AddCommand(NewClusterShardsCommand()) + command.AddCommand(NewClusterStatsCommand(clientOpts)) + command.AddCommand(NewClusterShardsCommand(clientOpts)) namespacesCommand := NewClusterNamespacesCommand() namespacesCommand.AddCommand(NewClusterEnableNamespacedMode()) namespacesCommand.AddCommand(NewClusterDisableNamespacedMode()) @@ -60,14 +71,14 @@ func NewClusterCommand(pathOpts *clientcmd.PathOptions) *cobra.Command { } type ClusterWithInfo struct { - argoappv1.Cluster + v1alpha1.Cluster // Shard holds controller shard number that handles the cluster Shard int // Namespaces holds list of namespaces managed by Argo CD in the cluster Namespaces []string } -func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClient *versioned.Clientset, replicas int, namespace string, portForwardRedis bool, cacheSrc func() (*appstatecache.Cache, error), shard int) ([]ClusterWithInfo, error) { +func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClient *versioned.Clientset, replicas int, shardingAlgorithm string, namespace string, portForwardRedis bool, cacheSrc func() (*appstatecache.Cache, error), shard int, redisName string, redisHaProxyName string, redisCompressionStr string) ([]ClusterWithInfo, error) { settingsMgr := settings.NewSettingsManager(ctx, kubeClient, namespace) argoDB := db.NewDB(namespace, settingsMgr, kubeClient) @@ -75,16 +86,30 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie if err != nil { return nil, err } + appItems, err := appClient.ArgoprojV1alpha1().Applications(namespace).List(ctx, v1.ListOptions{}) + if err != nil { + return nil, err + } + clusterShardingCache := sharding.NewClusterSharding(argoDB, shard, replicas, shardingAlgorithm) + clusterShardingCache.Init(clustersList, appItems) + clusterShards := clusterShardingCache.GetDistribution() + var cache *appstatecache.Cache if portForwardRedis { overrides := clientcmd.ConfigOverrides{} + redisHaProxyPodLabelSelector := common.LabelKeyAppName + "=" + redisHaProxyName + redisPodLabelSelector := common.LabelKeyAppName + "=" + redisName port, err := kubeutil.PortForward(6379, namespace, &overrides, - "app.kubernetes.io/name=argocd-redis-ha-haproxy", "app.kubernetes.io/name=argocd-redis") + redisHaProxyPodLabelSelector, redisPodLabelSelector) if err != nil { return nil, err } client := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", port)}) - cache = appstatecache.NewCache(cacheutil.NewCache(cacheutil.NewRedisCache(client, time.Hour, cacheutil.RedisCompressionNone)), time.Hour) + compressionType, err := cacheutil.CompressionTypeFromString(redisCompressionStr) + if err != nil { + return nil, err + } + cache = appstatecache.NewCache(cacheutil.NewCache(cacheutil.NewRedisCache(client, time.Hour, compressionType)), time.Hour) } else { cache, err = cacheSrc() if err != nil { @@ -92,10 +117,6 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie } } - appItems, err := appClient.ArgoprojV1alpha1().Applications(namespace).List(ctx, v1.ListOptions{}) - if err != nil { - return nil, err - } apps := appItems.Items for i, app := range apps { err := argo.ValidateDestination(ctx, &app.Spec.Destination, argoDB) @@ -105,6 +126,7 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie apps[i] = app } clusters := make([]ClusterWithInfo, len(clustersList.Items)) + batchSize := 10 batchesCount := int(math.Ceil(float64(len(clusters)) / float64(batchSize))) for batchNum := 0; batchNum < batchesCount; batchNum++ { @@ -115,12 +137,13 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie } batch := clustersList.Items[batchStart:batchEnd] _ = kube.RunAllAsync(len(batch), func(i int) error { - cluster := batch[i] clusterShard := 0 + cluster := batch[i] if replicas > 0 { - clusterShard = sharding.GetShardByID(cluster.ID, replicas) + clusterShard = clusterShards[cluster.Server] + cluster.Shard = pointer.Int64(int64(clusterShard)) + log.Infof("Cluster with uid: %s will be processed by shard %d", cluster.ID, clusterShard) } - if shard != -1 && clusterShard != shard { return nil } @@ -142,26 +165,29 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie return clusters, nil } -func getControllerReplicas(ctx context.Context, kubeClient *kubernetes.Clientset, namespace string) (int, error) { +func getControllerReplicas(ctx context.Context, kubeClient *kubernetes.Clientset, namespace string, appControllerName string) (int, error) { + appControllerPodLabelSelector := common.LabelKeyAppName + "=" + appControllerName controllerPods, err := kubeClient.CoreV1().Pods(namespace).List(ctx, v1.ListOptions{ - LabelSelector: "app.kubernetes.io/name=argocd-application-controller"}) + LabelSelector: appControllerPodLabelSelector}) if err != nil { return 0, err } return len(controllerPods.Items), nil } -func NewClusterShardsCommand() *cobra.Command { +func NewClusterShardsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - shard int - replicas int - clientConfig clientcmd.ClientConfig - cacheSrc func() (*appstatecache.Cache, error) - portForwardRedis bool + shard int + replicas int + shardingAlgorithm string + clientConfig clientcmd.ClientConfig + cacheSrc func() (*appstatecache.Cache, error) + portForwardRedis bool + redisCompressionStr string ) var command = cobra.Command{ Use: "shards", - Short: "Print information about each controller shard and portion of Kubernetes resources it is responsible for.", + Short: "Print information about each controller shard and the estimated portion of Kubernetes resources it is responsible for.", Run: func(cmd *cobra.Command, args []string) { ctx := cmd.Context() @@ -175,14 +201,13 @@ func NewClusterShardsCommand() *cobra.Command { appClient := versioned.NewForConfigOrDie(clientCfg) if replicas == 0 { - replicas, err = getControllerReplicas(ctx, kubeClient, namespace) + replicas, err = getControllerReplicas(ctx, kubeClient, namespace, clientOpts.AppControllerName) errors.CheckError(err) } if replicas == 0 { return } - - clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard) + clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, shardingAlgorithm, namespace, portForwardRedis, cacheSrc, shard, clientOpts.RedisName, clientOpts.RedisHaProxyName, redisCompressionStr) errors.CheckError(err) if len(clusters) == 0 { return @@ -194,8 +219,16 @@ func NewClusterShardsCommand() *cobra.Command { clientConfig = cli.AddKubectlFlagsToCmd(&command) command.Flags().IntVar(&shard, "shard", -1, "Cluster shard filter") command.Flags().IntVar(&replicas, "replicas", 0, "Application controller replicas count. Inferred from number of running controller pods if not specified") + command.Flags().StringVar(&shardingAlgorithm, "sharding-method", common.DefaultShardingAlgorithm, "Sharding method. Defaults: legacy. Supported sharding methods are : [legacy, round-robin] ") command.Flags().BoolVar(&portForwardRedis, "port-forward-redis", true, "Automatically port-forward ha proxy redis from current namespace?") + cacheSrc = appstatecache.AddCacheFlagsToCmd(&command) + + // parse all added flags so far to get the redis-compression flag that was added by AddCacheFlagsToCmd() above + // we can ignore unchecked error here as the command will be parsed again and checked when command.Execute() is run later + // nolint:errcheck + command.ParseFlags(os.Args[1:]) + redisCompressionStr, _ = command.Flags().GetString(cacheutil.CLIFlagRedisCompress) return &command } @@ -429,17 +462,28 @@ func NewClusterDisableNamespacedMode() *cobra.Command { return &command } -func NewClusterStatsCommand() *cobra.Command { +func NewClusterStatsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - shard int - replicas int - clientConfig clientcmd.ClientConfig - cacheSrc func() (*appstatecache.Cache, error) - portForwardRedis bool + shard int + replicas int + shardingAlgorithm string + clientConfig clientcmd.ClientConfig + cacheSrc func() (*appstatecache.Cache, error) + portForwardRedis bool + redisCompressionStr string ) var command = cobra.Command{ Use: "stats", Short: "Prints information cluster statistics and inferred shard number", + Example: ` +#Display stats and shards for clusters +argocd admin cluster stats + +#Display Cluster Statistics for a Specific Shard +argocd admin cluster stats --shard=1 + +#In a multi-cluster environment to print stats for a specific cluster say(target-cluster) +argocd admin cluster stats target-cluster`, Run: func(cmd *cobra.Command, args []string) { ctx := cmd.Context() @@ -453,10 +497,10 @@ func NewClusterStatsCommand() *cobra.Command { kubeClient := kubernetes.NewForConfigOrDie(clientCfg) appClient := versioned.NewForConfigOrDie(clientCfg) if replicas == 0 { - replicas, err = getControllerReplicas(ctx, kubeClient, namespace) + replicas, err = getControllerReplicas(ctx, kubeClient, namespace, clientOpts.AppControllerName) errors.CheckError(err) } - clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard) + clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, shardingAlgorithm, namespace, portForwardRedis, cacheSrc, shard, clientOpts.RedisName, clientOpts.RedisHaProxyName, redisCompressionStr) errors.CheckError(err) w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) @@ -470,8 +514,15 @@ func NewClusterStatsCommand() *cobra.Command { clientConfig = cli.AddKubectlFlagsToCmd(&command) command.Flags().IntVar(&shard, "shard", -1, "Cluster shard filter") command.Flags().IntVar(&replicas, "replicas", 0, "Application controller replicas count. Inferred from number of running controller pods if not specified") + command.Flags().StringVar(&shardingAlgorithm, "sharding-method", common.DefaultShardingAlgorithm, "Sharding method. Defaults: legacy. Supported sharding methods are : [legacy, round-robin] ") command.Flags().BoolVar(&portForwardRedis, "port-forward-redis", true, "Automatically port-forward ha proxy redis from current namespace?") cacheSrc = appstatecache.AddCacheFlagsToCmd(&command) + + // parse all added flags so far to get the redis-compression flag that was added by AddCacheFlagsToCmd() above + // we can ignore unchecked error here as the command will be parsed again and checked when command.Execute() is run later + // nolint:errcheck + command.ParseFlags(os.Args[1:]) + redisCompressionStr, _ = command.Flags().GetString(cacheutil.CLIFlagRedisCompress) return &command } @@ -484,6 +535,18 @@ func NewClusterConfig() *cobra.Command { Use: "kubeconfig CLUSTER_URL OUTPUT_PATH", Short: "Generates kubeconfig for the specified cluster", DisableAutoGenTag: true, + Example: ` +#Generate a kubeconfig for a cluster named "my-cluster" on console +argocd admin cluster kubeconfig my-cluster + +#Listing available kubeconfigs for clusters managed by argocd +argocd admin cluster kubeconfig + +#Removing a specific kubeconfig file +argocd admin cluster kubeconfig my-cluster --delete + +#Generate a Kubeconfig for a Cluster with TLS Verification Disabled +argocd admin cluster kubeconfig https://cluster-api-url:6443 /path/to/output/kubeconfig.yaml --insecure-skip-tls-verify`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -541,6 +604,11 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command return } + if clusterOpts.InCluster && clusterOpts.ClusterEndpoint != "" { + log.Fatal("Can only use one of --in-cluster or --cluster-endpoint") + return + } + overrides := clientcmd.ConfigOverrides{ Context: *clstContext, } @@ -549,15 +617,16 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command errors.CheckError(err) kubeClientset := fake.NewSimpleClientset() - var awsAuthConf *argoappv1.AWSAuthConfig - var execProviderConf *argoappv1.ExecProviderConfig + var awsAuthConf *v1alpha1.AWSAuthConfig + var execProviderConf *v1alpha1.ExecProviderConfig if clusterOpts.AwsClusterName != "" { - awsAuthConf = &argoappv1.AWSAuthConfig{ + awsAuthConf = &v1alpha1.AWSAuthConfig{ ClusterName: clusterOpts.AwsClusterName, RoleARN: clusterOpts.AwsRoleArn, + Profile: clusterOpts.AwsProfile, } } else if clusterOpts.ExecProviderCommand != "" { - execProviderConf = &argoappv1.ExecProviderConfig{ + execProviderConf = &v1alpha1.ExecProviderConfig{ Command: clusterOpts.ExecProviderCommand, Args: clusterOpts.ExecProviderArgs, Env: clusterOpts.ExecProviderEnv, @@ -580,8 +649,12 @@ func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command errors.CheckError(err) clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, clusterOpts.ClusterResources, conf, bearerToken, awsAuthConf, execProviderConf, labelsMap, annotationsMap) - if clusterOpts.InCluster { - clst.Server = argoappv1.KubernetesInternalAPIServerAddr + if clusterOpts.InClusterEndpoint() { + clst.Server = v1alpha1.KubernetesInternalAPIServerAddr + } + if clusterOpts.ClusterEndpoint == string(cmdutil.KubePublicEndpoint) { + // Ignore `kube-public` cluster endpoints, since this command is intended to run without invoking any network connections. + log.Warn("kube-public cluster endpoints are not supported. Falling back to the endpoint listed in the kubconfig context.") } if clusterOpts.Shard >= 0 { clst.Shard = &clusterOpts.Shard diff --git a/cmd/argocd/commands/admin/dashboard.go b/cmd/argocd/commands/admin/dashboard.go index 05a85c47a0a7b..21b621d264022 100644 --- a/cmd/argocd/commands/admin/dashboard.go +++ b/cmd/argocd/commands/admin/dashboard.go @@ -3,19 +3,25 @@ package admin import ( "fmt" + "github.com/argoproj/argo-cd/v2/util/cli" "github.com/spf13/cobra" + "k8s.io/client-go/tools/clientcmd" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize" "github.com/argoproj/argo-cd/v2/common" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" + "github.com/argoproj/argo-cd/v2/util/cache" + "github.com/argoproj/argo-cd/v2/util/env" "github.com/argoproj/argo-cd/v2/util/errors" ) -func NewDashboardCommand() *cobra.Command { +func NewDashboardCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - port int - address string + port int + address string + compressionStr string + clientConfig clientcmd.ClientConfig ) cmd := &cobra.Command{ Use: "dashboard", @@ -23,13 +29,26 @@ func NewDashboardCommand() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { ctx := cmd.Context() - errors.CheckError(headless.StartLocalServer(ctx, &argocdclient.ClientOptions{Core: true}, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address)) + compression, err := cache.CompressionTypeFromString(compressionStr) + errors.CheckError(err) + clientOpts.Core = true + errors.CheckError(headless.MaybeStartLocalServer(ctx, clientOpts, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address, compression, clientConfig)) println(fmt.Sprintf("Argo CD UI is available at http://%s:%d", address, port)) <-ctx.Done() }, + Example: `# Start the Argo CD Web UI locally on the default port and address +$ argocd admin dashboard + +# Start the Argo CD Web UI locally on a custom port and address +$ argocd admin dashboard --port 8080 --address 127.0.0.1 + +# Start the Argo CD Web UI with GZip compression +$ argocd admin dashboard --redis-compress gzip + `, } - initialize.InitCommand(cmd) + clientConfig = cli.AddKubectlFlagsToSet(cmd.Flags()) cmd.Flags().IntVar(&port, "port", common.DefaultPortAPIServer, "Listen on given port") - cmd.Flags().StringVar(&address, "address", common.DefaultAddressAPIServer, "Listen on given address") + cmd.Flags().StringVar(&address, "address", common.DefaultAddressAdminDashboard, "Listen on given address") + cmd.Flags().StringVar(&compressionStr, "redis-compress", env.StringFromEnv("REDIS_COMPRESSION", string(cache.RedisCompressionGZip)), "Enable this if the application controller is configured with redis compression enabled. (possible values: gzip, none)") return cmd } diff --git a/cmd/argocd/commands/admin/generatespec_utils.go b/cmd/argocd/commands/admin/generatespec_utils.go index 1a7eb8c99a694..f9d902111a5d1 100644 --- a/cmd/argocd/commands/admin/generatespec_utils.go +++ b/cmd/argocd/commands/admin/generatespec_utils.go @@ -8,8 +8,8 @@ import ( "os" "github.com/argoproj/gitops-engine/pkg/utils/kube" - "github.com/ghodss/yaml" v1 "k8s.io/api/core/v1" + "sigs.k8s.io/yaml" ioutil "github.com/argoproj/argo-cd/v2/util/io" ) diff --git a/cmd/argocd/commands/admin/notifications.go b/cmd/argocd/commands/admin/notifications.go index 990df87391940..3cbac0a53b5c2 100644 --- a/cmd/argocd/commands/admin/notifications.go +++ b/cmd/argocd/commands/admin/notifications.go @@ -15,12 +15,13 @@ import ( settings "github.com/argoproj/argo-cd/v2/util/notification/settings" "github.com/argoproj/argo-cd/v2/util/tls" + "github.com/argoproj/argo-cd/v2/pkg/apis/application" "github.com/argoproj/notifications-engine/pkg/cmd" "github.com/spf13/cobra" ) var ( - applications = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"} + applications = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.ApplicationPlural} ) func NewNotificationsCommand() *cobra.Command { @@ -35,7 +36,7 @@ func NewNotificationsCommand() *cobra.Command { "notifications", "argocd admin notifications", applications, - settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm"), func(clientConfig clientcmd.ClientConfig) { + settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm", false), func(clientConfig clientcmd.ClientConfig) { k8sCfg, err := clientConfig.ClientConfig() if err != nil { log.Fatalf("Failed to parse k8s config: %v", err) @@ -64,7 +65,7 @@ func NewNotificationsCommand() *cobra.Command { log.Fatalf("Failed to initialize Argo CD service: %v", err) } }) - toolsCommand.PersistentFlags().StringVar(&argocdRepoServer, "argocd-repo-server", "argocd-repo-server:8081", "Argo CD repo server address") + toolsCommand.PersistentFlags().StringVar(&argocdRepoServer, "argocd-repo-server", common.DefaultRepoServerAddr, "Argo CD repo server address") toolsCommand.PersistentFlags().BoolVar(&argocdRepoServerPlaintext, "argocd-repo-server-plaintext", false, "Use a plaintext client (non-TLS) to connect to repository server") toolsCommand.PersistentFlags().BoolVar(&argocdRepoServerStrictTLS, "argocd-repo-server-strict-tls", false, "Perform strict validation of TLS certificates when connecting to repo server") return toolsCommand diff --git a/cmd/argocd/commands/admin/project.go b/cmd/argocd/commands/admin/project.go index 1364607cebfa8..8d4d5615bc826 100644 --- a/cmd/argocd/commands/admin/project.go +++ b/cmd/argocd/commands/admin/project.go @@ -14,6 +14,7 @@ import ( "github.com/argoproj/argo-cd/v2/util/cli" "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/io" + "github.com/argoproj/argo-cd/v2/util/templates" "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/spf13/cobra" @@ -47,6 +48,17 @@ func NewGenProjectSpecCommand() *cobra.Command { var command = &cobra.Command{ Use: "generate-spec PROJECT", Short: "Generate declarative config for a project", + Example: templates.Examples(` + # Generate a YAML configuration for a project named "myproject" + argocd admin projects generate-spec myproject + + # Generate a JSON configuration for a project named "anotherproject" and specify an output file + argocd admin projects generate-spec anotherproject --output json --file config.json + + # Generate a YAML configuration for a project named "someproject" and write it back to the input file + argocd admin projects generate-spec someproject --inline + `), + Run: func(c *cobra.Command, args []string) { proj, err := cmdutil.ConstructAppProj(fileURL, args, opts, c) errors.CheckError(err) diff --git a/cmd/argocd/commands/admin/project_allowlist.go b/cmd/argocd/commands/admin/project_allowlist.go index 1825a9a1e283f..460ea21d93329 100644 --- a/cmd/argocd/commands/admin/project_allowlist.go +++ b/cmd/argocd/commands/admin/project_allowlist.go @@ -7,7 +7,6 @@ import ( "os" "strings" - "github.com/ghodss/yaml" "github.com/spf13/cobra" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -16,6 +15,7 @@ import ( "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/util/errors" @@ -28,6 +28,8 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" // load the azure plugin (required to authenticate with AKS clusters). _ "k8s.io/client-go/plugin/pkg/client/auth/azure" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) // NewProjectAllowListGenCommand generates a project from clusterRole @@ -39,6 +41,8 @@ func NewProjectAllowListGenCommand() *cobra.Command { var command = &cobra.Command{ Use: "generate-allow-list CLUSTERROLE_PATH PROJ_NAME", Short: "Generates project allow list from the specified clusterRole file", + Example: `# Generates project allow list from the specified clusterRole file +argocd admin proj generate-allow-list /path/to/clusterrole.yaml my-project`, Run: func(c *cobra.Command, args []string) { if len(args) != 2 { c.HelpFunc()(c, args) @@ -151,7 +155,7 @@ func generateProjectAllowList(serverResources []*metav1.APIResourceList, cluster } globalProj := v1alpha1.AppProject{ TypeMeta: metav1.TypeMeta{ - Kind: "AppProject", + Kind: application.AppProjectKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{Name: projName}, diff --git a/cmd/argocd/commands/admin/settings.go b/cmd/argocd/commands/admin/settings.go index 9fda66fa73a43..0274b4a422f09 100644 --- a/cmd/argocd/commands/admin/settings.go +++ b/cmd/argocd/commands/admin/settings.go @@ -12,7 +12,6 @@ import ( "text/tabwriter" healthutil "github.com/argoproj/gitops-engine/pkg/health" - "github.com/ghodss/yaml" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" @@ -21,6 +20,7 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -206,7 +206,7 @@ var validatorsByGroup = map[string]settingValidator{ } ssoProvider = "Dex" } else if general.OIDCConfigRAW != "" { - if _, err := settings.UnmarshalOIDCConfig(general.OIDCConfigRAW); err != nil { + if err := settings.ValidateOIDCConfig(general.OIDCConfigRAW); err != nil { return "", fmt.Errorf("invalid oidc.config: %v", err) } ssoProvider = "OIDC" @@ -233,13 +233,6 @@ var validatorsByGroup = map[string]settingValidator{ _, err := manager.GetGoogleAnalytics() return "", err }), - "plugins": func(manager *settings.SettingsManager) (string, error) { - plugins, err := manager.GetConfigManagementPlugins() - if err != nil { - return "", err - } - return fmt.Sprintf("%d plugins", len(plugins)), nil - }, "kustomize": func(manager *settings.SettingsManager) (string, error) { opts, err := manager.GetKustomizeSettings() if err != nil { @@ -356,6 +349,7 @@ func NewResourceOverridesCommand(cmdCtx commandContext) *cobra.Command { }, } command.AddCommand(NewResourceIgnoreDifferencesCommand(cmdCtx)) + command.AddCommand(NewResourceIgnoreResourceUpdatesCommand(cmdCtx)) command.AddCommand(NewResourceActionListCommand(cmdCtx)) command.AddCommand(NewResourceActionRunCommand(cmdCtx)) command.AddCommand(NewResourceHealthCommand(cmdCtx)) @@ -379,6 +373,27 @@ func executeResourceOverrideCommand(ctx context.Context, cmdCtx commandContext, if gvk.Group != "" { key = fmt.Sprintf("%s/%s", gvk.Group, gvk.Kind) } + override := overrides[key] + callback(res, override, overrides) +} + +func executeIgnoreResourceUpdatesOverrideCommand(ctx context.Context, cmdCtx commandContext, args []string, callback func(res unstructured.Unstructured, override v1alpha1.ResourceOverride, overrides map[string]v1alpha1.ResourceOverride)) { + data, err := os.ReadFile(args[0]) + errors.CheckError(err) + + res := unstructured.Unstructured{} + errors.CheckError(yaml.Unmarshal(data, &res)) + + settingsManager, err := cmdCtx.createSettingsManager(ctx) + errors.CheckError(err) + + overrides, err := settingsManager.GetIgnoreResourceUpdatesOverrides() + errors.CheckError(err) + gvk := res.GroupVersionKind() + key := gvk.Kind + if gvk.Group != "" { + key = fmt.Sprintf("%s/%s", gvk.Group, gvk.Kind) + } override, hasOverride := overrides[key] if !hasOverride { _, _ = fmt.Printf("No overrides configured for '%s/%s'\n", gvk.Group, gvk.Kind) @@ -437,6 +452,52 @@ argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argo return command } +func NewResourceIgnoreResourceUpdatesCommand(cmdCtx commandContext) *cobra.Command { + var command = &cobra.Command{ + Use: "ignore-resource-updates RESOURCE_YAML_PATH", + Short: "Renders fields excluded from resource updates", + Long: "Renders ignored fields using the 'ignoreResourceUpdates' setting specified in the 'resource.customizations' field of 'argocd-cm' ConfigMap", + Example: ` +argocd admin settings resource-overrides ignore-resource-updates ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`, + Run: func(c *cobra.Command, args []string) { + ctx := c.Context() + + if len(args) < 1 { + c.HelpFunc()(c, args) + os.Exit(1) + } + + executeIgnoreResourceUpdatesOverrideCommand(ctx, cmdCtx, args, func(res unstructured.Unstructured, override v1alpha1.ResourceOverride, overrides map[string]v1alpha1.ResourceOverride) { + gvk := res.GroupVersionKind() + if len(override.IgnoreResourceUpdates.JSONPointers) == 0 && len(override.IgnoreResourceUpdates.JQPathExpressions) == 0 { + _, _ = fmt.Printf("Ignore resource updates are not configured for '%s/%s'\n", gvk.Group, gvk.Kind) + return + } + + normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides) + errors.CheckError(err) + + normalizedRes := res.DeepCopy() + logs := collectLogs(func() { + errors.CheckError(normalizer.Normalize(normalizedRes)) + }) + if logs != "" { + _, _ = fmt.Println(logs) + } + + if reflect.DeepEqual(&res, normalizedRes) { + _, _ = fmt.Printf("No fields are ignored by ignoreResourceUpdates settings: \n%s\n", override.IgnoreResourceUpdates) + return + } + + _, _ = fmt.Printf("Following fields are ignored:\n\n") + _ = cli.PrintDiff(res.GetName(), &res, normalizedRes) + }) + }, + } + return command +} + func NewResourceHealthCommand(cmdCtx commandContext) *cobra.Command { var command = &cobra.Command{ Use: "health RESOURCE_YAML_PATH", @@ -454,16 +515,16 @@ argocd admin settings resource-overrides health ./deploy.yaml --argocd-cm-path . executeResourceOverrideCommand(ctx, cmdCtx, args, func(res unstructured.Unstructured, override v1alpha1.ResourceOverride, overrides map[string]v1alpha1.ResourceOverride) { gvk := res.GroupVersionKind() - if override.HealthLua == "" { - _, _ = fmt.Printf("Health script is not configured for '%s/%s'\n", gvk.Group, gvk.Kind) - return - } - resHealth, err := healthutil.GetResourceHealth(&res, lua.ResourceHealthOverrides(overrides)) - errors.CheckError(err) - _, _ = fmt.Printf("STATUS: %s\n", resHealth.Status) - _, _ = fmt.Printf("MESSAGE: %s\n", resHealth.Message) + if err != nil { + errors.CheckError(err) + } else if resHealth == nil { + fmt.Printf("Health script is not configured for '%s/%s'\n", gvk.Group, gvk.Kind) + } else { + _, _ = fmt.Printf("STATUS: %s\n", resHealth.Status) + _, _ = fmt.Printf("MESSAGE: %s\n", resHealth.Message) + } }) }, } @@ -503,7 +564,7 @@ argocd admin settings resource-overrides action list /tmp/deploy.yaml --argocd-c }) w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - _, _ = fmt.Fprintf(w, "NAME\tENABLED\n") + _, _ = fmt.Fprintf(w, "NAME\tDISABLED\n") for _, action := range availableActions { _, _ = fmt.Fprintf(w, "%s\t%s\n", action.Name, strconv.FormatBool(action.Disabled)) } @@ -545,13 +606,26 @@ argocd admin settings resource-overrides action run /tmp/deploy.yaml restart --a modifiedRes, err := luaVM.ExecuteResourceAction(&res, action.ActionLua) errors.CheckError(err) - if reflect.DeepEqual(&res, modifiedRes) { - _, _ = fmt.Printf("No fields had been changed by action: \n%s\n", action.Name) - return + for _, impactedResource := range modifiedRes { + result := impactedResource.UnstructuredObj + switch impactedResource.K8SOperation { + // No default case since a not supported operation would have failed upon unmarshaling earlier + case lua.PatchOperation: + if reflect.DeepEqual(&res, modifiedRes) { + _, _ = fmt.Printf("No fields had been changed by action: \n%s\n", action.Name) + return + } + + _, _ = fmt.Printf("Following fields have been changed:\n\n") + _ = cli.PrintDiff(res.GetName(), &res, result) + case lua.CreateOperation: + yamlBytes, err := yaml.Marshal(impactedResource.UnstructuredObj) + errors.CheckError(err) + fmt.Println("Following resource was created:") + fmt.Println(bytes.NewBuffer(yamlBytes).String()) + } } - _, _ = fmt.Printf("Following fields have been changed:\n\n") - _ = cli.PrintDiff(res.GetName(), &res, modifiedRes) }) }, } diff --git a/cmd/argocd/commands/admin/settings_rbac.go b/cmd/argocd/commands/admin/settings_rbac.go index e52887fe33071..1c09fa0d1cfe7 100644 --- a/cmd/argocd/commands/admin/settings_rbac.go +++ b/cmd/argocd/commands/admin/settings_rbac.go @@ -4,14 +4,15 @@ import ( "context" "fmt" "os" + "strings" - "github.com/ghodss/yaml" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/server/rbacpolicy" @@ -22,38 +23,40 @@ import ( // Provide a mapping of short-hand resource names to their RBAC counterparts var resourceMap map[string]string = map[string]string{ - "account": rbacpolicy.ResourceAccounts, - "app": rbacpolicy.ResourceApplications, - "apps": rbacpolicy.ResourceApplications, - "application": rbacpolicy.ResourceApplications, - "cert": rbacpolicy.ResourceCertificates, - "certs": rbacpolicy.ResourceCertificates, - "certificate": rbacpolicy.ResourceCertificates, - "cluster": rbacpolicy.ResourceClusters, - "gpgkey": rbacpolicy.ResourceGPGKeys, - "key": rbacpolicy.ResourceGPGKeys, - "log": rbacpolicy.ResourceLogs, - "logs": rbacpolicy.ResourceLogs, - "exec": rbacpolicy.ResourceExec, - "proj": rbacpolicy.ResourceProjects, - "projs": rbacpolicy.ResourceProjects, - "project": rbacpolicy.ResourceProjects, - "repo": rbacpolicy.ResourceRepositories, - "repos": rbacpolicy.ResourceRepositories, - "repository": rbacpolicy.ResourceRepositories, + "account": rbacpolicy.ResourceAccounts, + "app": rbacpolicy.ResourceApplications, + "apps": rbacpolicy.ResourceApplications, + "application": rbacpolicy.ResourceApplications, + "applicationsets": rbacpolicy.ResourceApplicationSets, + "cert": rbacpolicy.ResourceCertificates, + "certs": rbacpolicy.ResourceCertificates, + "certificate": rbacpolicy.ResourceCertificates, + "cluster": rbacpolicy.ResourceClusters, + "gpgkey": rbacpolicy.ResourceGPGKeys, + "key": rbacpolicy.ResourceGPGKeys, + "log": rbacpolicy.ResourceLogs, + "logs": rbacpolicy.ResourceLogs, + "exec": rbacpolicy.ResourceExec, + "proj": rbacpolicy.ResourceProjects, + "projs": rbacpolicy.ResourceProjects, + "project": rbacpolicy.ResourceProjects, + "repo": rbacpolicy.ResourceRepositories, + "repos": rbacpolicy.ResourceRepositories, + "repository": rbacpolicy.ResourceRepositories, } // List of allowed RBAC resources var validRBACResources map[string]bool = map[string]bool{ - rbacpolicy.ResourceAccounts: true, - rbacpolicy.ResourceApplications: true, - rbacpolicy.ResourceCertificates: true, - rbacpolicy.ResourceClusters: true, - rbacpolicy.ResourceGPGKeys: true, - rbacpolicy.ResourceLogs: true, - rbacpolicy.ResourceExec: true, - rbacpolicy.ResourceProjects: true, - rbacpolicy.ResourceRepositories: true, + rbacpolicy.ResourceAccounts: true, + rbacpolicy.ResourceApplications: true, + rbacpolicy.ResourceApplicationSets: true, + rbacpolicy.ResourceCertificates: true, + rbacpolicy.ResourceClusters: true, + rbacpolicy.ResourceGPGKeys: true, + rbacpolicy.ResourceLogs: true, + rbacpolicy.ResourceExec: true, + rbacpolicy.ResourceProjects: true, + rbacpolicy.ResourceRepositories: true, } // List of allowed RBAC actions @@ -186,7 +189,6 @@ argocd admin settings rbac can someuser create application 'default/app' --defau } }, } - clientConfig = cli.AddKubectlFlagsToCmd(command) command.Flags().StringVar(&policyFile, "policy-file", "", "path to the policy file to use") command.Flags().StringVar(&defaultRole, "default-role", "", "name of the default role to use") @@ -199,24 +201,55 @@ argocd admin settings rbac can someuser create application 'default/app' --defau // NewRBACValidateCommand returns a new rbac validate command func NewRBACValidateCommand() *cobra.Command { var ( - policyFile string + policyFile string + namespace string + clientConfig clientcmd.ClientConfig ) var command = &cobra.Command{ - Use: "validate --policy-file=POLICYFILE", + Use: "validate [--policy-file POLICYFILE] [--namespace NAMESPACE]", Short: "Validate RBAC policy", Long: ` Validates an RBAC policy for being syntactically correct. The policy must be -a local file, and in either CSV or K8s ConfigMap format. +a local file or a K8s ConfigMap in the provided namespace, and in either CSV or K8s ConfigMap format. +`, + Example: ` +# Check whether a given policy file is valid using a local policy.csv file. +argocd admin settings rbac validate --policy-file policy.csv + +# Policy file can also be K8s config map with data keys like argocd-rbac-cm, +# i.e. 'policy.csv' and (optionally) 'policy.default' +argocd admin settings rbac validate --policy-file argocd-rbac-cm.yaml + +# If --policy-file is not given, and instead --namespace is giventhe ConfigMap 'argocd-rbac-cm' +# from K8s is used. +argocd admin settings rbac validate --namespace argocd + +# Either --policy-file or --namespace must be given. `, Run: func(c *cobra.Command, args []string) { ctx := c.Context() - if policyFile == "" { + if len(args) > 0 { c.HelpFunc()(c, args) - log.Fatalf("Please specify policy to validate using --policy-file") + log.Fatalf("too many arguments") + } + + if (namespace == "" && policyFile == "") || (namespace != "" && policyFile != "") { + c.HelpFunc()(c, args) + log.Fatalf("please provide exactly one of --policy-file or --namespace") + } + + restConfig, err := clientConfig.ClientConfig() + if err != nil { + log.Fatalf("could not get config to create k8s client: %v", err) + } + realClientset, err := kubernetes.NewForConfig(restConfig) + if err != nil { + log.Fatalf("could not create k8s client: %v", err) } - userPolicy, _, _ := getPolicy(ctx, policyFile, nil, "") + + userPolicy, _, _ := getPolicy(ctx, policyFile, realClientset, namespace) if userPolicy != "" { if err := rbac.ValidatePolicy(userPolicy); err == nil { fmt.Printf("Policy is valid.\n") @@ -225,11 +258,15 @@ a local file, and in either CSV or K8s ConfigMap format. fmt.Printf("Policy is invalid: %v\n", err) os.Exit(1) } + } else { + log.Fatalf("Policy is empty or could not be loaded.") } }, } - + clientConfig = cli.AddKubectlFlagsToCmd(command) command.Flags().StringVar(&policyFile, "policy-file", "", "path to the policy file to use") + command.Flags().StringVar(&namespace, "namespace", "", "namespace to get argo rbac configmap from") + return command } @@ -292,11 +329,9 @@ func getPolicyFromConfigMap(cm *corev1.ConfigMap) (string, string, string) { if !ok { userPolicy = "" } - if defaultRole == "" { - defaultRole, ok = cm.Data[rbac.ConfigMapPolicyDefaultKey] - if !ok { - defaultRole = "" - } + defaultRole, ok = cm.Data[rbac.ConfigMapPolicyDefaultKey] + if !ok { + defaultRole = "" } return userPolicy, defaultRole, cm.Data[rbac.ConfigMapMatchModeKey] @@ -373,6 +408,9 @@ func resolveRBACResourceName(name string) string { // isValidRBACAction checks whether a given action is a valid RBAC action func isValidRBACAction(action string) bool { + if strings.HasPrefix(action, rbacpolicy.ActionAction+"/") { + return true + } _, ok := validRBACActions[action] return ok } diff --git a/cmd/argocd/commands/admin/settings_rbac_test.go b/cmd/argocd/commands/admin/settings_rbac_test.go index 93601eed1d303..79835ffd0c14d 100644 --- a/cmd/argocd/commands/admin/settings_rbac_test.go +++ b/cmd/argocd/commands/admin/settings_rbac_test.go @@ -5,15 +5,42 @@ import ( "os" "testing" + "github.com/argoproj/argo-cd/v2/util/assets" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" - - "github.com/argoproj/argo-cd/v2/util/assets" + restclient "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" ) +type FakeClientConfig struct { + clientConfig clientcmd.ClientConfig +} + +func NewFakeClientConfig(clientConfig clientcmd.ClientConfig) *FakeClientConfig { + return &FakeClientConfig{clientConfig: clientConfig} +} + +func (f *FakeClientConfig) RawConfig() (clientcmdapi.Config, error) { + config, err := f.clientConfig.RawConfig() + return config, err +} + +func (f *FakeClientConfig) ClientConfig() (*restclient.Config, error) { + return f.clientConfig.ClientConfig() +} + +func (f *FakeClientConfig) Namespace() (string, bool, error) { + return f.clientConfig.Namespace() +} + +func (f *FakeClientConfig) ConfigAccess() clientcmd.ConfigAccess { + return nil +} + func Test_isValidRBACAction(t *testing.T) { for k := range validRBACActions { t.Run(k, func(t *testing.T) { @@ -27,6 +54,11 @@ func Test_isValidRBACAction(t *testing.T) { }) } +func Test_isValidRBACAction_ActionAction(t *testing.T) { + ok := isValidRBACAction("action/apps/Deployment/restart") + assert.True(t, ok) +} + func Test_isValidRBACResource(t *testing.T) { for k := range validRBACResources { t.Run(k, func(t *testing.T) { @@ -102,6 +134,22 @@ func Test_PolicyFromK8s(t *testing.T) { ok := checkPolicy("role:user", "get", "certificates", ".*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "regex", true) require.False(t, ok) }) + t.Run("get logs", func(t *testing.T) { + ok := checkPolicy("role:test", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) + require.True(t, ok) + }) + t.Run("create exec", func(t *testing.T) { + ok := checkPolicy("role:test", "create", "exec", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) + require.True(t, ok) + }) + t.Run("create applicationsets", func(t *testing.T) { + ok := checkPolicy("role:user", "create", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) + require.True(t, ok) + }) + t.Run("delete applicationsets", func(t *testing.T) { + ok := checkPolicy("role:user", "delete", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true) + require.True(t, ok) + }) } func Test_PolicyFromK8sUsingRegex(t *testing.T) { @@ -111,7 +159,12 @@ func Test_PolicyFromK8sUsingRegex(t *testing.T) { p, role:user, clusters, get, .+, allow p, role:user, clusters, get, https://kubernetes.*, deny p, role:user, applications, get, .*, allow -p, role:user, applications, create, .*/.*, allow` +p, role:user, applications, create, .*/.*, allow +p, role:user, applicationsets, create, .*/.*, allow +p, role:user, applicationsets, delete, .*/.*, allow +p, role:user, logs, get, .*/.*, allow +p, role:user, exec, create, .*/.*, allow +` kubeclientset := fake.NewSimpleClientset(&v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -157,4 +210,36 @@ p, role:, certificates, get, .*, allow` ok := checkPolicy("role:user", "get", "certificates", ".+", builtInPolicy, uPol, dRole, "glob", true) require.False(t, ok) }) + t.Run("get logs via glob match mode", func(t *testing.T) { + ok := checkPolicy("role:user", "get", "logs", ".*/.*", builtInPolicy, uPol, dRole, "glob", true) + require.True(t, ok) + }) + t.Run("create exec", func(t *testing.T) { + ok := checkPolicy("role:user", "create", "exec", ".*/.*", builtInPolicy, uPol, dRole, "regex", true) + require.True(t, ok) + }) + t.Run("create applicationsets", func(t *testing.T) { + ok := checkPolicy("role:user", "create", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true) + require.True(t, ok) + }) + t.Run("delete applicationsets", func(t *testing.T) { + ok := checkPolicy("role:user", "delete", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true) + require.True(t, ok) + }) +} + +func TestNewRBACCanCommand(t *testing.T) { + command := NewRBACCanCommand() + + require.NotNil(t, command) + assert.Equal(t, "can", command.Name()) + assert.Equal(t, "Check RBAC permissions for a role or subject", command.Short) +} + +func TestNewRBACValidateCommand(t *testing.T) { + command := NewRBACValidateCommand() + + require.NotNil(t, command) + assert.Equal(t, "validate", command.Name()) + assert.Equal(t, "Validate RBAC policy", command.Short) } diff --git a/cmd/argocd/commands/admin/settings_test.go b/cmd/argocd/commands/admin/settings_test.go index 696387d0e01fc..ff817017f4be5 100644 --- a/cmd/argocd/commands/admin/settings_test.go +++ b/cmd/argocd/commands/admin/settings_test.go @@ -151,13 +151,6 @@ clientSecret: aaaabbbbccccddddeee`, }, containsSummary: "Dex is configured ('url' field is missing)", }, - "Plugins_ValidConfig": { - validator: "plugins", - data: map[string]string{ - "configManagementPlugins": `[{"name": "test1"}, {"name": "test2"}]`, - }, - containsSummary: "2 plugins", - }, "Kustomize_ModifiedOptions": { validator: "kustomize", containsSummary: "default options", @@ -233,6 +226,29 @@ spec: replicas: 0` ) +const ( + testCustomResourceYAML = `apiVersion: v1 +apiVersion: example.com/v1alpha1 +kind: ExampleResource +metadata: + name: example-resource + labels: + app: example +spec: + replicas: 0` +) + +const ( + testCronJobYAML = `apiVersion: batch/v1 +kind: CronJob +metadata: + name: hello + namespace: test-ns + uid: "123" +spec: + schedule: "* * * * *"` +) + func tempFile(content string) (string, io.Closer, error) { f, err := os.CreateTemp("", "*.yaml") if err != nil { @@ -281,7 +297,7 @@ func TestResourceOverrideIgnoreDifferences(t *testing.T) { assert.NoError(t, err) }) assert.NoError(t, err) - assert.Contains(t, out, "No overrides configured") + assert.Contains(t, out, "Ignore differences are not configured for 'apps/Deployment'\n") }) t.Run("DataIgnored", func(t *testing.T) { @@ -301,7 +317,7 @@ func TestResourceOverrideIgnoreDifferences(t *testing.T) { } func TestResourceOverrideHealth(t *testing.T) { - f, closer, err := tempFile(testDeploymentYAML) + f, closer, err := tempFile(testCustomResourceYAML) if !assert.NoError(t, err) { return } @@ -309,19 +325,34 @@ func TestResourceOverrideHealth(t *testing.T) { t.Run("NoHealthAssessment", func(t *testing.T) { cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ - "resource.customizations": `apps/Deployment: {}`})) + "resource.customizations": `example.com/ExampleResource: {}`})) out, err := captureStdout(func() { cmd.SetArgs([]string{"health", f}) err := cmd.Execute() assert.NoError(t, err) }) assert.NoError(t, err) - assert.Contains(t, out, "Health script is not configured") + assert.Contains(t, out, "Health script is not configured for 'example.com/ExampleResource'\n") }) t.Run("HealthAssessmentConfigured", func(t *testing.T) { cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ - "resource.customizations": `apps/Deployment: + "resource.customizations": `example.com/ExampleResource: + health.lua: | + return { status = "Progressing" } +`})) + out, err := captureStdout(func() { + cmd.SetArgs([]string{"health", f}) + err := cmd.Execute() + assert.NoError(t, err) + }) + assert.NoError(t, err) + assert.Contains(t, out, "Progressing") + }) + + t.Run("HealthAssessmentConfiguredWildcard", func(t *testing.T) { + cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ + "resource.customizations": `example.com/*: health.lua: | return { status = "Progressing" } `})) @@ -342,6 +373,12 @@ func TestResourceOverrideAction(t *testing.T) { } defer utils.Close(closer) + cronJobFile, closer, err := tempFile(testCronJobYAML) + if !assert.NoError(t, err) { + return + } + defer utils.Close(closer) + t.Run("NoActions", func(t *testing.T) { cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ "resource.customizations": `apps/Deployment: {}`})) @@ -354,7 +391,7 @@ func TestResourceOverrideAction(t *testing.T) { assert.Contains(t, out, "Actions are not configured") }) - t.Run("ActionConfigured", func(t *testing.T) { + t.Run("OldStyleActionConfigured", func(t *testing.T) { cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ "resource.customizations": `apps/Deployment: actions: | @@ -383,9 +420,55 @@ func TestResourceOverrideAction(t *testing.T) { assert.NoError(t, err) }) assert.NoError(t, err) - assert.Contains(t, out, `NAME ENABLED + assert.Contains(t, out, `NAME DISABLED restart false resume false `) }) + + t.Run("NewStyleActionConfigured", func(t *testing.T) { + cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ + "resource.customizations": `batch/CronJob: + actions: | + discovery.lua: | + actions = {} + actions["create-a-job"] = {["disabled"] = false} + return actions + definitions: + - name: test + action.lua: | + job1 = {} + job1.apiVersion = "batch/v1" + job1.kind = "Job" + job1.metadata = {} + job1.metadata.name = "hello-1" + job1.metadata.namespace = "obj.metadata.namespace" + impactedResource1 = {} + impactedResource1.operation = "create" + impactedResource1.resource = job1 + result = {} + result[1] = impactedResource1 + return result +`})) + out, err := captureStdout(func() { + cmd.SetArgs([]string{"run-action", cronJobFile, "test"}) + err := cmd.Execute() + assert.NoError(t, err) + }) + assert.NoError(t, err) + assert.Contains(t, out, "resource was created:") + assert.Contains(t, out, "hello-1") + + out, err = captureStdout(func() { + cmd.SetArgs([]string{"list-actions", cronJobFile}) + err := cmd.Execute() + assert.NoError(t, err) + }) + + assert.NoError(t, err) + assert.Contains(t, out, "NAME") + assert.Contains(t, out, "DISABLED") + assert.Contains(t, out, "create-a-job") + assert.Contains(t, out, "false") + }) } diff --git a/cmd/argocd/commands/admin/testdata/rbac/argocd-rbac-cm.yaml b/cmd/argocd/commands/admin/testdata/rbac/argocd-rbac-cm.yaml index 06cb30e8df665..bf947fb8b7110 100644 --- a/cmd/argocd/commands/admin/testdata/rbac/argocd-rbac-cm.yaml +++ b/cmd/argocd/commands/admin/testdata/rbac/argocd-rbac-cm.yaml @@ -8,6 +8,8 @@ data: p, role:user, applications, create, */*, allow p, role:user, applications, delete, *, allow p, role:user, applications, delete, */guestbook, deny + p, role:user, applicationsets, create, */*, allow + p, role:user, applicationsets, delete, */*, allow p, role:user, logs, get, */*, allow g, test, role:user policy.default: role:unknown diff --git a/cmd/argocd/commands/admin/testdata/rbac/policy.csv b/cmd/argocd/commands/admin/testdata/rbac/policy.csv index a92060ec3b4fe..b18d0904f5f60 100644 --- a/cmd/argocd/commands/admin/testdata/rbac/policy.csv +++ b/cmd/argocd/commands/admin/testdata/rbac/policy.csv @@ -5,6 +5,8 @@ p, role:user, applications, get, *, allow p, role:user, applications, create, */*, allow p, role:user, applications, delete, *, allow p, role:user, applications, delete, */guestbook, deny +p, role:user, applicationsets, create, */*, allow +p, role:user, applicationsets, delete, */*, allow p, role:test, certificates, get, *, allow p, role:test, logs, get, */*, allow p, role:test, exec, create, */*, allow diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index b2a4c6f13933b..3c0f1e7ad672b 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -19,7 +19,6 @@ import ( "github.com/argoproj/gitops-engine/pkg/sync/hook" "github.com/argoproj/gitops-engine/pkg/sync/ignore" "github.com/argoproj/gitops-engine/pkg/utils/kube" - "github.com/ghodss/yaml" grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" "github.com/mattn/go-isatty" log "github.com/sirupsen/logrus" @@ -29,19 +28,18 @@ import ( "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + k8swatch "k8s.io/apimachinery/pkg/watch" "k8s.io/utils/pointer" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" - argocommon "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/controller" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" - applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster" projectpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/project" "github.com/argoproj/argo-cd/v2/pkg/apiclient/settings" - settingspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/settings" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" repoapiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/reposerver/repository" @@ -53,6 +51,7 @@ import ( "github.com/argoproj/argo-cd/v2/util/grpc" argoio "github.com/argoproj/argo-cd/v2/util/io" "github.com/argoproj/argo-cd/v2/util/manifeststream" + "github.com/argoproj/argo-cd/v2/util/templates" "github.com/argoproj/argo-cd/v2/util/text/label" ) @@ -94,6 +93,8 @@ func NewApplicationCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman command.AddCommand(NewApplicationResourceActionsCommand(clientOpts)) command.AddCommand(NewApplicationListResourcesCommand(clientOpts)) command.AddCommand(NewApplicationLogsCommand(clientOpts)) + command.AddCommand(NewApplicationAddSourceCommand(clientOpts)) + command.AddCommand(NewApplicationRemoveSourceCommand(clientOpts)) return command } @@ -103,6 +104,7 @@ type watchOpts struct { operation bool suspended bool degraded bool + delete bool } // NewApplicationCreateCommand returns a new instance of an `argocd app create` command @@ -135,13 +137,15 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra. # 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 + # Create a MultiSource app while yaml file contains an application with multiple sources + argocd app create guestbook --file + # Create a app using a custom tool: argocd app create kasane --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`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() argocdClient := headless.NewClientOrDie(clientOpts, c) - apps, err := cmdutil.ConstructApps(fileURL, appName, labels, annotations, args, appOpts, c.Flags()) errors.CheckError(err) @@ -150,9 +154,6 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra. c.HelpFunc()(c, args) os.Exit(1) } - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning) - } if appNamespace != "" { app.Namespace = appNamespace } @@ -161,15 +162,17 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra. } conn, appIf := argocdClient.NewApplicationClientOrDie() defer argoio.Close(conn) - appCreateRequest := applicationpkg.ApplicationCreateRequest{ + appCreateRequest := application.ApplicationCreateRequest{ Application: app, Upsert: &upsert, Validate: &appOpts.Validate, } // Get app before creating to see if it is being updated or no change - existing, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &app.Name}) - if grpc.UnwrapGRPCStatus(err).Code() != codes.NotFound { + existing, err := appIf.Get(ctx, &application.ApplicationQuery{Name: &app.Name}) + unwrappedError := grpc.UnwrapGRPCStatus(err).Code() + // As part of the fix for CVE-2022-41354, the API will return Permission Denied when an app does not exist. + if unwrappedError != codes.NotFound && unwrappedError != codes.PermissionDenied { errors.CheckError(err) } @@ -263,6 +266,52 @@ func hasAppChanged(appReq, appRes *argoappv1.Application, upsert bool) bool { return true } +func parentChildDetails(appIf application.ApplicationServiceClient, ctx context.Context, appName string, appNs string) (map[string]argoappv1.ResourceNode, map[string][]string, map[string]struct{}) { + + mapUidToNode := make(map[string]argoappv1.ResourceNode) + mapParentToChild := make(map[string][]string) + parentNode := make(map[string]struct{}) + + resourceTree, err := appIf.ResourceTree(ctx, &application.ResourcesQuery{Name: &appName, AppNamespace: &appNs, ApplicationName: &appName}) + errors.CheckError(err) + + for _, node := range resourceTree.Nodes { + mapUidToNode[node.UID] = node + + if len(node.ParentRefs) > 0 { + _, ok := mapParentToChild[node.ParentRefs[0].UID] + if !ok { + var temp []string + mapParentToChild[node.ParentRefs[0].UID] = temp + } + mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) + } else { + parentNode[node.UID] = struct{}{} + } + } + return mapUidToNode, mapParentToChild, parentNode +} + +func printHeader(acdClient argocdclient.Client, app *argoappv1.Application, ctx context.Context, windows *argoappv1.SyncWindows, showOperation bool, showParams bool) { + aURL := appURL(ctx, acdClient, app.Name) + printAppSummaryTable(app, aURL, windows) + + if len(app.Status.Conditions) > 0 { + fmt.Println() + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + printAppConditions(w, app) + _ = w.Flush() + fmt.Println() + } + if showOperation && app.Status.OperationState != nil { + fmt.Println() + printOperationResult(app.Status.OperationState) + } + if !app.Spec.HasMultipleSources() && showParams { + printParams(app) + } +} + // NewApplicationGetCommand returns a new instance of an `argocd app get` command func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( @@ -271,13 +320,42 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com output string showParams bool showOperation bool + appNamespace string ) var command = &cobra.Command{ Use: "get APPNAME", Short: "Get application details", + Example: templates.Examples(` + # Get basic details about the application "my-app" in wide format + argocd app get my-app -o wide + + # Get detailed information about the application "my-app" in YAML format + argocd app get my-app -o yaml + + # Get details of the application "my-app" in JSON format + argocd get my-app -o json + + # Get application details and include information about the current operation + argocd app get my-app --show-operation + + # Show application parameters and overrides + argocd app get my-app --show-params + + # Refresh application data when retrieving + argocd app get my-app --refresh + + # Perform a hard refresh, including refreshing application data and target manifests cache + argocd app get my-app --hard-refresh + + # Get application details and display them in a tree format + argocd app get my-app --output tree + + # Get application details and display them in a detailed tree format + argocd app get my-app --output tree=detailed + `), + Run: func(c *cobra.Command, args []string) { ctx := c.Context() - if len(args) == 0 { c.HelpFunc()(c, args) os.Exit(1) @@ -286,17 +364,15 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com conn, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) - appName, appNs := argo.ParseAppQualifiedName(args[0], "") - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + + app, err := appIf.Get(ctx, &application.ApplicationQuery{ Name: &appName, Refresh: getRefreshType(refresh, hardRefresh), AppNamespace: &appNs, }) - errors.CheckError(err) - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning) - } + errors.CheckError(err) pConn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie() defer argoio.Close(pConn) @@ -310,39 +386,38 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com err := PrintResource(app, output) errors.CheckError(err) case "wide", "": - aURL := appURL(ctx, acdClient, app.Name) - printAppSummaryTable(app, aURL, windows) - - if len(app.Status.Conditions) > 0 { + printHeader(acdClient, app, ctx, windows, showOperation, showParams) + if len(app.Status.Resources) > 0 { fmt.Println() w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - printAppConditions(w, app) + printAppResources(w, app) _ = w.Flush() - fmt.Println() } - if showOperation && app.Status.OperationState != nil { + case "tree": + printHeader(acdClient, app, ctx, windows, showOperation, showParams) + mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState := resourceParentChild(ctx, acdClient, appName, appNs) + if len(mapUidToNode) > 0 { fmt.Println() - printOperationResult(app.Status.OperationState) - } - if showParams { - printParams(app) + printTreeView(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) } - if len(app.Status.Resources) > 0 { + case "tree=detailed": + printHeader(acdClient, app, ctx, windows, showOperation, showParams) + mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState := resourceParentChild(ctx, acdClient, appName, appNs) + if len(mapUidToNode) > 0 { fmt.Println() - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - printAppResources(w, app) - _ = w.Flush() + printTreeViewDetailed(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) } default: errors.CheckError(fmt.Errorf("unknown output format: %s", output)) } }, } - command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide") + command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|tree") command.Flags().BoolVar(&showOperation, "show-operation", false, "Show application operation") command.Flags().BoolVar(&showParams, "show-params", false, "Show application parameters and overrides") 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().StringVarP(&appNamespace, "app-namespace", "N", "", "Only get application from namespace") return command } @@ -364,6 +439,44 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co var command = &cobra.Command{ Use: "logs APPNAME", Short: "Get logs of application pods", + Example: templates.Examples(` + # Get logs of pods associated with the application "my-app" + argocd app logs my-app + + # Get logs of pods associated with the application "my-app" in a specific resource group + argocd app logs my-app --group my-group + + # Get logs of pods associated with the application "my-app" in a specific resource kind + argocd app logs my-app --kind my-kind + + # Get logs of pods associated with the application "my-app" in a specific namespace + argocd app logs my-app --namespace my-namespace + + # Get logs of pods associated with the application "my-app" for a specific resource name + argocd app logs my-app --name my-resource + + # Stream logs in real-time for the application "my-app" + argocd app logs my-app -f + + # Get the last N lines of logs for the application "my-app" + argocd app logs my-app --tail 100 + + # Get logs since a specified number of seconds ago + argocd app logs my-app --since-seconds 3600 + + # Get logs until a specified time (format: "2023-10-10T15:30:00Z") + argocd app logs my-app --until-time "2023-10-10T15:30:00Z" + + # Filter logs to show only those containing a specific string + argocd app logs my-app --filter "error" + + # Get logs for a specific container within the pods + argocd app logs my-app -c my-container + + # Get previously terminated container logs + argocd app logs my-app -p + `), + Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -374,12 +487,12 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co acdClient := headless.NewClientOrDie(clientOpts, c) conn, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + appName, appNs := argo.ParseFromQualifiedName(args[0], "") retry := true for retry { retry = false - stream, err := appIf.PodLogs(ctx, &applicationpkg.ApplicationPodLogsQuery{ + stream, err := appIf.PodLogs(ctx, &application.ApplicationPodLogsQuery{ Name: &appName, Group: &group, Namespace: pointer.String(namespace), @@ -419,8 +532,8 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } else { return } - } //Done with receive message - } //Done with retry + } // Done with receive message + } // Done with retry }, } @@ -428,28 +541,31 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co command.Flags().StringVar(&kind, "kind", "", "Resource kind") command.Flags().StringVar(&namespace, "namespace", "", "Resource namespace") command.Flags().StringVar(&resourceName, "name", "", "Resource name") - command.Flags().BoolVar(&follow, "follow", false, "Specify if the logs should be streamed") + command.Flags().BoolVarP(&follow, "follow", "f", false, "Specify if the logs should be streamed") command.Flags().Int64Var(&tail, "tail", 0, "The number of lines from the end of the logs to show") command.Flags().Int64Var(&sinceSeconds, "since-seconds", 0, "A relative time in seconds before the current time from which to show logs") command.Flags().StringVar(&untilTime, "until-time", "", "Show logs until this time") command.Flags().StringVar(&filter, "filter", "", "Show logs contain this string") - command.Flags().StringVar(&container, "container", "", "Optional container name") + command.Flags().StringVarP(&container, "container", "c", "", "Optional container name") command.Flags().BoolVarP(&previous, "previous", "p", false, "Specify if the previously terminated container logs should be returned") return command } func printAppSummaryTable(app *argoappv1.Application, appURL string, windows *argoappv1.SyncWindows) { - source := app.Spec.GetSource() fmt.Printf(printOpFmtStr, "Name:", app.QualifiedName()) fmt.Printf(printOpFmtStr, "Project:", app.Spec.GetProject()) fmt.Printf(printOpFmtStr, "Server:", getServer(app)) fmt.Printf(printOpFmtStr, "Namespace:", app.Spec.Destination.Namespace) fmt.Printf(printOpFmtStr, "URL:", appURL) - fmt.Printf(printOpFmtStr, "Repo:", source.RepoURL) - fmt.Printf(printOpFmtStr, "Target:", source.TargetRevision) - fmt.Printf(printOpFmtStr, "Path:", source.Path) - printAppSourceDetails(&source) + if !app.Spec.HasMultipleSources() { + fmt.Println("Source:") + } else { + fmt.Println("Sources:") + } + for _, source := range app.Spec.GetSources() { + printAppSourceDetails(&source) + } var wds []string var status string var allow, deny, inactiveAllows bool @@ -497,7 +613,7 @@ func printAppSummaryTable(app *argoappv1.Application, appURL string, windows *ar syncPolicy += " (Prune)" } } else { - syncPolicy = "" + syncPolicy = "Manual" } fmt.Printf(printOpFmtStr, "Sync Policy:", syncPolicy) syncStatusStr := string(app.Status.Sync.Status) @@ -519,11 +635,19 @@ func printAppSummaryTable(app *argoappv1.Application, appURL string, windows *ar } func printAppSourceDetails(appSrc *argoappv1.ApplicationSource) { + fmt.Printf(printOpFmtStr, "- Repo:", appSrc.RepoURL) + fmt.Printf(printOpFmtStr, " Target:", appSrc.TargetRevision) + if appSrc.Path != "" { + fmt.Printf(printOpFmtStr, " Path:", appSrc.Path) + } + if appSrc.Ref != "" { + fmt.Printf(printOpFmtStr, " Ref:", appSrc.Ref) + } if appSrc.Helm != nil && len(appSrc.Helm.ValueFiles) > 0 { - fmt.Printf(printOpFmtStr, "Helm Values:", strings.Join(appSrc.Helm.ValueFiles, ",")) + fmt.Printf(printOpFmtStr, " Helm Values:", strings.Join(appSrc.Helm.ValueFiles, ",")) } if appSrc.Kustomize != nil && appSrc.Kustomize.NamePrefix != "" { - fmt.Printf(printOpFmtStr, "Name Prefix:", appSrc.Kustomize.NamePrefix) + fmt.Printf(printOpFmtStr, " Name Prefix:", appSrc.Kustomize.NamePrefix) } } @@ -554,7 +678,7 @@ func appURLDefault(acdClient argocdclient.Client, appName string) string { func appURL(ctx context.Context, acdClient argocdclient.Client, appName string) string { conn, settingsIf := acdClient.NewSettingsClientOrDie() defer argoio.Close(conn) - argoSettings, err := settingsIf.Get(ctx, &settingspkg.SettingsQuery{}) + argoSettings, err := settingsIf.Get(ctx, &settings.SettingsQuery{}) errors.CheckError(err) if argoSettings.URL != "" { @@ -606,11 +730,33 @@ func getServer(app *argoappv1.Application) string { // NewApplicationSetCommand returns a new instance of an `argocd app set` command func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - appOpts cmdutil.AppOptions + appOpts cmdutil.AppOptions + appNamespace string + sourceIndex int ) var command = &cobra.Command{ Use: "set APPNAME", Short: "Set application parameters", + Example: templates.Examples(` + # Set application parameters for the application "my-app" + argocd app set my-app --parameter key1=value1 --parameter key2=value2 + + # Set and validate application parameters for "my-app" + argocd app set my-app --parameter key1=value1 --parameter key2=value2 --validate + + # Set and override application parameters with JSON or YAML file + argocd app set my-app --from-file path/to/parameters.json + + # Set and override application parameters with a parameter file + argocd app set my-app --parameter-file path/to/parameter-file.yaml + + # Set and override application parameters for a source at index 1 under spec.sources of app my-app. source-index starts at 1. + argocd app set my-app --source-index 1 --repo https://github.com/argoproj/argocd-example-apps.git + + # Set application parameters and specify the namespace + argocd app set my-app --parameter key1=value1 --parameter key2=value2 --namespace my-namespace + `), + Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -618,26 +764,33 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) argocdClient := headless.NewClientOrDie(clientOpts, c) conn, appIf := argocdClient.NewApplicationClientOrDie() defer argoio.Close(conn) - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName, AppNamespace: &appNs}) + app, err := appIf.Get(ctx, &application.ApplicationQuery{Name: &appName, AppNamespace: &appNs}) errors.CheckError(err) - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning) + if app.Spec.HasMultipleSources() { + if sourceIndex <= 0 { + errors.CheckError(fmt.Errorf("Source index should be specified and greater than 0 for applications with multiple sources")) + } + if len(app.Spec.GetSources()) < sourceIndex { + errors.CheckError(fmt.Errorf("Source index should be less than the number of sources in the application")) + } } - visited := cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts) + // sourceIndex startes with 1, thus, it needs to be decreased by 1 to find the correct index in the list of sources + sourceIndex = sourceIndex - 1 + visited := cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts, sourceIndex) if visited == 0 { log.Error("Please set at least one option to update") c.HelpFunc()(c, args) os.Exit(1) } - setParameterOverrides(app, appOpts.Parameters) - _, err = appIf.UpdateSpec(ctx, &applicationpkg.ApplicationUpdateSpecRequest{ + setParameterOverrides(app, appOpts.Parameters, sourceIndex) + _, err = appIf.UpdateSpec(ctx, &application.ApplicationUpdateSpecRequest{ Name: &app.Name, Spec: &app.Spec, Validate: &appOpts.Validate, @@ -646,7 +799,9 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com errors.CheckError(err) }, } + command.Flags().IntVar(&sourceIndex, "source-index", -1, "Index of the source from the list of sources of the app. Index starts at 1.") cmdutil.AddAppFlags(command, &appOpts) + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Set application parameters in namespace") return command } @@ -655,28 +810,49 @@ type unsetOpts struct { namePrefix bool nameSuffix bool kustomizeVersion bool + kustomizeNamespace bool kustomizeImages []string + kustomizeReplicas []string parameters []string valuesFiles []string valuesLiteral bool ignoreMissingValueFiles bool pluginEnvs []string passCredentials bool + ref bool +} + +// IsZero returns true when the Application options for kustomize are considered empty +func (o *unsetOpts) KustomizeIsZero() bool { + return o == nil || + !o.namePrefix && + !o.nameSuffix && + !o.kustomizeVersion && + !o.kustomizeNamespace && + len(o.kustomizeImages) == 0 && + len(o.kustomizeReplicas) == 0 } // NewApplicationUnsetCommand returns a new instance of an `argocd app unset` command func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { + var ( + sourceIndex int + ) appOpts := cmdutil.AppOptions{} opts := unsetOpts{} + var appNamespace string var command = &cobra.Command{ Use: "unset APPNAME parameters", Short: "Unset application parameters", Example: ` # Unset kustomize override kustomize image argocd app unset my-app --kustomize-image=alpine - # Unset kustomize override prefix + # Unset kustomize override suffix argocd app unset my-app --namesuffix + # Unset kustomize override suffix for source at index 1 under spec.sources of app my-app. source-index starts at 1. + argocd app unset my-app --source-index 1 --namesuffix + # Unset parameter override argocd app unset my-app -p COMPONENT=PARAM`, @@ -687,18 +863,25 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + + appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName, AppNamespace: &appNs}) + app, err := appIf.Get(ctx, &application.ApplicationQuery{Name: &appName, AppNamespace: &appNs}) errors.CheckError(err) - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning) + if app.Spec.HasMultipleSources() { + if sourceIndex <= 0 { + errors.CheckError(fmt.Errorf("Source index should be specified and greater than 0 for applications with multiple sources")) + } + if len(app.Spec.GetSources()) < sourceIndex { + errors.CheckError(fmt.Errorf("Source index should be less than the number of sources in the application")) + } } - source := app.Spec.GetSource() - updated, nothingToUnset := unset(&source, opts) + source := app.Spec.GetSourcePtr(sourceIndex) + + updated, nothingToUnset := unset(source, opts) if nothingToUnset { c.HelpFunc()(c, args) os.Exit(1) @@ -707,8 +890,8 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C return } - cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts) - _, err = appIf.UpdateSpec(ctx, &applicationpkg.ApplicationUpdateSpecRequest{ + cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts, sourceIndex) + _, err = appIf.UpdateSpec(ctx, &application.ApplicationUpdateSpecRequest{ Name: &app.Name, Spec: &app.Spec, Validate: &appOpts.Validate, @@ -717,6 +900,7 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C errors.CheckError(err) }, } + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Unset application parameters in namespace") command.Flags().StringArrayVarP(&opts.parameters, "parameter", "p", []string{}, "Unset a parameter override (e.g. -p guestbook=image)") command.Flags().StringArrayVar(&opts.valuesFiles, "values", []string{}, "Unset one or more Helm values files") command.Flags().BoolVar(&opts.valuesLiteral, "values-literal", false, "Unset literal Helm values block") @@ -724,16 +908,27 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C command.Flags().BoolVar(&opts.nameSuffix, "namesuffix", false, "Kustomize namesuffix") command.Flags().BoolVar(&opts.namePrefix, "nameprefix", false, "Kustomize nameprefix") command.Flags().BoolVar(&opts.kustomizeVersion, "kustomize-version", false, "Kustomize version") + command.Flags().BoolVar(&opts.kustomizeNamespace, "kustomize-namespace", false, "Kustomize namespace") command.Flags().StringArrayVar(&opts.kustomizeImages, "kustomize-image", []string{}, "Kustomize images name (e.g. --kustomize-image node --kustomize-image mysql)") + command.Flags().StringArrayVar(&opts.kustomizeReplicas, "kustomize-replica", []string{}, "Kustomize replicas name (e.g. --kustomize-replica my-deployment --kustomize-replica my-statefulset)") command.Flags().StringArrayVar(&opts.pluginEnvs, "plugin-env", []string{}, "Unset plugin env variables (e.g --plugin-env name)") command.Flags().BoolVar(&opts.passCredentials, "pass-credentials", false, "Unset passCredentials") + command.Flags().BoolVar(&opts.ref, "ref", false, "Unset ref on the source") + command.Flags().IntVar(&sourceIndex, "source-index", -1, "Index of the source from the list of sources of the app. Index starts at 1.") return command } func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, nothingToUnset bool) { + needToUnsetRef := false + if opts.ref && source.Ref != "" { + source.Ref = "" + updated = true + needToUnsetRef = true + } + if source.Kustomize != nil { - if !opts.namePrefix && !opts.nameSuffix && !opts.kustomizeVersion && len(opts.kustomizeImages) == 0 { - return false, true + if opts.KustomizeIsZero() { + return updated, !needToUnsetRef } if opts.namePrefix && source.Kustomize.NamePrefix != "" { @@ -751,11 +946,16 @@ func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, n source.Kustomize.Version = "" } + if opts.kustomizeNamespace && source.Kustomize.Namespace != "" { + updated = true + source.Kustomize.Namespace = "" + } + for _, kustomizeImage := range opts.kustomizeImages { for i, item := range source.Kustomize.Images { if argoappv1.KustomizeImage(kustomizeImage).Match(item) { updated = true - //remove i + // remove i a := source.Kustomize.Images copy(a[i:], a[i+1:]) // Shift a[i+1:] left one index. a[len(a)-1] = "" // Erase last element (write zero value). @@ -764,10 +964,21 @@ func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, n } } } + + for _, kustomizeReplica := range opts.kustomizeReplicas { + kustomizeReplicas := source.Kustomize.Replicas + for i, item := range kustomizeReplicas { + if kustomizeReplica == item.Name { + source.Kustomize.Replicas = append(kustomizeReplicas[0:i], kustomizeReplicas[i+1:]...) + updated = true + break + } + } + } } if source.Helm != nil { if len(opts.parameters) == 0 && len(opts.valuesFiles) == 0 && !opts.valuesLiteral && !opts.ignoreMissingValueFiles && !opts.passCredentials { - return false, true + return updated, !needToUnsetRef } for _, paramStr := range opts.parameters { helmParams := source.Helm.Parameters @@ -779,9 +990,11 @@ func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, n } } } - if opts.valuesLiteral && source.Helm.Values != "" { - source.Helm.Values = "" - updated = true + if opts.valuesLiteral && !source.Helm.ValuesIsEmpty() { + err := source.Helm.SetValuesString("") + if err == nil { + updated = true + } } for _, valuesFile := range opts.valuesFiles { specValueFiles := source.Helm.ValueFiles @@ -802,9 +1015,10 @@ func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, n updated = true } } + if source.Plugin != nil { if len(opts.pluginEnvs) == 0 { - return false, true + return false, !needToUnsetRef } for _, env := range opts.pluginEnvs { err := source.Plugin.RemoveEnvEntry(env) @@ -829,9 +1043,9 @@ func targetObjects(resources []*argoappv1.ResourceDiff) ([]*unstructured.Unstruc return objs, nil } -func getLocalObjects(ctx context.Context, app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions, - configManagementPlugins []*argoappv1.ConfigManagementPlugin, trackingMethod string) []*unstructured.Unstructured { - manifestStrings := getLocalObjectsString(ctx, app, local, localRepoRoot, appLabelKey, kubeVersion, apiVersions, kustomizeOptions, configManagementPlugins, trackingMethod) +func getLocalObjects(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions, + trackingMethod string) []*unstructured.Unstructured { + manifestStrings := getLocalObjectsString(ctx, app, proj, local, localRepoRoot, appLabelKey, kubeVersion, apiVersions, kustomizeOptions, trackingMethod) objs := make([]*unstructured.Unstructured, len(manifestStrings)) for i := range manifestStrings { obj := unstructured.Unstructured{} @@ -842,20 +1056,21 @@ func getLocalObjects(ctx context.Context, app *argoappv1.Application, local, loc return objs } -func getLocalObjectsString(ctx context.Context, app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions, - configManagementPlugins []*argoappv1.ConfigManagementPlugin, trackingMethod string) []string { +func getLocalObjectsString(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions, + trackingMethod string) []string { source := app.Spec.GetSource() res, err := repository.GenerateManifests(ctx, local, localRepoRoot, source.TargetRevision, &repoapiclient.ManifestRequest{ - Repo: &argoappv1.Repository{Repo: source.RepoURL}, - AppLabelKey: appLabelKey, - AppName: app.Name, - Namespace: app.Spec.Destination.Namespace, - ApplicationSource: &source, - KustomizeOptions: kustomizeOptions, - KubeVersion: kubeVersion, - ApiVersions: apiVersions, - Plugins: configManagementPlugins, - TrackingMethod: trackingMethod, + Repo: &argoappv1.Repository{Repo: source.RepoURL}, + AppLabelKey: appLabelKey, + AppName: app.Name, + Namespace: app.Spec.Destination.Namespace, + ApplicationSource: &source, + KustomizeOptions: kustomizeOptions, + KubeVersion: kubeVersion, + ApiVersions: apiVersions, + TrackingMethod: trackingMethod, + ProjectName: proj.Name, + ProjectSourceRepos: proj.Spec.SourceRepos, }, true, &git.NoopCredsStore{}, resource.MustParse("0"), nil) errors.CheckError(err) @@ -909,12 +1124,13 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co localRepoRoot string serverSideGenerate bool localIncludes []string + appNamespace string ) shortDesc := "Perform a diff against the target and live state." var command = &cobra.Command{ Use: "diff APPNAME", Short: shortDesc, - Long: shortDesc + "\nUses 'diff' to render the difference. KUBECTL_EXTERNAL_DIFF environment variable can be used to select your own diff tool.\nReturns the following exit codes: 2 on general errors, 1 when a diff is found, and 0 when no diff is found", + Long: shortDesc + "\nUses 'diff' to render the difference. KUBECTL_EXTERNAL_DIFF environment variable can be used to select your own diff tool.\nReturns the following exit codes: 2 on general errors, 1 when a diff is found, and 0 when no diff is found\nKubernetes Secrets are ignored from this diff.", Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -925,27 +1141,23 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co clientset := headless.NewClientOrDie(clientOpts, c) conn, appIf := clientset.NewApplicationClientOrDie() defer argoio.Close(conn) - appName, appNs := argo.ParseAppQualifiedName(args[0], "") - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + app, err := appIf.Get(ctx, &application.ApplicationQuery{ Name: &appName, Refresh: getRefreshType(refresh, hardRefresh), AppNamespace: &appNs, }) errors.CheckError(err) - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning) - } - - resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName, AppNamespace: &appNs}) + resources, err := appIf.ManagedResources(ctx, &application.ResourcesQuery{ApplicationName: &appName, AppNamespace: &appNs}) errors.CheckError(err) conn, settingsIf := clientset.NewSettingsClientOrDie() defer argoio.Close(conn) - argoSettings, err := settingsIf.Get(ctx, &settingspkg.SettingsQuery{}) + argoSettings, err := settingsIf.Get(ctx, &settings.SettingsQuery{}) errors.CheckError(err) diffOption := &DifferenceOption{} if revision != "" { - q := applicationpkg.ApplicationManifestQuery{ + q := application.ApplicationManifestQuery{ Name: &appName, Revision: &revision, AppNamespace: &appNs, @@ -967,17 +1179,19 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co diffOption.serversideRes = res } else { - fmt.Fprintf(os.Stderr, "Warning: local diff without --server-side-generate is deprecated and does not work with plugins. Server-side generation will be the default in v2.6.") + fmt.Fprintf(os.Stderr, "Warning: local diff without --server-side-generate is deprecated and does not work with plugins. Server-side generation will be the default in v2.7.") conn, clusterIf := clientset.NewClusterClientOrDie() defer argoio.Close(conn) cluster, err := clusterIf.Get(ctx, &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server}) errors.CheckError(err) + diffOption.local = local diffOption.localRepoRoot = localRepoRoot diffOption.cluster = cluster } } - foundDiffs := findandPrintDiff(ctx, app, resources, argoSettings, appName, diffOption) + proj := getProject(c, clientOpts, ctx, app.Spec.Project) + foundDiffs := findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption) if foundDiffs && exitCode { os.Exit(1) } @@ -991,6 +1205,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co command.Flags().StringVar(&localRepoRoot, "local-repo-root", "/", "Path to the repository root. Used together with --local allows setting the repository root") command.Flags().BoolVar(&serverSideGenerate, "server-side-generate", false, "Used with --local, this will send your manifests to the server for diffing") command.Flags().StringArrayVar(&localIncludes, "local-include", []string{"*.yaml", "*.yml", "*.json"}, "Used with --server-side-generate, specify patterns of filenames to send. Matching is based on filename and not path.") + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Only render the difference in namespace") return command } @@ -1005,14 +1220,14 @@ type DifferenceOption struct { } // findandPrintDiff ... Prints difference between application current state and state stored in git or locally, returns boolean as true if difference is found else returns false -func findandPrintDiff(ctx context.Context, app *argoappv1.Application, resources *applicationpkg.ManagedResourcesResponse, argoSettings *settingspkg.Settings, appName string, diffOptions *DifferenceOption) bool { +func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, resources *application.ManagedResourcesResponse, argoSettings *settings.Settings, diffOptions *DifferenceOption) bool { var foundDiffs bool liveObjs, err := cmdutil.LiveObjects(resources.Items) errors.CheckError(err) items := make([]objKeyLiveTarget, 0) if diffOptions.local != "" { - localObjs := groupObjsByKey(getLocalObjects(ctx, app, diffOptions.local, diffOptions.localRepoRoot, argoSettings.AppLabelKey, diffOptions.cluster.Info.ServerVersion, diffOptions.cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins, argoSettings.TrackingMethod), liveObjs, app.Spec.Destination.Namespace) - items = groupObjsForDiff(resources, localObjs, items, argoSettings, app.InstanceName(argoSettings.ControllerNamespace)) + localObjs := groupObjsByKey(getLocalObjects(ctx, app, proj, diffOptions.local, diffOptions.localRepoRoot, argoSettings.AppLabelKey, diffOptions.cluster.Info.ServerVersion, diffOptions.cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod), liveObjs, app.Spec.Destination.Namespace) + items = groupObjsForDiff(resources, localObjs, items, argoSettings, app.InstanceName(argoSettings.ControllerNamespace), app.Spec.Destination.Namespace) } else if diffOptions.revision != "" { var unstructureds []*unstructured.Unstructured for _, mfst := range diffOptions.res.Manifests { @@ -1021,7 +1236,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, resources unstructureds = append(unstructureds, obj) } groupedObjs := groupObjsByKey(unstructureds, liveObjs, app.Spec.Destination.Namespace) - items = groupObjsForDiff(resources, groupedObjs, items, argoSettings, app.Name) + items = groupObjsForDiff(resources, groupedObjs, items, argoSettings, app.InstanceName(argoSettings.ControllerNamespace), app.Spec.Destination.Namespace) } else if diffOptions.serversideRes != nil { var unstructureds []*unstructured.Unstructured for _, mfst := range diffOptions.serversideRes.Manifests { @@ -1030,7 +1245,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, resources unstructureds = append(unstructureds, obj) } groupedObjs := groupObjsByKey(unstructureds, liveObjs, app.Spec.Destination.Namespace) - items = groupObjsForDiff(resources, groupedObjs, items, argoSettings, app.Name) + items = groupObjsForDiff(resources, groupedObjs, items, argoSettings, app.InstanceName(argoSettings.ControllerNamespace), app.Spec.Destination.Namespace) } else { for i := range resources.Items { res := resources.Items[i] @@ -1090,7 +1305,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, resources return foundDiffs } -func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[kube.ResourceKey]*unstructured.Unstructured, items []objKeyLiveTarget, argoSettings *settings.Settings, appName string) []objKeyLiveTarget { +func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[kube.ResourceKey]*unstructured.Unstructured, items []objKeyLiveTarget, argoSettings *settings.Settings, appName, namespace string) []objKeyLiveTarget { resourceTracking := argo.NewResourceTracking() for _, res := range resources.Items { var live = &unstructured.Unstructured{} @@ -1105,7 +1320,7 @@ func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[ } if local, ok := objs[key]; ok || live != nil { if local != nil && !kube.IsCRD(local) { - err = resourceTracking.SetAppInstance(local, argoSettings.AppLabelKey, appName, "", argoappv1.TrackingMethod(argoSettings.GetTrackingMethod())) + err = resourceTracking.SetAppInstance(local, argoSettings.AppLabelKey, appName, namespace, argoappv1.TrackingMethod(argoSettings.GetTrackingMethod())) errors.CheckError(err) } @@ -1131,6 +1346,8 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. noPrompt bool propagationPolicy string selector string + wait bool + appNamespace string ) var command = &cobra.Command{ Use: "delete APPNAME", @@ -1154,7 +1371,8 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. c.HelpFunc()(c, args) os.Exit(1) } - conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() + acdClient := headless.NewClientOrDie(clientOpts, c) + conn, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) var isTerminal bool = isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) var isConfirmAll bool = false @@ -1172,8 +1390,8 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. } for _, appFullName := range appNames { - appName, appNs := argo.ParseAppQualifiedName(appFullName, "") - appDeleteReq := applicationpkg.ApplicationDeleteRequest{ + appName, appNs := argo.ParseFromQualifiedName(appFullName, appNamespace) + appDeleteReq := application.ApplicationDeleteRequest{ Name: &appName, AppNamespace: &appNs, } @@ -1201,6 +1419,9 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. if lowercaseAnswer == "y" { _, err := appIf.Delete(ctx, &appDeleteReq) errors.CheckError(err) + if wait { + checkForDeleteEvent(ctx, acdClient, appFullName) + } fmt.Printf("application '%s' deleted\n", appFullName) } else { fmt.Println("The command to delete '" + appFullName + "' was cancelled.") @@ -1208,6 +1429,10 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. } else { _, err := appIf.Delete(ctx, &appDeleteReq) errors.CheckError(err) + + if wait { + checkForDeleteEvent(ctx, acdClient, appFullName) + } } } }, @@ -1216,9 +1441,20 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. command.Flags().StringVarP(&propagationPolicy, "propagation-policy", "p", "foreground", "Specify propagation policy for deletion of application's resources. One of: foreground|background") command.Flags().BoolVarP(&noPrompt, "yes", "y", false, "Turn off prompting to confirm cascaded deletion of application resources") command.Flags().StringVarP(&selector, "selector", "l", "", "Delete all apps with matching label. Supports '=', '==', '!=', in, notin, exists & not exists. Matching apps must satisfy all of the specified label constraints.") + command.Flags().BoolVar(&wait, "wait", false, "Wait until deletion of the application(s) completes") + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Namespace where the application will be deleted from") return command } +func checkForDeleteEvent(ctx context.Context, acdClient argocdclient.Client, appFullName string) { + appEventCh := acdClient.WatchApplicationWithRetry(ctx, appFullName, "") + for appEvent := range appEventCh { + if appEvent.Type == k8swatch.Deleted { + return + } + } +} + // Print simple list of application names func printApplicationNames(apps []argoappv1.Application) { for _, app := range apps { @@ -1284,7 +1520,7 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - apps, err := appIf.List(ctx, &applicationpkg.ApplicationQuery{ + apps, err := appIf.List(ctx, &application.ApplicationQuery{ Selector: pointer.String(selector), AppNamespace: &appNamespace, }) @@ -1301,17 +1537,6 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co if cluster != "" { appList = argo.FilterByCluster(appList, cluster) } - var appsWithDeprecatedPlugins []string - for _, app := range appList { - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - appsWithDeprecatedPlugins = append(appsWithDeprecatedPlugins, app.Name) - } - } - - if len(appsWithDeprecatedPlugins) > 0 { - log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning) - log.Warnf("The following Applications use deprecated plugins: %s", strings.Join(appsWithDeprecatedPlugins, ", ")) - } switch output { case "yaml", "json": @@ -1337,7 +1562,7 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co func formatSyncPolicy(app argoappv1.Application) string { if app.Spec.SyncPolicy == nil || app.Spec.SyncPolicy.Automated == nil { - return "" + return "Manual" } policy := "Auto" if app.Spec.SyncPolicy.Automated.Prune { @@ -1441,10 +1666,12 @@ func getWatchOpts(watch watchOpts) watchOpts { // NewApplicationWaitCommand returns a new instance of an `argocd app wait` command func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - watch watchOpts - timeout uint - selector string - resources []string + watch watchOpts + timeout uint + selector string + resources []string + output string + appNamespace string ) var command = &cobra.Command{ Use: "wait [APPNAME.. | -l selector]", @@ -1486,14 +1713,18 @@ func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co closer, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(closer) if selector != "" { - list, err := appIf.List(ctx, &applicationpkg.ApplicationQuery{Selector: pointer.String(selector)}) + list, err := appIf.List(ctx, &application.ApplicationQuery{Selector: pointer.String(selector)}) errors.CheckError(err) for _, i := range list.Items { - appNames = append(appNames, i.Name) + appNames = append(appNames, i.QualifiedName()) } } for _, appName := range appNames { - _, err := waitOnApplicationStatus(ctx, acdClient, appName, timeout, watch, selectedResources) + // Construct QualifiedName + if appNamespace != "" && !strings.Contains(appName, "/") { + appName = appNamespace + "/" + appName + } + _, _, err := waitOnApplicationStatus(ctx, acdClient, appName, timeout, watch, selectedResources, output) errors.CheckError(err) } }, @@ -1502,10 +1733,13 @@ func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co command.Flags().BoolVar(&watch.health, "health", false, "Wait for health") command.Flags().BoolVar(&watch.suspended, "suspended", false, "Wait for suspended") command.Flags().BoolVar(&watch.degraded, "degraded", false, "Wait for degraded") + command.Flags().BoolVar(&watch.delete, "delete", false, "Wait for delete") command.Flags().StringVarP(&selector, "selector", "l", "", "Wait for apps by label. Supports '=', '==', '!=', in, notin, exists & not exists. Matching apps must satisfy all of the specified label constraints.") command.Flags().StringArrayVar(&resources, "resource", []string{}, fmt.Sprintf("Sync only specific resources as GROUP%[1]sKIND%[1]sNAME or %[2]sGROUP%[1]sKIND%[1]sNAME. Fields may be blank and '*' can be used. This option may be specified repeatedly", resourceFieldDelimiter, resourceExcludeIndicator)) command.Flags().BoolVar(&watch.operation, "operation", false, "Wait for pending operations") command.Flags().UintVar(&timeout, "timeout", defaultCheckTimeoutSeconds, "Time out after this many seconds") + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Only wait for an application in namespace") + command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|tree|tree=detailed") return command } @@ -1517,6 +1751,24 @@ func printAppResources(w io.Writer, app *argoappv1.Application) { } } +func printTreeView(nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, mapNodeNameToResourceState map[string]*resourceState) { + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + _, _ = fmt.Fprintf(w, "KIND/NAME\tSTATUS\tHEALTH\tMESSAGE\n") + for uid := range parentNodes { + treeViewAppGet("", nodeMapping, parentChildMapping, nodeMapping[uid], mapNodeNameToResourceState, w) + } + _ = w.Flush() +} + +func printTreeViewDetailed(nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, mapNodeNameToResourceState map[string]*resourceState) { + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + fmt.Fprintf(w, "KIND/NAME\tSTATUS\tHEALTH\tAGE\tMESSAGE\tREASON\n") + for uid := range parentNodes { + detailedTreeViewAppGet("", nodeMapping, parentChildMapping, nodeMapping[uid], mapNodeNameToResourceState, w) + } + _ = w.Flush() +} + // NewApplicationSyncCommand returns a new instance of an `argocd app sync` command func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( @@ -1531,6 +1783,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co force bool replace bool serverSideApply bool + applyOutOfSyncOnly bool async bool retryLimit int64 retryBackoffDuration time.Duration @@ -1542,6 +1795,8 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co diffChanges bool diffChangesConfirm bool projects []string + output string + appNamespace string ) var command = &cobra.Command{ Use: "sync [APPNAME... | -l selector | --project project-name]", @@ -1570,17 +1825,13 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co argocd app sync my-app --resource argoproj.io:Rollout:my-namespace/my-rollout`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() - if len(args) == 0 && selector == "" && len(projects) == 0 { - c.HelpFunc()(c, args) os.Exit(1) } - if len(args) > 1 && selector != "" { log.Fatal("Cannot use selector option when application name(s) passed as argument(s)") } - acdClient := headless.NewClientOrDie(clientOpts, c) conn, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) @@ -1590,7 +1841,10 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co appNames := args if selector != "" || len(projects) > 0 { - list, err := appIf.List(ctx, &applicationpkg.ApplicationQuery{Selector: pointer.String(selector), Projects: projects}) + list, err := appIf.List(ctx, &application.ApplicationQuery{ + Selector: pointer.String(selector), + AppNamespace: &appNamespace, + Projects: projects}) errors.CheckError(err) // unlike list, we'd want to fail if nothing was found @@ -1611,10 +1865,14 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } for _, appQualifiedName := range appNames { - appName, appNs := argo.ParseAppQualifiedName(appQualifiedName, "") + // Construct QualifiedName + if appNamespace != "" && !strings.Contains(appQualifiedName, "/") { + appQualifiedName = appNamespace + "/" + appQualifiedName + } + appName, appNs := argo.ParseFromQualifiedName(appQualifiedName, "") if len(selectedLabels) > 0 { - q := applicationpkg.ApplicationManifestQuery{ + q := application.ApplicationManifestQuery{ Name: &appName, AppNamespace: &appNs, Revision: &revision, @@ -1625,6 +1883,8 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co log.Fatal(err) } + fmt.Println("The name of the app is ", appName) + for _, mfst := range res.Manifests { obj, err := argoappv1.UnmarshalToUnstructured(mfst) errors.CheckError(err) @@ -1650,15 +1910,22 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co var localObjsStrings []string diffOption := &DifferenceOption{} - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + app, err := appIf.Get(ctx, &application.ApplicationQuery{ Name: &appName, AppNamespace: &appNs, }) errors.CheckError(err) if app.Spec.HasMultipleSources() { - log.Fatal("argocd cli does not work on multi-source app") - return + if revision != "" { + log.Fatal("argocd cli does not work on multi-source app with --revision flag") + return + } + + if local != "" { + log.Fatal("argocd cli does not work on multi-source app with --local flag") + return + } } // filters out only those resources that needs to be synced @@ -1670,17 +1937,13 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } if local != "" { - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning) - } - if app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.Automated != nil && !dryRun { log.Fatal("Cannot use local sync when Automatic Sync Policy is enabled except with --dry-run") } errors.CheckError(err) conn, settingsIf := acdClient.NewSettingsClientOrDie() - argoSettings, err := settingsIf.Get(ctx, &settingspkg.SettingsQuery{}) + argoSettings, err := settingsIf.Get(ctx, &settings.SettingsQuery{}) errors.CheckError(err) argoio.Close(conn) @@ -1689,15 +1952,17 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co cluster, err := clusterIf.Get(ctx, &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server}) errors.CheckError(err) argoio.Close(conn) - localObjsStrings = getLocalObjectsString(ctx, app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.Info.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins, argoSettings.TrackingMethod) + + proj := getProject(c, clientOpts, ctx, app.Spec.Project) + localObjsStrings = getLocalObjectsString(ctx, app, proj.Project, local, localRepoRoot, argoSettings.AppLabelKey, cluster.Info.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod) errors.CheckError(err) diffOption.local = local diffOption.localRepoRoot = localRepoRoot diffOption.cluster = cluster } - syncOptionsFactory := func() *applicationpkg.SyncOptions { - syncOptions := applicationpkg.SyncOptions{} + syncOptionsFactory := func() *application.SyncOptions { + syncOptions := application.SyncOptions{} items := make([]string, 0) if replace { items = append(items, common.SyncOptionReplace) @@ -1705,6 +1970,9 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co if serverSideApply { items = append(items, common.SyncOptionServerSideApply) } + if applyOutOfSyncOnly { + items = append(items, common.SyncOptionApplyOutOfSyncOnly) + } if len(items) == 0 { // for prevent send even empty array if not need @@ -1714,7 +1982,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co return &syncOptions } - syncReq := applicationpkg.ApplicationSyncRequest{ + syncReq := application.ApplicationSyncRequest{ Name: &appName, AppNamespace: &appNs, DryRun: &dryRun, @@ -1742,27 +2010,25 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co Backoff: &argoappv1.Backoff{ Duration: retryBackoffDuration.String(), MaxDuration: retryBackoffMaxDuration.String(), - Factor: pointer.Int64Ptr(retryBackoffFactor), + Factor: pointer.Int64(retryBackoffFactor), }, } } if diffChanges { - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning) - } - - resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ + resources, err := appIf.ManagedResources(ctx, &application.ResourcesQuery{ ApplicationName: &appName, AppNamespace: &appNs, }) errors.CheckError(err) conn, settingsIf := acdClient.NewSettingsClientOrDie() defer argoio.Close(conn) - argoSettings, err := settingsIf.Get(ctx, &settingspkg.SettingsQuery{}) + argoSettings, err := settingsIf.Get(ctx, &settings.SettingsQuery{}) errors.CheckError(err) foundDiffs := false fmt.Printf("====== Previewing differences between live and desired state of application %s ======\n", appQualifiedName) - foundDiffs = findandPrintDiff(ctx, app, resources, argoSettings, appQualifiedName, diffOption) + + proj := getProject(c, clientOpts, ctx, app.Spec.Project) + foundDiffs = findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption) if foundDiffs { if !diffChangesConfirm { yesno := cli.AskToProceed(fmt.Sprintf("Please review changes to application %s shown above. Do you want to continue the sync process? (y/n): ", appQualifiedName)) @@ -1778,15 +2044,15 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co errors.CheckError(err) if !async { - app, err := waitOnApplicationStatus(ctx, acdClient, appQualifiedName, timeout, watchOpts{operation: true}, selectedResources) + app, opState, err := waitOnApplicationStatus(ctx, acdClient, appQualifiedName, timeout, watchOpts{operation: true}, selectedResources, output) errors.CheckError(err) if !dryRun { - if !app.Status.OperationState.Phase.Successful() { - log.Fatalf("Operation has completed with phase: %s", app.Status.OperationState.Phase) + if !opState.Phase.Successful() { + log.Fatalf("Operation has completed with phase: %s", opState.Phase) } else if len(selectedResources) == 0 && app.Status.Sync.Status != argoappv1.SyncStatusCodeSynced { // Only get resources to be pruned if sync was application-wide and final status is not synced - pruningRequired := app.Status.OperationState.SyncResult.Resources.PruningRequired() + pruningRequired := opState.SyncResult.Resources.PruningRequired() if pruningRequired > 0 { log.Fatalf("%d resources require pruning", pruningRequired) } @@ -1811,6 +2077,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co command.Flags().BoolVar(&force, "force", false, "Use a force apply") command.Flags().BoolVar(&replace, "replace", false, "Use a kubectl create/replace instead apply") command.Flags().BoolVar(&serverSideApply, "server-side", false, "Use server-side apply while syncing the application") + command.Flags().BoolVar(&applyOutOfSyncOnly, "apply-out-of-sync-only", false, "Sync only out-of-sync resources") command.Flags().BoolVar(&async, "async", false, "Do not wait for application to sync before continuing") command.Flags().StringVar(&local, "local", "", "Path to a local directory. When this flag is present no git queries will be made") command.Flags().StringVar(&localRepoRoot, "local-repo-root", "/", "Path to the repository root. Used together with --local allows setting the repository root") @@ -1818,13 +2085,15 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co command.Flags().BoolVar(&diffChangesConfirm, "assumeYes", false, "Assume yes as answer for all user queries or prompts") command.Flags().BoolVar(&diffChanges, "preview-changes", false, "Preview difference against the target and live state before syncing app and wait for user confirmation") command.Flags().StringArrayVar(&projects, "project", []string{}, "Sync apps that belong to the specified projects. This option may be specified repeatedly.") + command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|tree|tree=detailed") + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Only sync an application in namespace") return command } -func getAppNamesBySelector(ctx context.Context, appIf applicationpkg.ApplicationServiceClient, selector string) ([]string, error) { +func getAppNamesBySelector(ctx context.Context, appIf application.ApplicationServiceClient, selector string) ([]string, error) { appNames := []string{} if selector != "" { - list, err := appIf.List(ctx, &applicationpkg.ApplicationQuery{Selector: pointer.String(selector)}) + list, err := appIf.List(ctx, &application.ApplicationQuery{Selector: pointer.String(selector)}) if err != nil { return []string{}, err } @@ -1833,7 +2102,7 @@ func getAppNamesBySelector(ctx context.Context, appIf applicationpkg.Application return []string{}, fmt.Errorf("no apps match selector %v", selector) } for _, i := range list.Items { - appNames = append(appNames, i.Name) + appNames = append(appNames, i.QualifiedName()) } } return appNames, nil @@ -1969,6 +2238,9 @@ func groupResourceStates(app *argoappv1.Application, selectedResources []*argoap // check if resource health, sync and operation statuses matches watch options func checkResourceStatus(watch watchOpts, healthStatus string, syncStatus string, operationStatus *argoappv1.Operation) bool { + if watch.delete { + return false + } healthCheckPassed := true if watch.suspended && watch.health && watch.degraded { @@ -1981,7 +2253,7 @@ func checkResourceStatus(watch watchOpts, healthStatus string, syncStatus string } else if watch.degraded && watch.health { healthCheckPassed = healthStatus == string(health.HealthStatusHealthy) || healthStatus == string(health.HealthStatusDegraded) - //below are good + // below are good } else if watch.suspended && watch.health { healthCheckPassed = healthStatus == string(health.HealthStatusHealthy) || healthStatus == string(health.HealthStatusSuspended) @@ -1998,9 +2270,26 @@ func checkResourceStatus(watch watchOpts, healthStatus string, syncStatus string return synced && healthCheckPassed && operational } +// resourceParentChild gets the latest state of the app and the latest state of the app's resource tree and then +// constructs the necessary data structures to print the app as a tree. +func resourceParentChild(ctx context.Context, acdClient argocdclient.Client, appName string, appNs string) (map[string]argoappv1.ResourceNode, map[string][]string, map[string]struct{}, map[string]*resourceState) { + _, appIf := acdClient.NewApplicationClientOrDie() + mapUidToNode, mapParentToChild, parentNode := parentChildDetails(appIf, ctx, appName, appNs) + app, err := appIf.Get(ctx, &application.ApplicationQuery{Name: pointer.String(appName), AppNamespace: pointer.String(appNs)}) + errors.CheckError(err) + mapNodeNameToResourceState := make(map[string]*resourceState) + for _, res := range getResourceStates(app, nil) { + mapNodeNameToResourceState[res.Kind+"/"+res.Name] = res + } + return mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState +} + const waitFormatString = "%s\t%5s\t%10s\t%10s\t%20s\t%8s\t%7s\t%10s\t%s\n" -func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, appName string, timeout uint, watch watchOpts, selectedResources []*argoappv1.SyncOperationResource) (*argoappv1.Application, error) { +// waitOnApplicationStatus watches an application and blocks until either the desired watch conditions +// are fulfilled or we reach the timeout. Returns the app once desired conditions have been filled. +// Additionally return the operationState at time of fulfilment (which may be different than returned app). +func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, appName string, timeout uint, watch watchOpts, selectedResources []*argoappv1.SyncOperationResource, output string) (*argoappv1.Application, *argoappv1.OperationState, error) { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -2009,14 +2298,14 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, // time when the sync status lags behind when an operation completes refresh := false - appRealName, appNs := argo.ParseAppQualifiedName(appName, "") + appRealName, appNs := argo.ParseFromQualifiedName(appName, "") printFinalStatus := func(app *argoappv1.Application) *argoappv1.Application { var err error if refresh { conn, appClient := acdClient.NewApplicationClientOrDie() refreshType := string(argoappv1.RefreshTypeNormal) - app, err = appClient.Get(ctx, &applicationpkg.ApplicationQuery{ + app, err = appClient.Get(ctx, &application.ApplicationQuery{ Name: &appRealName, Refresh: &refreshType, AppNamespace: &appNs, @@ -2032,18 +2321,49 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, printOperationResult(app.Status.OperationState) } - if len(app.Status.Resources) > 0 { - fmt.Println() - w := tabwriter.NewWriter(os.Stdout, 5, 0, 2, ' ', 0) - printAppResources(w, app) - _ = w.Flush() + switch output { + case "yaml", "json": + err := PrintResource(app, output) + errors.CheckError(err) + case "wide", "": + if len(app.Status.Resources) > 0 { + fmt.Println() + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + printAppResources(w, app) + _ = w.Flush() + } + case "tree": + mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState := resourceParentChild(ctx, acdClient, appName, appNs) + if len(mapUidToNode) > 0 { + fmt.Println() + printTreeView(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) + } + case "tree=detailed": + mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState := resourceParentChild(ctx, acdClient, appName, appNs) + if len(mapUidToNode) > 0 { + fmt.Println() + printTreeViewDetailed(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) + } + default: + errors.CheckError(fmt.Errorf("unknown output format: %s", output)) } return app } if timeout != 0 { time.AfterFunc(time.Duration(timeout)*time.Second, func() { + _, appClient := acdClient.NewApplicationClientOrDie() + app, err := appClient.Get(ctx, &application.ApplicationQuery{ + Name: &appRealName, + AppNamespace: &appNs, + }) + errors.CheckError(err) + fmt.Println() + fmt.Println("This is the state of the app after `wait` timed out:") + printFinalStatus(app) cancel() + fmt.Println() + fmt.Println("The command timed out waiting for the conditions to be met.") }) } @@ -2053,16 +2373,32 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, prevStates := make(map[string]*resourceState) conn, appClient := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) - app, err := appClient.Get(ctx, &applicationpkg.ApplicationQuery{ + app, err := appClient.Get(ctx, &application.ApplicationQuery{ Name: &appRealName, AppNamespace: &appNs, }) errors.CheckError(err) + + // printFinalStatus() will refresh and update the app object, potentially causing the app's + // status.operationState to be different than the version when we break out of the event loop. + // This means the app.status is unreliable for determining the final state of the operation. + // finalOperationState captures the operationState as it was seen when we met the conditions of + // the wait, so the caller can rely on it to determine the outcome of the operation. + // See: https://github.com/argoproj/argo-cd/issues/5592 + finalOperationState := app.Status.OperationState + appEventCh := acdClient.WatchApplicationWithRetry(ctx, appName, app.ResourceVersion) for appEvent := range appEventCh { app = &appEvent.Application + finalOperationState = app.Status.OperationState operationInProgress := false + + if watch.delete && appEvent.Type == k8swatch.Deleted { + fmt.Printf("Application '%s' deleted\n", app.QualifiedName()) + return nil, nil, nil + } + // consider the operation is in progress if app.Operation != nil { // if it just got requested @@ -2099,7 +2435,7 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, if selectedResourcesAreReady && (!operationInProgress || !watch.operation) { app = printFinalStatus(app) - return app, nil + return app, finalOperationState, nil } newStates := groupResourceStates(app, selectedResources) @@ -2109,7 +2445,7 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, if prevState, found := prevStates[stateKey]; found { if watch.health && prevState.Health != string(health.HealthStatusUnknown) && prevState.Health != string(health.HealthStatusDegraded) && newState.Health == string(health.HealthStatusDegraded) { _ = printFinalStatus(app) - return nil, fmt.Errorf("application '%s' health state has transitioned from %s to %s", appName, prevState.Health, newState.Health) + return nil, finalOperationState, fmt.Errorf("application '%s' health state has transitioned from %s to %s", appName, prevState.Health, newState.Health) } doPrint = prevState.Merge(newState) } else { @@ -2123,17 +2459,17 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, _ = w.Flush() } _ = printFinalStatus(app) - return nil, fmt.Errorf("timed out (%ds) waiting for app %q match desired state", timeout, appName) + return nil, finalOperationState, fmt.Errorf("timed out (%ds) waiting for app %q match desired state", timeout, appName) } // setParameterOverrides updates an existing or appends a new parameter override in the application // the app is assumed to be a helm app and is expected to be in the form: // param=value -func setParameterOverrides(app *argoappv1.Application, parameters []string) { +func setParameterOverrides(app *argoappv1.Application, parameters []string, index int) { if len(parameters) == 0 { return } - source := app.Spec.GetSource() + source := app.Spec.GetSourcePtr(index) var sourceType argoappv1.ApplicationSourceType if st, _ := source.ExplicitType(); st != nil { sourceType = *st @@ -2172,14 +2508,56 @@ func printApplicationHistoryIds(revHistory []argoappv1.RevisionHistory) { // Print a history table for an application. func printApplicationHistoryTable(revHistory []argoappv1.RevisionHistory) { + MAX_ALLOWED_REVISIONS := 7 w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - _, _ = fmt.Fprintf(w, "ID\tDATE\tREVISION\n") + type history struct { + id int64 + date string + revision string + } + varHistory := map[string][]history{} + varHistoryKeys := []string{} for _, depInfo := range revHistory { - rev := depInfo.Source.TargetRevision - if len(depInfo.Revision) >= 7 { - rev = fmt.Sprintf("%s (%s)", rev, depInfo.Revision[0:7]) + if depInfo.Sources != nil { + for i, sourceInfo := range depInfo.Sources { + rev := sourceInfo.TargetRevision + if len(depInfo.Revisions) == len(depInfo.Sources) && len(depInfo.Revisions[i]) >= MAX_ALLOWED_REVISIONS { + rev = fmt.Sprintf("%s (%s)", rev, depInfo.Revisions[i][0:MAX_ALLOWED_REVISIONS]) + } + if _, ok := varHistory[sourceInfo.RepoURL]; !ok { + varHistoryKeys = append(varHistoryKeys, sourceInfo.RepoURL) + } + varHistory[sourceInfo.RepoURL] = append(varHistory[sourceInfo.RepoURL], history{ + id: depInfo.ID, + date: depInfo.DeployedAt.String(), + revision: rev, + }) + } + } else { + rev := depInfo.Source.TargetRevision + if len(depInfo.Revision) >= MAX_ALLOWED_REVISIONS { + rev = fmt.Sprintf("%s (%s)", rev, depInfo.Revision[0:MAX_ALLOWED_REVISIONS]) + } + if _, ok := varHistory[depInfo.Source.RepoURL]; !ok { + varHistoryKeys = append(varHistoryKeys, depInfo.Source.RepoURL) + } + varHistory[depInfo.Source.RepoURL] = append(varHistory[depInfo.Source.RepoURL], history{ + id: depInfo.ID, + date: depInfo.DeployedAt.String(), + revision: rev, + }) + } + } + for i, key := range varHistoryKeys { + _, _ = fmt.Fprintf(w, "SOURCE\t%s\n", key) + _, _ = fmt.Fprintf(w, "ID\tDATE\tREVISION\n") + for _, history := range varHistory[key] { + _, _ = fmt.Fprintf(w, "%d\t%s\t%s\n", history.id, history.date, history.revision) + } + // Add a newline if it's not the last iteration + if i < len(varHistoryKeys)-1 { + _, _ = fmt.Fprintf(w, "\n") } - _, _ = fmt.Fprintf(w, "%d\t%s\t%s\n", depInfo.ID, depInfo.DeployedAt, rev) } _ = w.Flush() } @@ -2187,7 +2565,8 @@ func printApplicationHistoryTable(revHistory []argoappv1.RevisionHistory) { // NewApplicationHistoryCommand returns a new instance of an `argocd app history` command func NewApplicationHistoryCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - output string + output string + appNamespace string ) var command = &cobra.Command{ Use: "history APPNAME", @@ -2201,17 +2580,13 @@ func NewApplicationHistoryCommand(clientOpts *argocdclient.ClientOptions) *cobra } conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - appName, appNs := argo.ParseAppQualifiedName(args[0], "") - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + app, err := appIf.Get(ctx, &application.ApplicationQuery{ Name: &appName, AppNamespace: &appNs, }) errors.CheckError(err) - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning) - } - if output == "id" { printApplicationHistoryIds(app.Status.History) } else { @@ -2219,6 +2594,7 @@ func NewApplicationHistoryCommand(clientOpts *argocdclient.ClientOptions) *cobra } }, } + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Only show application deployment history in namespace") command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: wide|id") return command } @@ -2243,20 +2619,21 @@ func findRevisionHistory(application *argoappv1.Application, historyId int64) (* // NewApplicationRollbackCommand returns a new instance of an `argocd app rollback` command func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - prune bool - timeout uint + prune bool + timeout uint + output string + appNamespace string ) var command = &cobra.Command{ Use: "rollback APPNAME [ID]", Short: "Rollback application to a previous deployed version by History ID, omitted will Rollback to the previous version", Run: func(c *cobra.Command, args []string) { ctx := c.Context() - if len(args) == 0 { c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) var err error depID := -1 if len(args) > 1 { @@ -2266,20 +2643,16 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr acdClient := headless.NewClientOrDie(clientOpts, c) conn, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + app, err := appIf.Get(ctx, &application.ApplicationQuery{ Name: &appName, AppNamespace: &appNs, }) errors.CheckError(err) - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning) - } - depInfo, err := findRevisionHistory(app, int64(depID)) errors.CheckError(err) - _, err = appIf.Rollback(ctx, &applicationpkg.ApplicationRollbackRequest{ + _, err = appIf.Rollback(ctx, &application.ApplicationRollbackRequest{ Name: &appName, AppNamespace: &appNs, Id: pointer.Int64(depInfo.ID), @@ -2287,14 +2660,16 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr }) errors.CheckError(err) - _, err = waitOnApplicationStatus(ctx, acdClient, app.QualifiedName(), timeout, watchOpts{ + _, _, err = waitOnApplicationStatus(ctx, acdClient, app.QualifiedName(), timeout, watchOpts{ operation: true, - }, nil) + }, nil, output) errors.CheckError(err) }, } command.Flags().BoolVar(&prune, "prune", false, "Allow deleting unexpected resources") command.Flags().UintVar(&timeout, "timeout", defaultCheckTimeoutSeconds, "Time out after this many seconds") + command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|tree|tree=detailed") + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Rollback application in namespace") return command } @@ -2307,7 +2682,11 @@ func printOperationResult(opState *argoappv1.OperationState) { } if opState.SyncResult != nil { fmt.Printf(printOpFmtStr, "Operation:", "Sync") - fmt.Printf(printOpFmtStr, "Sync Revision:", opState.SyncResult.Revision) + if opState.SyncResult.Sources != nil && opState.SyncResult.Revisions != nil { + fmt.Printf(printOpFmtStr, "Sync Revision:", strings.Join(opState.SyncResult.Revisions, ", ")) + } else { + fmt.Printf(printOpFmtStr, "Sync Revision:", opState.SyncResult.Revision) + } } fmt.Printf(printOpFmtStr, "Phase:", opState.Phase) fmt.Printf(printOpFmtStr, "Start:", opState.StartedAt) @@ -2342,11 +2721,11 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + appName, appNs := argo.ParseFromQualifiedName(args[0], "") clientset := headless.NewClientOrDie(clientOpts, c) conn, appIf := clientset.NewApplicationClientOrDie() defer argoio.Close(conn) - resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ + resources, err := appIf.ManagedResources(ctx, &application.ResourcesQuery{ ApplicationName: &appName, AppNamespace: &appNs, }) @@ -2356,16 +2735,12 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob switch source { case "git": if local != "" { - app, err := appIf.Get(context.Background(), &applicationpkg.ApplicationQuery{Name: &appName}) + app, err := appIf.Get(context.Background(), &application.ApplicationQuery{Name: &appName}) errors.CheckError(err) - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning) - } - settingsConn, settingsIf := clientset.NewSettingsClientOrDie() defer argoio.Close(settingsConn) - argoSettings, err := settingsIf.Get(context.Background(), &settingspkg.SettingsQuery{}) + argoSettings, err := settingsIf.Get(context.Background(), &settings.SettingsQuery{}) errors.CheckError(err) clusterConn, clusterIf := clientset.NewClusterClientOrDie() @@ -2373,9 +2748,10 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server}) errors.CheckError(err) - unstructureds = getLocalObjects(context.Background(), app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins, argoSettings.TrackingMethod) + proj := getProject(c, clientOpts, ctx, app.Spec.Project) + unstructureds = getLocalObjects(context.Background(), app, proj.Project, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod) } else if revision != "" { - q := applicationpkg.ApplicationManifestQuery{ + q := application.ApplicationManifestQuery{ Name: &appName, AppNamespace: &appNs, Revision: pointer.String(revision), @@ -2428,10 +2804,10 @@ func NewApplicationTerminateOpCommand(clientOpts *argocdclient.ClientOptions) *c c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + appName, appNs := argo.ParseFromQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - _, err := appIf.TerminateOperation(ctx, &applicationpkg.OperationTerminateRequest{ + _, err := appIf.TerminateOperation(ctx, &application.OperationTerminateRequest{ Name: &appName, AppNamespace: &appNs, }) @@ -2443,6 +2819,9 @@ func NewApplicationTerminateOpCommand(clientOpts *argocdclient.ClientOptions) *c } func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { + var ( + appNamespace string + ) var command = &cobra.Command{ Use: "edit APPNAME", Short: "Edit application", @@ -2453,19 +2832,16 @@ func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + + appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + app, err := appIf.Get(ctx, &application.ApplicationQuery{ Name: &appName, AppNamespace: &appNs, }) errors.CheckError(err) - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - log.Warnf(argocommon.ConfigMapPluginCLIDeprecationWarning) - } - appData, err := json.Marshal(app.Spec) errors.CheckError(err) appData, err = yaml.JSONToYAML(appData) @@ -2483,8 +2859,12 @@ func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } var appOpts cmdutil.AppOptions - cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts) - _, err = appIf.UpdateSpec(ctx, &applicationpkg.ApplicationUpdateSpecRequest{ + + // do not allow overrides for applications with multiple sources + if !app.Spec.HasMultipleSources() { + cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts, 0) + } + _, err = appIf.UpdateSpec(ctx, &application.ApplicationUpdateSpecRequest{ Name: &appName, Spec: &updatedSpec, Validate: &appOpts.Validate, @@ -2497,22 +2877,25 @@ func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co }) }, } + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Only edit application in namespace") return command } func NewApplicationPatchCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { - var patch string - var patchType string + var ( + patch string + patchType string + appNamespace string + ) command := cobra.Command{ Use: "patch APPNAME", Short: "Patch application", - Long: `Examples: - # Update an application's source path using json patch - argocd app patch myapplication --patch='[{"op": "replace", "path": "/spec/source/path", "value": "newPath"}]' --type json + Example: ` # Update an application's source path using json patch + argocd app patch myapplication --patch='[{"op": "replace", "path": "/spec/source/path", "value": "newPath"}]' --type json - # Update an application's repository target revision using merge patch - argocd app patch myapplication --patch '{"spec": { "source": { "targetRevision": "master" } }}' --type merge`, + # Update an application's repository target revision using merge patch + argocd app patch myapplication --patch '{"spec": { "source": { "targetRevision": "master" } }}' --type merge`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -2520,11 +2903,11 @@ func NewApplicationPatchCommand(clientOpts *argocdclient.ClientOptions) *cobra.C c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - patchedApp, err := appIf.Patch(ctx, &applicationpkg.ApplicationPatchRequest{ + patchedApp, err := appIf.Patch(ctx, &application.ApplicationPatchRequest{ Name: &appName, Patch: &patch, PatchType: &patchType, @@ -2538,8 +2921,137 @@ func NewApplicationPatchCommand(clientOpts *argocdclient.ClientOptions) *cobra.C fmt.Println(string(yamlBytes)) }, } - + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Only patch application in namespace") command.Flags().StringVar(&patch, "patch", "", "Patch body") command.Flags().StringVar(&patchType, "type", "json", "The type of patch being provided; one of [json merge]") return &command } + +// NewApplicationAddSourceCommand returns a new instance of an `argocd app add-source` command +func NewApplicationAddSourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { + var ( + appOpts cmdutil.AppOptions + appNamespace string + ) + var command = &cobra.Command{ + Use: "add-source APPNAME", + Short: "Adds a source to the list of sources in the application", + Example: ` # Append a source to the list of sources in the application + argocd app add-source guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook`, + Run: func(c *cobra.Command, args []string) { + ctx := c.Context() + if len(args) != 1 { + c.HelpFunc()(c, args) + os.Exit(1) + } + + argocdClient := headless.NewClientOrDie(clientOpts, c) + conn, appIf := argocdClient.NewApplicationClientOrDie() + defer argoio.Close(conn) + + appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + + app, err := appIf.Get(ctx, &application.ApplicationQuery{ + Name: &appName, + Refresh: getRefreshType(false, false), + AppNamespace: &appNs, + }) + + errors.CheckError(err) + + if c.Flags() == nil { + errors.CheckError(fmt.Errorf("ApplicationSource needs atleast repoUrl, path or chart or ref field. No source to add.")) + } + + if len(app.Spec.Sources) > 0 { + appSource, _ := cmdutil.ConstructSource(&argoappv1.ApplicationSource{}, appOpts, c.Flags()) + + // sourceIndex is the index at which new source will be appended to spec.Sources + sourceIndex := len(app.Spec.GetSources()) + app.Spec.Sources = append(app.Spec.Sources, *appSource) + + setParameterOverrides(app, appOpts.Parameters, sourceIndex) + + _, err = appIf.UpdateSpec(ctx, &application.ApplicationUpdateSpecRequest{ + Name: &app.Name, + Spec: &app.Spec, + Validate: &appOpts.Validate, + AppNamespace: &appNs, + }) + errors.CheckError(err) + + fmt.Printf("Application '%s' updated successfully\n", app.ObjectMeta.Name) + } else { + errors.CheckError(fmt.Errorf("Cannot add source: application %s does not have spec.sources defined", appName)) + } + }, + } + cmdutil.AddAppFlags(command, &appOpts) + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Namespace of the target application where the source will be appended") + return command +} + +// NewApplicationRemoveSourceCommand returns a new instance of an `argocd app remove-source` command +func NewApplicationRemoveSourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { + var ( + sourceIndex int + appNamespace string + ) + command := &cobra.Command{ + Use: "remove-source APPNAME", + Short: "Remove a source from multiple sources application. Index starts with 1. Default value is -1.", + Example: ` # Remove the source at index 1 from application's sources. Index starts at 1. + argocd app remove-source myapplication --source-index 1`, + Run: func(c *cobra.Command, args []string) { + ctx := c.Context() + + if len(args) != 1 { + c.HelpFunc()(c, args) + os.Exit(1) + } + + if sourceIndex <= 0 { + errors.CheckError(fmt.Errorf("Index value of source must be greater than 0")) + } + + argocdClient := headless.NewClientOrDie(clientOpts, c) + conn, appIf := argocdClient.NewApplicationClientOrDie() + defer argoio.Close(conn) + + appName, appNs := argo.ParseFromQualifiedName(args[0], appNamespace) + + app, err := appIf.Get(ctx, &application.ApplicationQuery{ + Name: &appName, + Refresh: getRefreshType(false, false), + AppNamespace: &appNs, + }) + errors.CheckError(err) + + if !app.Spec.HasMultipleSources() { + errors.CheckError(fmt.Errorf("Application does not have multiple sources configured")) + } + + if len(app.Spec.GetSources()) == 1 { + errors.CheckError(fmt.Errorf("Cannot remove the only source remaining in the app")) + } + + if len(app.Spec.GetSources()) < sourceIndex { + errors.CheckError(fmt.Errorf("Application does not have source at %d\n", sourceIndex)) + } + + app.Spec.Sources = append(app.Spec.Sources[:sourceIndex-1], app.Spec.Sources[sourceIndex:]...) + + _, err = appIf.UpdateSpec(ctx, &application.ApplicationUpdateSpecRequest{ + Name: &app.Name, + Spec: &app.Spec, + AppNamespace: &appNs, + }) + errors.CheckError(err) + + fmt.Printf("Application '%s' updated successfully\n", app.ObjectMeta.Name) + }, + } + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Namespace of the target application where the source will be appended") + command.Flags().IntVar(&sourceIndex, "source-index", -1, "Index of the source from the list of sources of the app. Index starts from 1.") + return command +} diff --git a/cmd/argocd/commands/app_actions.go b/cmd/argocd/commands/app_actions.go index 7145e2a52c658..866aed5ae349e 100644 --- a/cmd/argocd/commands/app_actions.go +++ b/cmd/argocd/commands/app_actions.go @@ -4,20 +4,22 @@ import ( "context" "encoding/json" "fmt" + "github.com/argoproj/argo-cd/v2/util/templates" "os" "strconv" "text/tabwriter" "github.com/argoproj/argo-cd/v2/cmd/util" - "github.com/ghodss/yaml" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "k8s.io/utils/pointer" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" + "github.com/argoproj/argo-cd/v2/pkg/apis/application" v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/errors" @@ -32,11 +34,22 @@ type DisplayedAction struct { Disabled bool } +var ( + appActionExample = templates.Examples(` + # List all the available actions for an application + argocd app actions list APPNAME + + # Run an available action for an application + argocd app actions run APPNAME ACTION --kind KIND [--resource-name RESOURCE] [--namespace NAMESPACE] [--group GROUP] + `) +) + // NewApplicationResourceActionsCommand returns a new instance of an `argocd app actions` command func NewApplicationResourceActionsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var command = &cobra.Command{ - Use: "actions", - Short: "Manage Resource actions", + Use: "actions", + Short: "Manage Resource actions", + Example: appActionExample, Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) os.Exit(1) @@ -57,6 +70,10 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt var command = &cobra.Command{ Use: "list APPNAME", Short: "Lists available actions on a resource", + Example: templates.Examples(` + # List all the available actions for an application + argocd app actions list APPNAME + `), } command.Run = func(c *cobra.Command, args []string) { ctx := c.Context() @@ -65,7 +82,7 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + appName, appNs := argo.ParseFromQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer io.Close(conn) resources, err := getActionableResourcesForApplication(appIf, ctx, &appNs, &appName) @@ -135,6 +152,10 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti var command = &cobra.Command{ Use: "run APPNAME ACTION", Short: "Runs an available action on resource(s)", + Example: templates.Examples(` + # Run an available action for an application + argocd app actions run APPNAME ACTION --kind KIND [--resource-name RESOURCE] [--namespace NAMESPACE] [--group GROUP] + `), } command.Flags().StringVar(&resourceName, "resource-name", "", "Name of resource") @@ -151,7 +172,7 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + appName, appNs := argo.ParseFromQualifiedName(args[0], "") actionName := args[1] conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() @@ -202,7 +223,7 @@ func getActionableResourcesForApplication(appIf applicationpkg.ApplicationServic if err != nil { return nil, err } - app.Kind = "Application" + app.Kind = application.ApplicationKind app.APIVersion = "argoproj.io/v1alpha1" appManifest, err := json.Marshal(app) if err != nil { diff --git a/cmd/argocd/commands/app_resource_test.go b/cmd/argocd/commands/app_resource_test.go index 2c94ad7a0f418..5846065141e15 100644 --- a/cmd/argocd/commands/app_resource_test.go +++ b/cmd/argocd/commands/app_resource_test.go @@ -1,13 +1,93 @@ package commands import ( + "bytes" "testing" + "text/tabwriter" "github.com/stretchr/testify/assert" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) +func TestPrintTreeViewAppResources(t *testing.T) { + var nodes [3]v1alpha1.ResourceNode + nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"} + nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}} + nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + var nodeMapping = make(map[string]v1alpha1.ResourceNode) + var mapParentToChild = make(map[string][]string) + var parentNode = make(map[string]struct{}) + for _, node := range nodes { + nodeMapping[node.UID] = node + if len(node.ParentRefs) > 0 { + _, ok := mapParentToChild[node.ParentRefs[0].UID] + if !ok { + var temp []string + mapParentToChild[node.ParentRefs[0].UID] = temp + } + mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) + } else { + parentNode[node.UID] = struct{}{} + } + } + buf := &bytes.Buffer{} + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + + printTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, false, false, w) + if err := w.Flush(); err != nil { + t.Fatal(err) + } + output := buf.String() + + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "argoproj.io") +} + +func TestPrintTreeViewDetailedAppResources(t *testing.T) { + var nodes [3]v1alpha1.ResourceNode + nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"} + nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}} + nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + nodes[2].Health = &v1alpha1.HealthStatus{ + Status: "Degraded", + Message: "Readiness Gate failed", + } + + var nodeMapping = make(map[string]v1alpha1.ResourceNode) + var mapParentToChild = make(map[string][]string) + var parentNode = make(map[string]struct{}) + for _, node := range nodes { + nodeMapping[node.UID] = node + if len(node.ParentRefs) > 0 { + _, ok := mapParentToChild[node.ParentRefs[0].UID] + if !ok { + var temp []string + mapParentToChild[node.ParentRefs[0].UID] = temp + } + mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) + } else { + parentNode[node.UID] = struct{}{} + } + } + buf := &bytes.Buffer{} + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + + printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, false, false, w) + if err := w.Flush(); err != nil { + t.Fatal(err) + } + output := buf.String() + + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "Degraded") + assert.Contains(t, output, "Readiness Gate failed") +} + func TestPrintResourcesTree(t *testing.T) { tree := v1alpha1.ApplicationTree{ Nodes: []v1alpha1.ResourceNode{ @@ -32,7 +112,7 @@ func TestPrintResourcesTree(t *testing.T) { }, } output, _ := captureOutput(func() error { - printResources(true, false, &tree) + printResources(true, false, &tree, "") return nil }) diff --git a/cmd/argocd/commands/app_resources.go b/cmd/argocd/commands/app_resources.go index 02c1054b6372d..4cffb706ff1bc 100644 --- a/cmd/argocd/commands/app_resources.go +++ b/cmd/argocd/commands/app_resources.go @@ -3,10 +3,10 @@ package commands import ( "fmt" "os" - - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "text/tabwriter" "github.com/argoproj/argo-cd/v2/cmd/util" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -19,8 +19,6 @@ import ( "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/errors" argoio "github.com/argoproj/argo-cd/v2/util/io" - - "text/tabwriter" ) func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { @@ -31,6 +29,7 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions) var kind string var group string var all bool + var project string command := &cobra.Command{ Use: "patch-resource APPNAME", Short: "Patch resource in an application", @@ -47,6 +46,7 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions) command.Flags().StringVar(&group, "group", "", "Group") command.Flags().StringVar(&namespace, "namespace", "", "Namespace") command.Flags().BoolVar(&all, "all", false, "Indicates whether to patch multiple matching of resources") + command.Flags().StringVar(&project, "project", "", `The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist`) command.Run = func(c *cobra.Command, args []string) { ctx := c.Context() @@ -54,7 +54,7 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions) c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + appName, appNs := argo.ParseFromQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) @@ -78,6 +78,7 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions) Kind: pointer.String(gvk.Kind), Patch: pointer.String(patch), PatchType: pointer.String(patchType), + Project: pointer.String(project), }) errors.CheckError(err) log.Infof("Resource '%s' patched", obj.GetName()) @@ -95,6 +96,7 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) var force bool var orphan bool var all bool + var project string command := &cobra.Command{ Use: "delete-resource APPNAME", Short: "Delete resource in an application", @@ -109,6 +111,7 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) command.Flags().BoolVar(&force, "force", false, "Indicates whether to orphan the dependents of the deleted resource") command.Flags().BoolVar(&orphan, "orphan", false, "Indicates whether to force delete the resource") command.Flags().BoolVar(&all, "all", false, "Indicates whether to patch multiple matching of resources") + command.Flags().StringVar(&project, "project", "", `The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist`) command.Run = func(c *cobra.Command, args []string) { ctx := c.Context() @@ -116,7 +119,7 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) c.HelpFunc()(c, args) os.Exit(1) } - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + appName, appNs := argo.ParseFromQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) @@ -140,6 +143,7 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) Kind: pointer.String(gvk.Kind), Force: &force, Orphan: &orphan, + Project: pointer.String(project), }) errors.CheckError(err) log.Infof("Resource '%s' deleted", obj.GetName()) @@ -149,50 +153,133 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) return command } -func printResources(listAll bool, orphaned bool, appResourceTree *v1alpha1.ApplicationTree) { - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - headers := []interface{}{"GROUP", "KIND", "NAMESPACE", "NAME", "ORPHANED"} - fmtStr := "%s\t%s\t%s\t%s\t%s\n" - _, _ = fmt.Fprintf(w, fmtStr, headers...) - if !orphaned || listAll { - for _, res := range appResourceTree.Nodes { - if len(res.ParentRefs) == 0 { - _, _ = fmt.Fprintf(w, fmtStr, res.Group, res.Kind, res.Namespace, res.Name, "No") +func parentChildInfo(nodes []v1alpha1.ResourceNode) (map[string]v1alpha1.ResourceNode, map[string][]string, map[string]struct{}) { + mapUidToNode := make(map[string]v1alpha1.ResourceNode) + mapParentToChild := make(map[string][]string) + parentNode := make(map[string]struct{}) + + for _, node := range nodes { + mapUidToNode[node.UID] = node + + if len(node.ParentRefs) > 0 { + _, ok := mapParentToChild[node.ParentRefs[0].UID] + if !ok { + var temp []string + mapParentToChild[node.ParentRefs[0].UID] = temp } + mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) + } else { + parentNode[node.UID] = struct{}{} } } - if orphaned || listAll { - for _, res := range appResourceTree.OrphanedNodes { - _, _ = fmt.Fprintf(w, fmtStr, res.Group, res.Kind, res.Namespace, res.Name, "Yes") + return mapUidToNode, mapParentToChild, parentNode +} + +func printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) { + for uid := range parentNodes { + detailedTreeViewAppResourcesNotOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w) + } + +} + +func printDetailedTreeViewAppResourcesOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) { + for uid := range parentNodes { + detailedTreeViewAppResourcesOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w) + } +} + +func printTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) { + for uid := range parentNodes { + treeViewAppResourcesNotOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w) + } + +} + +func printTreeViewAppResourcesOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) { + for uid := range parentNodes { + treeViewAppResourcesOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w) + } +} + +func printResources(listAll bool, orphaned bool, appResourceTree *v1alpha1.ApplicationTree, output string) { + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + if output == "tree=detailed" { + fmt.Fprintf(w, "GROUP\tKIND\tNAMESPACE\tNAME\tORPHANED\tAGE\tHEALTH\tREASON\n") + + if !orphaned || listAll { + mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.Nodes) + printDetailedTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w) } + + if orphaned || listAll { + mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.OrphanedNodes) + printDetailedTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w) + } + + } else if output == "tree" { + fmt.Fprintf(w, "GROUP\tKIND\tNAMESPACE\tNAME\tORPHANED\n") + + if !orphaned || listAll { + mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.Nodes) + printTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w) + } + + if orphaned || listAll { + mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.OrphanedNodes) + printTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w) + } + + } else { + + headers := []interface{}{"GROUP", "KIND", "NAMESPACE", "NAME", "ORPHANED"} + fmtStr := "%s\t%s\t%s\t%s\t%s\n" + _, _ = fmt.Fprintf(w, fmtStr, headers...) + if !orphaned || listAll { + for _, res := range appResourceTree.Nodes { + if len(res.ParentRefs) == 0 { + _, _ = fmt.Fprintf(w, fmtStr, res.Group, res.Kind, res.Namespace, res.Name, "No") + } + } + } + if orphaned || listAll { + for _, res := range appResourceTree.OrphanedNodes { + _, _ = fmt.Fprintf(w, fmtStr, res.Group, res.Kind, res.Namespace, res.Name, "Yes") + } + } + } _ = w.Flush() + } func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var orphaned bool + var output string + var project string var command = &cobra.Command{ Use: "resources APPNAME", Short: "List resource of application", Run: func(c *cobra.Command, args []string) { ctx := c.Context() - if len(args) != 1 { c.HelpFunc()(c, args) os.Exit(1) } listAll := !c.Flag("orphaned").Changed - appName, appNs := argo.ParseAppQualifiedName(args[0], "") + appName, appNs := argo.ParseFromQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) appResourceTree, err := appIf.ResourceTree(ctx, &applicationpkg.ResourcesQuery{ ApplicationName: &appName, AppNamespace: &appNs, + Project: &project, }) errors.CheckError(err) - printResources(listAll, orphaned, appResourceTree) + printResources(listAll, orphaned, appResourceTree, output) }, } command.Flags().BoolVar(&orphaned, "orphaned", false, "Lists only orphaned resources") + command.Flags().StringVar(&output, "output", "", "Provides the tree view of the resources") + command.Flags().StringVar(&project, "project", "", `The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist`) return command } diff --git a/cmd/argocd/commands/app_test.go b/cmd/argocd/commands/app_test.go index 104495cb541d0..ec9dcdf0f8e65 100644 --- a/cmd/argocd/commands/app_test.go +++ b/cmd/argocd/commands/app_test.go @@ -1,21 +1,43 @@ package commands import ( + "context" "fmt" + "io" + "net/http" "os" "testing" "time" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" + accountpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/account" + applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" + applicationsetpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/applicationset" + certificatepkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/certificate" + clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster" + gpgkeypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/gpgkey" + notificationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/notification" + projectpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/project" + repocredspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repocreds" + repositorypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository" + sessionpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/session" + settingspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/settings" + versionpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/version" + "github.com/argoproj/argo-cd/v2/pkg/apis/application" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/gitops-engine/pkg/health" "github.com/argoproj/gitops-engine/pkg/utils/kube" + "github.com/coreos/go-oidc/v3/oidc" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" + "golang.org/x/oauth2" 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/util/intstr" + k8swatch "k8s.io/apimachinery/pkg/watch" ) func Test_getInfos(t *testing.T) { @@ -113,6 +135,86 @@ func TestFindRevisionHistoryWithoutPassedId(t *testing.T) { } +func TestPrintTreeViewAppGet(t *testing.T) { + var nodes [3]v1alpha1.ResourceNode + nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"} + nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}} + nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + + var nodeMapping = make(map[string]v1alpha1.ResourceNode) + var mapParentToChild = make(map[string][]string) + var parentNode = make(map[string]struct{}) + + for _, node := range nodes { + nodeMapping[node.UID] = node + + if len(node.ParentRefs) > 0 { + _, ok := mapParentToChild[node.ParentRefs[0].UID] + if !ok { + var temp []string + mapParentToChild[node.ParentRefs[0].UID] = temp + } + mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) + } else { + parentNode[node.UID] = struct{}{} + } + } + + output, _ := captureOutput(func() error { + printTreeView(nodeMapping, mapParentToChild, parentNode, nil) + return nil + }) + + assert.Contains(t, output, "Pod") + assert.Contains(t, output, "ReplicaSet") + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "numalogic-rollout-demo-5dcd5457d5-6trpt") +} + +func TestPrintTreeViewDetailedAppGet(t *testing.T) { + var nodes [3]v1alpha1.ResourceNode + nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"} + nodes[0].Health = &v1alpha1.HealthStatus{Status: "Degraded", Message: "Readiness Gate failed"} + nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}} + nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + + var nodeMapping = make(map[string]v1alpha1.ResourceNode) + var mapParentToChild = make(map[string][]string) + var parentNode = make(map[string]struct{}) + + for _, node := range nodes { + nodeMapping[node.UID] = node + + if len(node.ParentRefs) > 0 { + _, ok := mapParentToChild[node.ParentRefs[0].UID] + if !ok { + var temp []string + mapParentToChild[node.ParentRefs[0].UID] = temp + } + mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) + } else { + parentNode[node.UID] = struct{}{} + } + } + + output, _ := captureOutput(func() error { + printTreeViewDetailed(nodeMapping, mapParentToChild, parentNode, nil) + return nil + }) + + assert.Contains(t, output, "Pod") + assert.Contains(t, output, "ReplicaSet") + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "numalogic-rollout-demo-5dcd5457d5-6trpt") + assert.Contains(t, output, "Degraded") + assert.Contains(t, output, "Readiness Gate failed") + +} + func TestDefaultWaitOptions(t *testing.T) { watch := watchOpts{ sync: false, @@ -305,8 +407,8 @@ func Test_groupObjsByKey(t *testing.T) { } expected := map[kube.ResourceKey]*unstructured.Unstructured{ - kube.ResourceKey{Group: "", Kind: "Pod", Namespace: "default", Name: "pod-name"}: localObjs[0], - kube.ResourceKey{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition", Namespace: "", Name: "certificates.cert-manager.io"}: localObjs[1], + {Group: "", Kind: "Pod", Namespace: "default", Name: "pod-name"}: localObjs[0], + {Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition", Namespace: "", Name: "certificates.cert-manager.io"}: localObjs[1], } objByKey := groupObjsByKey(localObjs, liveObjs, "default") @@ -320,8 +422,8 @@ func TestFormatSyncPolicy(t *testing.T) { policy := formatSyncPolicy(app) - if policy != "" { - t.Fatalf("Incorrect policy %q, should be ", policy) + if policy != "Manual" { + t.Fatalf("Incorrect policy %q, should be Manual", policy) } }) @@ -455,18 +557,100 @@ func TestPrintApplicationHistoryTable(t *testing.T) { ID: 1, Source: v1alpha1.ApplicationSource{ TargetRevision: "1", + RepoURL: "test", }, }, { ID: 2, Source: v1alpha1.ApplicationSource{ TargetRevision: "2", + RepoURL: "test", }, }, { ID: 3, Source: v1alpha1.ApplicationSource{ TargetRevision: "3", + RepoURL: "test", + }, + }, + } + + output, _ := captureOutput(func() error { + printApplicationHistoryTable(histories) + return nil + }) + + expectation := "SOURCE test\nID DATE REVISION\n1 0001-01-01 00:00:00 +0000 UTC 1\n2 0001-01-01 00:00:00 +0000 UTC 2\n3 0001-01-01 00:00:00 +0000 UTC 3\n" + + if output != expectation { + t.Fatalf("Incorrect print operation output %q, should be %q", output, expectation) + } +} + +func TestPrintApplicationHistoryTableWithMultipleSources(t *testing.T) { + histories := []v1alpha1.RevisionHistory{ + { + ID: 0, + Source: v1alpha1.ApplicationSource{ + TargetRevision: "0", + RepoURL: "test", + }, + }, + { + ID: 1, + Revisions: []string{ + "1a", + "1b", + }, + //added Source just for testing the fuction + Source: v1alpha1.ApplicationSource{ + TargetRevision: "-1", + RepoURL: "ignore", + }, + Sources: v1alpha1.ApplicationSources{ + v1alpha1.ApplicationSource{ + RepoURL: "test-1", + TargetRevision: "1a", + }, + v1alpha1.ApplicationSource{ + RepoURL: "test-2", + TargetRevision: "1b", + }, + }, + }, + { + ID: 2, + Revisions: []string{ + "2a", + "2b", + }, + Sources: v1alpha1.ApplicationSources{ + v1alpha1.ApplicationSource{ + RepoURL: "test-1", + TargetRevision: "2a", + }, + v1alpha1.ApplicationSource{ + RepoURL: "test-2", + TargetRevision: "2b", + }, + }, + }, + { + ID: 3, + Revisions: []string{ + "3a", + "3b", + }, + Sources: v1alpha1.ApplicationSources{ + v1alpha1.ApplicationSource{ + RepoURL: "test-1", + TargetRevision: "3a", + }, + v1alpha1.ApplicationSource{ + RepoURL: "test-2", + TargetRevision: "3b", + }, }, }, } @@ -476,7 +660,7 @@ func TestPrintApplicationHistoryTable(t *testing.T) { return nil }) - expectation := "ID DATE REVISION\n1 0001-01-01 00:00:00 +0000 UTC 1\n2 0001-01-01 00:00:00 +0000 UTC 2\n3 0001-01-01 00:00:00 +0000 UTC 3\n" + expectation := "SOURCE test\nID DATE REVISION\n0 0001-01-01 00:00:00 +0000 UTC 0\n\nSOURCE test-1\nID DATE REVISION\n1 0001-01-01 00:00:00 +0000 UTC 1a\n2 0001-01-01 00:00:00 +0000 UTC 2a\n3 0001-01-01 00:00:00 +0000 UTC 3a\n\nSOURCE test-2\nID DATE REVISION\n1 0001-01-01 00:00:00 +0000 UTC 1b\n2 0001-01-01 00:00:00 +0000 UTC 2b\n3 0001-01-01 00:00:00 +0000 UTC 3b\n" if output != expectation { t.Fatalf("Incorrect print operation output %q, should be %q", output, expectation) @@ -557,11 +741,110 @@ Project: default Server: local Namespace: argocd URL: url -Repo: test -Target: master -Path: /test -Helm Values: path1,path2 -Name Prefix: prefix +Source: +- Repo: test + Target: master + Path: /test + Helm Values: path1,path2 + Name Prefix: prefix +SyncWindow: Sync Denied +Assigned Windows: allow:0 0 * * *:24h,deny:0 0 * * *:24h,allow:0 0 * * *:24h +Sync Policy: Automated (Prune) +Sync Status: OutOfSync from master +Health Status: Progressing (health-message) +` + assert.Equalf(t, expectation, output, "Incorrect print app summary output %q, should be %q", output, expectation) +} + +func TestPrintAppSummaryTable_MultipleSources(t *testing.T) { + output, _ := captureOutput(func() error { + app := &v1alpha1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "argocd", + }, + Spec: v1alpha1.ApplicationSpec{ + SyncPolicy: &v1alpha1.SyncPolicy{ + Automated: &v1alpha1.SyncPolicyAutomated{ + Prune: true, + }, + }, + Project: "default", + Destination: v1alpha1.ApplicationDestination{Server: "local", Namespace: "argocd"}, + Sources: v1alpha1.ApplicationSources{ + { + RepoURL: "test", + TargetRevision: "master", + Path: "/test", + Helm: &v1alpha1.ApplicationSourceHelm{ + ValueFiles: []string{"path1", "path2"}, + }, + Kustomize: &v1alpha1.ApplicationSourceKustomize{NamePrefix: "prefix"}, + }, { + RepoURL: "test2", + TargetRevision: "master2", + Path: "/test2", + }, + }, + }, + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, + }, + Health: v1alpha1.HealthStatus{ + Status: health.HealthStatusProgressing, + Message: "health-message", + }, + }, + } + + windows := &v1alpha1.SyncWindows{ + { + Kind: "allow", + Schedule: "0 0 * * *", + Duration: "24h", + Applications: []string{ + "*-prod", + }, + ManualSync: true, + }, + { + Kind: "deny", + Schedule: "0 0 * * *", + Duration: "24h", + Namespaces: []string{ + "default", + }, + }, + { + Kind: "allow", + Schedule: "0 0 * * *", + Duration: "24h", + Clusters: []string{ + "in-cluster", + "cluster1", + }, + }, + } + + printAppSummaryTable(app, "url", windows) + return nil + }) + + expectation := `Name: argocd/test +Project: default +Server: local +Namespace: argocd +URL: url +Sources: +- Repo: test + Target: master + Path: /test + Helm Values: path1,path2 + Name Prefix: prefix +- Repo: test2 + Target: master2 + Path: /test2 SyncWindow: Sync Denied Assigned Windows: allow:0 0 * * *:24h,deny:0 0 * * *:24h,allow:0 0 * * *:24h Sync Policy: Automated (Prune) @@ -724,6 +1007,14 @@ func TestTargetObjects_invalid(t *testing.T) { assert.Error(t, err) } +func TestCheckForDeleteEvent(t *testing.T) { + + ctx := context.Background() + fakeClient := new(fakeAcdClient) + + checkForDeleteEvent(ctx, fakeClient, "testApp") +} + func TestPrintApplicationNames(t *testing.T) { output, _ := captureOutput(func() error { app := &v1alpha1.Application{ @@ -750,6 +1041,16 @@ func Test_unset(t *testing.T) { "old1=new:tag", "old2=new:tag", }, + Replicas: []v1alpha1.KustomizeReplica{ + { + Name: "my-deployment", + Count: intstr.FromInt(2), + }, + { + Name: "my-statefulset", + Count: intstr.FromInt(4), + }, + }, }, } @@ -767,7 +1068,7 @@ func Test_unset(t *testing.T) { }, }, PassCredentials: true, - Values: "some: yaml", + ValuesObject: &runtime.RawExtension{Raw: []byte("some: yaml")}, ValueFiles: []string{ "values-1.yaml", "values-2.yaml", @@ -826,6 +1127,15 @@ func Test_unset(t *testing.T) { assert.False(t, updated) assert.False(t, nothingToUnset) + assert.Equal(t, 2, len(kustomizeSource.Kustomize.Replicas)) + updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeReplicas: []string{"my-deployment"}}) + assert.Equal(t, 1, len(kustomizeSource.Kustomize.Replicas)) + assert.True(t, updated) + assert.False(t, nothingToUnset) + updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeReplicas: []string{"my-deployment"}}) + assert.False(t, updated) + assert.False(t, nothingToUnset) + assert.Equal(t, 2, len(helmSource.Helm.Parameters)) updated, nothingToUnset = unset(helmSource, unsetOpts{parameters: []string{"name-1"}}) assert.Equal(t, 1, len(helmSource.Helm.Parameters)) @@ -844,9 +1154,9 @@ func Test_unset(t *testing.T) { assert.False(t, updated) assert.False(t, nothingToUnset) - assert.Equal(t, "some: yaml", helmSource.Helm.Values) + assert.Equal(t, "some: yaml", helmSource.Helm.ValuesString()) updated, nothingToUnset = unset(helmSource, unsetOpts{valuesLiteral: true}) - assert.Equal(t, "", helmSource.Helm.Values) + assert.Equal(t, "", helmSource.Helm.ValuesString()) assert.True(t, updated) assert.False(t, nothingToUnset) updated, nothingToUnset = unset(helmSource, unsetOpts{valuesLiteral: true}) @@ -952,49 +1262,49 @@ func TestFilterAppResources(t *testing.T) { } // Resource filters var ( - blankValues = argoappv1.SyncOperationResource{ + blankValues = v1alpha1.SyncOperationResource{ Group: "", Kind: "", Name: "", Namespace: "", Exclude: false} // *:*:* - includeAllResources = argoappv1.SyncOperationResource{ + includeAllResources = v1alpha1.SyncOperationResource{ Group: "*", Kind: "*", Name: "*", Namespace: "", Exclude: false} // !*:*:* - excludeAllResources = argoappv1.SyncOperationResource{ + excludeAllResources = v1alpha1.SyncOperationResource{ Group: "*", Kind: "*", Name: "*", Namespace: "", Exclude: true} // *:Service:* - includeAllServiceResources = argoappv1.SyncOperationResource{ + includeAllServiceResources = v1alpha1.SyncOperationResource{ Group: "*", Kind: "Service", Name: "*", Namespace: "", Exclude: false} // !*:Service:* - excludeAllServiceResources = argoappv1.SyncOperationResource{ + excludeAllServiceResources = v1alpha1.SyncOperationResource{ Group: "*", Kind: "Service", Name: "*", Namespace: "", Exclude: true} // apps:ReplicaSet:replicaSet-name1 - includeReplicaSet1Resource = argoappv1.SyncOperationResource{ + includeReplicaSet1Resource = v1alpha1.SyncOperationResource{ Group: "apps", Kind: "ReplicaSet", Name: "replicaSet-name1", Namespace: "", Exclude: false} // !apps:ReplicaSet:replicaSet-name2 - excludeReplicaSet2Resource = argoappv1.SyncOperationResource{ + excludeReplicaSet2Resource = v1alpha1.SyncOperationResource{ Group: "apps", Kind: "ReplicaSet", Name: "replicaSet-name2", @@ -1043,60 +1353,60 @@ func TestFilterAppResources(t *testing.T) { ) tests := []struct { testName string - selectedResources []*argoappv1.SyncOperationResource - expectedResult []*argoappv1.SyncOperationResource + selectedResources []*v1alpha1.SyncOperationResource + expectedResult []*v1alpha1.SyncOperationResource }{ - //--resource apps:ReplicaSet:replicaSet-name1 --resource *:Service:* + // --resource apps:ReplicaSet:replicaSet-name1 --resource *:Service:* {testName: "Include ReplicaSet replicaSet-name1 resouce and all service resources", - selectedResources: []*argoappv1.SyncOperationResource{&includeAllServiceResources, &includeReplicaSet1Resource}, - expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1, &service1, &service2}, + selectedResources: []*v1alpha1.SyncOperationResource{&includeAllServiceResources, &includeReplicaSet1Resource}, + expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &service1, &service2}, }, - //--resource apps:ReplicaSet:replicaSet-name1 --resource !*:Service:* + // --resource apps:ReplicaSet:replicaSet-name1 --resource !*:Service:* {testName: "Include ReplicaSet replicaSet-name1 resouce and exclude all service resources", - selectedResources: []*argoappv1.SyncOperationResource{&excludeAllServiceResources, &includeReplicaSet1Resource}, - expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &deployment}, + selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllServiceResources, &includeReplicaSet1Resource}, + expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &deployment}, }, // --resource !apps:ReplicaSet:replicaSet-name2 --resource !*:Service:* {testName: "Exclude ReplicaSet replicaSet-name2 resouce and all service resources", - selectedResources: []*argoappv1.SyncOperationResource{&excludeReplicaSet2Resource, &excludeAllServiceResources}, - expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &service1, &service2, &deployment}, + selectedResources: []*v1alpha1.SyncOperationResource{&excludeReplicaSet2Resource, &excludeAllServiceResources}, + expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &service1, &service2, &deployment}, }, // --resource !apps:ReplicaSet:replicaSet-name2 {testName: "Exclude ReplicaSet replicaSet-name2 resouce", - selectedResources: []*argoappv1.SyncOperationResource{&excludeReplicaSet2Resource}, - expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1, &job, &service1, &service2, &deployment}, + selectedResources: []*v1alpha1.SyncOperationResource{&excludeReplicaSet2Resource}, + expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &job, &service1, &service2, &deployment}, }, // --resource apps:ReplicaSet:replicaSet-name1 {testName: "Include ReplicaSet replicaSet-name1 resouce", - selectedResources: []*argoappv1.SyncOperationResource{&includeReplicaSet1Resource}, - expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1}, + selectedResources: []*v1alpha1.SyncOperationResource{&includeReplicaSet1Resource}, + expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1}, }, // --resource !*:Service:* {testName: "Exclude Service resouces", - selectedResources: []*argoappv1.SyncOperationResource{&excludeAllServiceResources}, - expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &deployment}, + selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllServiceResources}, + expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &deployment}, }, // --resource *:Service:* {testName: "Include Service resouces", - selectedResources: []*argoappv1.SyncOperationResource{&includeAllServiceResources}, - expectedResult: []*argoappv1.SyncOperationResource{&service1, &service2}, + selectedResources: []*v1alpha1.SyncOperationResource{&includeAllServiceResources}, + expectedResult: []*v1alpha1.SyncOperationResource{&service1, &service2}, }, // --resource !*:*:* {testName: "Exclude all resouces", - selectedResources: []*argoappv1.SyncOperationResource{&excludeAllResources}, + selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllResources}, expectedResult: nil, }, // --resource *:*:* {testName: "Include all resouces", - selectedResources: []*argoappv1.SyncOperationResource{&includeAllResources}, - expectedResult: []*argoappv1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &service1, &service2, &deployment}, + selectedResources: []*v1alpha1.SyncOperationResource{&includeAllResources}, + expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &service1, &service2, &deployment}, }, {testName: "No Filters", - selectedResources: []*argoappv1.SyncOperationResource{&blankValues}, + selectedResources: []*v1alpha1.SyncOperationResource{&blankValues}, expectedResult: nil, }, {testName: "Empty Filter", - selectedResources: []*argoappv1.SyncOperationResource{}, + selectedResources: []*v1alpha1.SyncOperationResource{}, expectedResult: nil, }, } @@ -1121,13 +1431,13 @@ func TestParseSelectedResources(t *testing.T) { assert.Equal(t, *operationResources[0], v1alpha1.SyncOperationResource{ Namespace: "", Name: "test", - Kind: "Application", + Kind: application.ApplicationKind, Group: "v1alpha", }) assert.Equal(t, *operationResources[1], v1alpha1.SyncOperationResource{ Namespace: "namespace", Name: "test", - Kind: "Application", + Kind: application.ApplicationKind, Group: "v1alpha", }) assert.Equal(t, *operationResources[2], v1alpha1.SyncOperationResource{ @@ -1200,7 +1510,7 @@ func TestPrintApplicationTableNotWide(t *testing.T) { return nil }) assert.NoError(t, err) - expectation := "NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS\napp-name http://localhost:8080 default prj OutOfSync Healthy \napp-name http://localhost:8080 default prj OutOfSync Healthy \n" + expectation := "NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS\napp-name http://localhost:8080 default prj OutOfSync Healthy Manual \napp-name http://localhost:8080 default prj OutOfSync Healthy Manual \n" assert.Equal(t, output, expectation) } @@ -1236,7 +1546,7 @@ func TestPrintApplicationTableWide(t *testing.T) { return nil }) assert.NoError(t, err) - expectation := "NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET\napp-name http://localhost:8080 default prj OutOfSync Healthy https://github.com/argoproj/argocd-example-apps guestbook 123\napp-name http://localhost:8080 default prj OutOfSync Healthy https://github.com/argoproj/argocd-example-apps guestbook 123\n" + expectation := "NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET\napp-name http://localhost:8080 default prj OutOfSync Healthy Manual https://github.com/argoproj/argocd-example-apps guestbook 123\napp-name http://localhost:8080 default prj OutOfSync Healthy Manual https://github.com/argoproj/argocd-example-apps guestbook 123\n" assert.Equal(t, output, expectation) } @@ -1419,8 +1729,8 @@ func TestCheckResourceStatus(t *testing.T) { func Test_hasAppChanged(t *testing.T) { type args struct { - appReq *argoappv1.Application - appRes *argoappv1.Application + appReq *v1alpha1.Application + appRes *v1alpha1.Application upsert bool } tests := []struct { @@ -1482,19 +1792,120 @@ func Test_hasAppChanged(t *testing.T) { } } -func testApp(name, project string, labels map[string]string, annotations map[string]string, finalizers []string) *argoappv1.Application { - return &argoappv1.Application{ +func testApp(name, project string, labels map[string]string, annotations map[string]string, finalizers []string) *v1alpha1.Application { + return &v1alpha1.Application{ ObjectMeta: metav1.ObjectMeta{ Name: name, Labels: labels, Annotations: annotations, Finalizers: finalizers, }, - Spec: argoappv1.ApplicationSpec{ - Source: &argoappv1.ApplicationSource{ + Spec: v1alpha1.ApplicationSpec{ + Source: &v1alpha1.ApplicationSource{ RepoURL: "https://github.com/argoproj/argocd-example-apps.git", }, Project: project, }, } } + +type fakeAcdClient struct{} + +func (c *fakeAcdClient) ClientOptions() argocdclient.ClientOptions { + return argocdclient.ClientOptions{} +} +func (c *fakeAcdClient) HTTPClient() (*http.Client, error) { return nil, nil } +func (c *fakeAcdClient) OIDCConfig(context.Context, *settingspkg.Settings) (*oauth2.Config, *oidc.Provider, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewRepoClient() (io.Closer, repositorypkg.RepositoryServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewRepoClientOrDie() (io.Closer, repositorypkg.RepositoryServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) NewRepoCredsClient() (io.Closer, repocredspkg.RepoCredsServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewRepoCredsClientOrDie() (io.Closer, repocredspkg.RepoCredsServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) NewCertClient() (io.Closer, certificatepkg.CertificateServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewCertClientOrDie() (io.Closer, certificatepkg.CertificateServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) NewClusterClient() (io.Closer, clusterpkg.ClusterServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewClusterClientOrDie() (io.Closer, clusterpkg.ClusterServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) NewGPGKeyClient() (io.Closer, gpgkeypkg.GPGKeyServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewGPGKeyClientOrDie() (io.Closer, gpgkeypkg.GPGKeyServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) NewApplicationClient() (io.Closer, applicationpkg.ApplicationServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewApplicationSetClient() (io.Closer, applicationsetpkg.ApplicationSetServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewApplicationClientOrDie() (io.Closer, applicationpkg.ApplicationServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) NewApplicationSetClientOrDie() (io.Closer, applicationsetpkg.ApplicationSetServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) NewNotificationClient() (io.Closer, notificationpkg.NotificationServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewNotificationClientOrDie() (io.Closer, notificationpkg.NotificationServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) NewSessionClient() (io.Closer, sessionpkg.SessionServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewSessionClientOrDie() (io.Closer, sessionpkg.SessionServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) NewSettingsClient() (io.Closer, settingspkg.SettingsServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewSettingsClientOrDie() (io.Closer, settingspkg.SettingsServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) NewVersionClient() (io.Closer, versionpkg.VersionServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewVersionClientOrDie() (io.Closer, versionpkg.VersionServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) NewProjectClient() (io.Closer, projectpkg.ProjectServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewProjectClientOrDie() (io.Closer, projectpkg.ProjectServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) NewAccountClient() (io.Closer, accountpkg.AccountServiceClient, error) { + return nil, nil, nil +} +func (c *fakeAcdClient) NewAccountClientOrDie() (io.Closer, accountpkg.AccountServiceClient) { + return nil, nil +} +func (c *fakeAcdClient) WatchApplicationWithRetry(ctx context.Context, appName string, revision string) chan *v1alpha1.ApplicationWatchEvent { + appEventsCh := make(chan *v1alpha1.ApplicationWatchEvent) + + go func() { + modifiedEvent := new(v1alpha1.ApplicationWatchEvent) + modifiedEvent.Type = k8swatch.Modified + appEventsCh <- modifiedEvent + deletedEvent := new(v1alpha1.ApplicationWatchEvent) + deletedEvent.Type = k8swatch.Deleted + appEventsCh <- deletedEvent + }() + return appEventsCh +} diff --git a/cmd/argocd/commands/applicationset.go b/cmd/argocd/commands/applicationset.go index 678d5699b9d19..f5ed6a15b6208 100644 --- a/cmd/argocd/commands/applicationset.go +++ b/cmd/argocd/commands/applicationset.go @@ -16,6 +16,7 @@ import ( argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" "github.com/argoproj/argo-cd/v2/pkg/apiclient/applicationset" arogappsetv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/cli" "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/grpc" @@ -66,6 +67,10 @@ func NewApplicationSetGetCommand(clientOpts *argocdclient.ClientOptions) *cobra. var command = &cobra.Command{ Use: "get APPSETNAME", Short: "Get ApplicationSet details", + Example: templates.Examples(` + # Get ApplicationSets + argocd appset get APPSETNAME + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -76,8 +81,10 @@ func NewApplicationSetGetCommand(clientOpts *argocdclient.ClientOptions) *cobra. acdClient := headless.NewClientOrDie(clientOpts, c) conn, appIf := acdClient.NewApplicationSetClientOrDie() defer argoio.Close(conn) - appSetName := args[0] - appSet, err := appIf.Get(ctx, &applicationset.ApplicationSetGetQuery{Name: appSetName}) + + appSetName, appSetNs := argo.ParseFromQualifiedName(args[0], "") + + appSet, err := appIf.Get(ctx, &applicationset.ApplicationSetGetQuery{Name: appSetName, AppsetNamespace: appSetNs}) errors.CheckError(err) switch output { @@ -144,7 +151,7 @@ func NewApplicationSetCreateCommand(clientOpts *argocdclient.ClientOptions) *cob defer argoio.Close(conn) // Get app before creating to see if it is being updated or no change - existing, err := appIf.Get(ctx, &applicationset.ApplicationSetGetQuery{Name: appset.Name}) + existing, err := appIf.Get(ctx, &applicationset.ApplicationSetGetQuery{Name: appset.Name, AppsetNamespace: appset.Namespace}) if grpc.UnwrapGRPCStatus(err).Code() != codes.NotFound { errors.CheckError(err) } @@ -176,9 +183,10 @@ func NewApplicationSetCreateCommand(clientOpts *argocdclient.ClientOptions) *cob // NewApplicationSetListCommand returns a new instance of an `argocd appset list` command func NewApplicationSetListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - output string - selector string - projects []string + output string + selector string + projects []string + appSetNamespace string ) var command = &cobra.Command{ Use: "list", @@ -192,7 +200,7 @@ func NewApplicationSetListCommand(clientOpts *argocdclient.ClientOptions) *cobra conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationSetClientOrDie() defer argoio.Close(conn) - appsets, err := appIf.List(ctx, &applicationset.ApplicationSetListQuery{Selector: selector, Projects: projects}) + appsets, err := appIf.List(ctx, &applicationset.ApplicationSetListQuery{Selector: selector, Projects: projects, AppsetNamespace: appSetNamespace}) errors.CheckError(err) appsetList := appsets.Items @@ -213,6 +221,7 @@ func NewApplicationSetListCommand(clientOpts *argocdclient.ClientOptions) *cobra command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: wide|name|json|yaml") command.Flags().StringVarP(&selector, "selector", "l", "", "List applicationsets by label") command.Flags().StringArrayVarP(&projects, "project", "p", []string{}, "Filter by project name") + command.Flags().StringVarP(&appSetNamespace, "appset-namespace", "N", "", "Only list applicationsets in namespace") return command } @@ -245,18 +254,22 @@ func NewApplicationSetDeleteCommand(clientOpts *argocdclient.ClientOptions) *cob if promptFlag.Changed && promptFlag.Value.String() == "true" { noPrompt = true } - for _, appsetName := range args { + for _, appSetQualifiedName := range args { + + appSetName, appSetNs := argo.ParseFromQualifiedName(appSetQualifiedName, "") + appsetDeleteReq := applicationset.ApplicationSetDeleteRequest{ - Name: appsetName, + Name: appSetName, + AppsetNamespace: appSetNs, } if isTerminal && !noPrompt { var lowercaseAnswer string if numOfApps == 1 { - lowercaseAnswer = cli.AskToProceedS("Are you sure you want to delete '" + appsetName + "' and all its Applications? [y/n] ") + lowercaseAnswer = cli.AskToProceedS("Are you sure you want to delete '" + appSetQualifiedName + "' and all its Applications? [y/n] ") } else { if !isConfirmAll { - lowercaseAnswer = cli.AskToProceedS("Are you sure you want to delete '" + appsetName + "' and all its Applications? [y/n/A] where 'A' is to delete all specified ApplicationSets and their Applications without prompting") + lowercaseAnswer = cli.AskToProceedS("Are you sure you want to delete '" + appSetQualifiedName + "' and all its Applications? [y/n/A] where 'A' is to delete all specified ApplicationSets and their Applications without prompting") if lowercaseAnswer == "a" || lowercaseAnswer == "all" { lowercaseAnswer = "y" isConfirmAll = true @@ -268,9 +281,9 @@ func NewApplicationSetDeleteCommand(clientOpts *argocdclient.ClientOptions) *cob if lowercaseAnswer == "y" || lowercaseAnswer == "yes" { _, err := appIf.Delete(ctx, &appsetDeleteReq) errors.CheckError(err) - fmt.Printf("applicationset '%s' deleted\n", appsetName) + fmt.Printf("applicationset '%s' deleted\n", appSetQualifiedName) } else { - fmt.Println("The command to delete '" + appsetName + "' was cancelled.") + fmt.Println("The command to delete '" + appSetQualifiedName + "' was cancelled.") } } else { _, err := appIf.Delete(ctx, &appsetDeleteReq) @@ -286,7 +299,7 @@ func NewApplicationSetDeleteCommand(clientOpts *argocdclient.ClientOptions) *cob // Print simple list of application names func printApplicationSetNames(apps []arogappsetv1.ApplicationSet) { for _, app := range apps { - fmt.Println(app.Name) + fmt.Println(app.QualifiedName()) } } @@ -294,12 +307,12 @@ func printApplicationSetNames(apps []arogappsetv1.ApplicationSet) { func printApplicationSetTable(apps []arogappsetv1.ApplicationSet, output *string) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) var fmtStr string - headers := []interface{}{"NAME", "NAMESPACE", "PROJECT", "SYNCPOLICY", "CONDITIONS"} + headers := []interface{}{"NAME", "PROJECT", "SYNCPOLICY", "CONDITIONS"} if *output == "wide" { - fmtStr = "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" + fmtStr = "%s\t%s\t%s\t%s\t%s\t%s\t%s\n" headers = append(headers, "REPO", "PATH", "TARGET") } else { - fmtStr = "%s\t%s\t%s\t%s\t%s\n" + fmtStr = "%s\t%s\t%s\t%s\n" } _, _ = fmt.Fprintf(w, fmtStr, headers...) for _, app := range apps { @@ -310,8 +323,7 @@ func printApplicationSetTable(apps []arogappsetv1.ApplicationSet, output *string } } vals := []interface{}{ - app.ObjectMeta.Name, - app.ObjectMeta.Namespace, + app.QualifiedName(), app.Spec.Template.Spec.Project, app.Spec.SyncPolicy, conditions, @@ -334,25 +346,30 @@ func getServerForAppSet(appSet *arogappsetv1.ApplicationSet) string { func printAppSetSummaryTable(appSet *arogappsetv1.ApplicationSet) { source := appSet.Spec.Template.Spec.GetSource() - fmt.Printf(printOpFmtStr, "Name:", appSet.Name) + fmt.Printf(printOpFmtStr, "Name:", appSet.QualifiedName()) fmt.Printf(printOpFmtStr, "Project:", appSet.Spec.Template.Spec.GetProject()) fmt.Printf(printOpFmtStr, "Server:", getServerForAppSet(appSet)) fmt.Printf(printOpFmtStr, "Namespace:", appSet.Spec.Template.Spec.Destination.Namespace) - fmt.Printf(printOpFmtStr, "Repo:", source.RepoURL) - fmt.Printf(printOpFmtStr, "Target:", source.TargetRevision) - fmt.Printf(printOpFmtStr, "Path:", source.Path) + if !appSet.Spec.Template.Spec.HasMultipleSources() { + fmt.Println("Source:") + } else { + fmt.Println("Sources:") + } printAppSourceDetails(&source) - var syncPolicy string - if appSet.Spec.SyncPolicy != nil && appSet.Spec.Template.Spec.SyncPolicy.Automated != nil { - syncPolicy = "Automated" - if appSet.Spec.Template.Spec.SyncPolicy.Automated.Prune { - syncPolicy += " (Prune)" + var ( + syncPolicyStr string + syncPolicy = appSet.Spec.Template.Spec.SyncPolicy + ) + if syncPolicy != nil && syncPolicy.Automated != nil { + syncPolicyStr = "Automated" + if syncPolicy.Automated.Prune { + syncPolicyStr += " (Prune)" } } else { - syncPolicy = "" + syncPolicyStr = "" } - fmt.Printf(printOpFmtStr, "SyncPolicy:", syncPolicy) + fmt.Printf(printOpFmtStr, "SyncPolicy:", syncPolicyStr) } diff --git a/cmd/argocd/commands/applicationset_test.go b/cmd/argocd/commands/applicationset_test.go index 9937b183e5c29..7740c95a4e63b 100644 --- a/cmd/argocd/commands/applicationset_test.go +++ b/cmd/argocd/commands/applicationset_test.go @@ -1,25 +1,32 @@ package commands import ( + "io" + "os" "testing" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - arogappsetv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestPrintApplicationSetNames(t *testing.T) { output, _ := captureOutput(func() error { - appSet := &arogappsetv1.ApplicationSet{ + appSet := &v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, } - printApplicationSetNames([]arogappsetv1.ApplicationSet{*appSet, *appSet}) + appSet2 := &v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "team-one", + Name: "test", + }, + } + printApplicationSetNames([]v1alpha1.ApplicationSet{*appSet, *appSet2}) return nil }) - expectation := "test\ntest\n" + expectation := "test\nteam-one/test\n" if output != expectation { t.Fatalf("Incorrect print params output %q, should be %q", output, expectation) } @@ -27,44 +34,200 @@ func TestPrintApplicationSetNames(t *testing.T) { func TestPrintApplicationSetTable(t *testing.T) { output, err := captureOutput(func() error { - app := &arogappsetv1.ApplicationSet{ + app := &v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "app-name", }, - Spec: arogappsetv1.ApplicationSetSpec{ - Generators: []arogappsetv1.ApplicationSetGenerator{ - arogappsetv1.ApplicationSetGenerator{ - Git: &arogappsetv1.GitGenerator{ + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ + { + Git: &v1alpha1.GitGenerator{ + RepoURL: "https://github.com/argoproj/argo-cd.git", + Revision: "head", + Directories: []v1alpha1.GitDirectoryGeneratorItem{ + { + Path: "applicationset/examples/git-generator-directory/cluster-addons/*", + }, + }, + }, + }, + }, + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ + Project: "default", + }, + }, + }, + Status: v1alpha1.ApplicationSetStatus{ + Conditions: []v1alpha1.ApplicationSetCondition{ + { + Status: v1alpha1.ApplicationSetConditionStatusTrue, + Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, + }, + }, + }, + } + + app2 := &v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app-name", + Namespace: "team-two", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ + { + Git: &v1alpha1.GitGenerator{ RepoURL: "https://github.com/argoproj/argo-cd.git", Revision: "head", - Directories: []arogappsetv1.GitDirectoryGeneratorItem{ - arogappsetv1.GitDirectoryGeneratorItem{ + Directories: []v1alpha1.GitDirectoryGeneratorItem{ + { Path: "applicationset/examples/git-generator-directory/cluster-addons/*", }, }, }, }, }, - Template: arogappsetv1.ApplicationSetTemplate{ + Template: v1alpha1.ApplicationSetTemplate{ Spec: v1alpha1.ApplicationSpec{ Project: "default", }, }, }, - Status: arogappsetv1.ApplicationSetStatus{ - Conditions: []arogappsetv1.ApplicationSetCondition{ - arogappsetv1.ApplicationSetCondition{ + Status: v1alpha1.ApplicationSetStatus{ + Conditions: []v1alpha1.ApplicationSetCondition{ + { Status: v1alpha1.ApplicationSetConditionStatusTrue, - Type: arogappsetv1.ApplicationSetConditionResourcesUpToDate, + Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, }, }, }, } output := "table" - printApplicationSetTable([]arogappsetv1.ApplicationSet{*app, *app}, &output) + printApplicationSetTable([]v1alpha1.ApplicationSet{*app, *app2}, &output) return nil }) assert.NoError(t, err) - expectation := "NAME NAMESPACE PROJECT SYNCPOLICY CONDITIONS\napp-name default nil [{ResourcesUpToDate True }]\napp-name default nil [{ResourcesUpToDate True }]\n" + expectation := "NAME PROJECT SYNCPOLICY CONDITIONS\napp-name default nil [{ResourcesUpToDate True }]\nteam-two/app-name default nil [{ResourcesUpToDate True }]\n" assert.Equal(t, expectation, output) } + +func TestPrintAppSetSummaryTable(t *testing.T) { + baseAppSet := &v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app-name", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Generators: []v1alpha1.ApplicationSetGenerator{ + { + Git: &v1alpha1.GitGenerator{ + RepoURL: "https://github.com/argoproj/argo-cd.git", + Revision: "head", + Directories: []v1alpha1.GitDirectoryGeneratorItem{ + { + Path: "applicationset/examples/git-generator-directory/cluster-addons/*", + }, + }, + }, + }, + }, + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ + Project: "default", + }, + }, + }, + Status: v1alpha1.ApplicationSetStatus{ + Conditions: []v1alpha1.ApplicationSetCondition{ + { + Status: v1alpha1.ApplicationSetConditionStatusTrue, + Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, + }, + }, + }, + } + + appsetSpecSyncPolicy := baseAppSet.DeepCopy() + appsetSpecSyncPolicy.Spec.SyncPolicy = &v1alpha1.ApplicationSetSyncPolicy{ + PreserveResourcesOnDeletion: true, + } + + appSetTemplateSpecSyncPolicy := baseAppSet.DeepCopy() + appSetTemplateSpecSyncPolicy.Spec.Template.Spec.SyncPolicy = &v1alpha1.SyncPolicy{ + Automated: &v1alpha1.SyncPolicyAutomated{ + SelfHeal: true, + }, + } + + appSetBothSyncPolicies := baseAppSet.DeepCopy() + appSetBothSyncPolicies.Spec.SyncPolicy = &v1alpha1.ApplicationSetSyncPolicy{ + PreserveResourcesOnDeletion: true, + } + appSetBothSyncPolicies.Spec.Template.Spec.SyncPolicy = &v1alpha1.SyncPolicy{ + Automated: &v1alpha1.SyncPolicyAutomated{ + SelfHeal: true, + }, + } + + for _, tt := range []struct { + name string + appSet *v1alpha1.ApplicationSet + expectedOutput string + }{ + { + name: "appset with only spec.syncPolicy set", + appSet: appsetSpecSyncPolicy, + expectedOutput: `Name: app-name +Project: default +Server: +Namespace: +Source: +- Repo: + Target: +SyncPolicy: +`, + }, + { + name: "appset with only spec.template.spec.syncPolicy set", + appSet: appSetTemplateSpecSyncPolicy, + expectedOutput: `Name: app-name +Project: default +Server: +Namespace: +Source: +- Repo: + Target: +SyncPolicy: Automated +`, + }, + { + name: "appset with both spec.SyncPolicy and spec.template.spec.syncPolicy set", + appSet: appSetBothSyncPolicies, + expectedOutput: `Name: app-name +Project: default +Server: +Namespace: +Source: +- Repo: + Target: +SyncPolicy: Automated +`, + }, + } { + t.Run(tt.name, func(t *testing.T) { + oldStdout := os.Stdout + defer func() { + os.Stdout = oldStdout + }() + + r, w, _ := os.Pipe() + os.Stdout = w + + printAppSetSummaryTable(tt.appSet) + w.Close() + + out, err := io.ReadAll(r) + assert.NoError(t, err) + assert.Equal(t, tt.expectedOutput, string(out)) + }) + } +} diff --git a/cmd/argocd/commands/bcrypt.go b/cmd/argocd/commands/bcrypt.go index 2aaf94fe9450d..6d8f87fd447a5 100644 --- a/cmd/argocd/commands/bcrypt.go +++ b/cmd/argocd/commands/bcrypt.go @@ -8,14 +8,16 @@ import ( "golang.org/x/crypto/bcrypt" ) -// bcryptCmd represents the bcrypt command +// NewBcryptCmd represents the bcrypt command func NewBcryptCmd() *cobra.Command { var ( password string ) var bcryptCmd = &cobra.Command{ Use: "bcrypt", - Short: "Generate bcrypt hash for the admin password", + Short: "Generate bcrypt hash for any password", + Example: `# Generate bcrypt hash for any password +argocd account bcrypt --password YOUR_PASSWORD`, Run: func(cmd *cobra.Command, args []string) { bytePassword := []byte(password) // Hashing the password diff --git a/cmd/argocd/commands/bcrypt_test.go b/cmd/argocd/commands/bcrypt_test.go index c5949977a1425..ec00a73b0dcba 100644 --- a/cmd/argocd/commands/bcrypt_test.go +++ b/cmd/argocd/commands/bcrypt_test.go @@ -12,7 +12,7 @@ func TestGeneratePassword(t *testing.T) { bcryptCmd := NewBcryptCmd() bcryptCmd.SetArgs([]string{"--password", "abc"}) output := new(bytes.Buffer) - bcryptCmd.SetOutput(output) + bcryptCmd.SetOut(output) err := bcryptCmd.Execute() if err != nil { return diff --git a/cmd/argocd/commands/cert.go b/cmd/argocd/commands/cert.go index 17f13a87d2a7d..d443d57e337d4 100644 --- a/cmd/argocd/commands/cert.go +++ b/cmd/argocd/commands/cert.go @@ -130,12 +130,12 @@ func NewCertAddTLSCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command } }, } - command.Flags().StringVar(&fromFile, "from", "", "read TLS certificate data from file (default is to read from stdin)") + command.Flags().StringVar(&fromFile, "from", "", "Read TLS certificate data from file (default is to read from stdin)") command.Flags().BoolVar(&upsert, "upsert", false, "Replace existing TLS certificate if certificate is different in input") return command } -// NewCertAddCommand returns a new instance of an `argocd cert add` command +// NewCertAddSSHCommand returns a new instance of an `argocd cert add` command func NewCertAddSSHCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( fromFile string @@ -300,9 +300,9 @@ func NewCertListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { } command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide") - command.Flags().StringVar(&sortOrder, "sort", "", "set display sort order for output format wide. One of: hostname|type") - command.Flags().StringVar(&certType, "cert-type", "", "only list certificates of given type, valid: 'ssh','https'") - command.Flags().StringVar(&hostNamePattern, "hostname-pattern", "", "only list certificates for hosts matching given glob-pattern") + command.Flags().StringVar(&sortOrder, "sort", "", "Set display sort order for output format wide. One of: hostname|type") + command.Flags().StringVar(&certType, "cert-type", "", "Only list certificates of given type, valid: 'ssh','https'") + command.Flags().StringVar(&hostNamePattern, "hostname-pattern", "", "Only list certificates for hosts matching given glob-pattern") return command } diff --git a/cmd/argocd/commands/cluster.go b/cmd/argocd/commands/cluster.go index fa8a50d05c25f..f203b82ae9ac0 100644 --- a/cmd/argocd/commands/cluster.go +++ b/cmd/argocd/commands/cluster.go @@ -93,9 +93,17 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie cmdutil.PrintKubeContexts(configAccess) os.Exit(1) } + + if clusterOpts.InCluster && clusterOpts.ClusterEndpoint != "" { + log.Fatal("Can only use one of --in-cluster or --cluster-endpoint") + return + } + contextName := args[0] conf, err := getRestConfig(pathOpts, contextName) errors.CheckError(err) + clientset, err := kubernetes.NewForConfig(conf) + errors.CheckError(err) managerBearerToken := "" var awsAuthConf *argoappv1.AWSAuthConfig var execProviderConf *argoappv1.ExecProviderConfig @@ -103,6 +111,7 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie awsAuthConf = &argoappv1.AWSAuthConfig{ ClusterName: clusterOpts.AwsClusterName, RoleARN: clusterOpts.AwsRoleArn, + Profile: clusterOpts.AwsProfile, } } else if clusterOpts.ExecProviderCommand != "" { execProviderConf = &argoappv1.ExecProviderConfig{ @@ -114,13 +123,10 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie } } else { // Install RBAC resources for managing the cluster - clientset, err := kubernetes.NewForConfig(conf) - errors.CheckError(err) if clusterOpts.ServiceAccount != "" { managerBearerToken, err = clusterauth.GetServiceAccountBearerToken(clientset, clusterOpts.SystemNamespace, clusterOpts.ServiceAccount, common.BearerTokenTimeout) } else { isTerminal := isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) - if isTerminal && !skipConfirmation { accessLevel := "cluster" if len(clusterOpts.Namespaces) > 0 { @@ -147,9 +153,18 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie contextName = clusterOpts.Name } clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, clusterOpts.ClusterResources, conf, managerBearerToken, awsAuthConf, execProviderConf, labelsMap, annotationsMap) - if clusterOpts.InCluster { + if clusterOpts.InClusterEndpoint() { clst.Server = argoappv1.KubernetesInternalAPIServerAddr + } else if clusterOpts.ClusterEndpoint == string(cmdutil.KubePublicEndpoint) { + endpoint, err := cmdutil.GetKubePublicEndpoint(clientset) + if err != nil || len(endpoint) == 0 { + log.Warnf("Failed to find the cluster endpoint from kube-public data: %v", err) + log.Infof("Falling back to the endpoint '%s' as listed in the kubeconfig context", clst.Server) + endpoint = clst.Server + } + clst.Server = endpoint } + if clusterOpts.Shard >= 0 { clst.Shard = &clusterOpts.Shard } @@ -471,6 +486,23 @@ func NewClusterListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman errors.CheckError(fmt.Errorf("unknown output format: %s", output)) } }, + Example: ` +# List Clusters in Default "Wide" Format +argocd cluster list + +# List Cluster via specifing the server +argocd cluster list --server + +# List Clusters in JSON Format +argocd cluster list -o json --server + +# List Clusters in YAML Format +argocd cluster list -o yaml --server + +# List Clusters that have been added to your Argo CD +argocd cluster list -o server + +`, } command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|server") return command diff --git a/cmd/argocd/commands/common.go b/cmd/argocd/commands/common.go index b7529f768a69a..849b9a48f02b6 100644 --- a/cmd/argocd/commands/common.go +++ b/cmd/argocd/commands/common.go @@ -5,7 +5,7 @@ import ( "fmt" "reflect" - "github.com/ghodss/yaml" + "sigs.k8s.io/yaml" ) const ( diff --git a/cmd/argocd/commands/completion.go b/cmd/argocd/commands/completion.go index 5e587283375ac..7d3f5675ee95e 100644 --- a/cmd/argocd/commands/completion.go +++ b/cmd/argocd/commands/completion.go @@ -204,8 +204,20 @@ To access completions in your current shell, run $ source <(argocd completion bash) Alternatively, write it to a file and source in .bash_profile -For zsh, output to a file in a directory referenced by the $fpath shell -variable. +For zsh, add the following to your ~/.zshrc file: +source <(argocd completion zsh) +compdef _argocd argocd + +Optionally, also add the following, in case you are getting errors involving compdef & compinit such as command not found: compdef: +autoload -Uz compinit +compinit +`, + Example: `# For bash +$ source <(argocd completion bash) + +# For zsh +$ argocd completion zsh > _argocd +$ source _argocd `, Run: func(cmd *cobra.Command, args []string) { if len(args) != 1 { diff --git a/cmd/argocd/commands/context.go b/cmd/argocd/commands/context.go index ec12aa380cd58..51d003b4df9df 100644 --- a/cmd/argocd/commands/context.go +++ b/cmd/argocd/commands/context.go @@ -22,6 +22,14 @@ func NewContextCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { Use: "context [CONTEXT]", Aliases: []string{"ctx"}, Short: "Switch between contexts", + Example: `# List Argo CD Contexts +argocd context + +# Switch Argo CD context +argocd context cd.argoproj.io + +# Delete Argo CD context +argocd context cd.argoproj.io --delete`, Run: func(c *cobra.Command, args []string) { localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath) diff --git a/cmd/argocd/commands/gpg.go b/cmd/argocd/commands/gpg.go index 7a48a915bebec..73768fc18a324 100644 --- a/cmd/argocd/commands/gpg.go +++ b/cmd/argocd/commands/gpg.go @@ -14,6 +14,7 @@ import ( appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/util/errors" argoio "github.com/argoproj/argo-cd/v2/util/io" + "github.com/argoproj/argo-cd/v2/util/templates" ) // NewGPGCommand returns a new instance of an `argocd repo` command @@ -42,6 +43,17 @@ func NewGPGListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var command = &cobra.Command{ Use: "list", Short: "List configured GPG public keys", + Example: templates.Examples(` + # List all configured GPG public keys in wide format (default). + argocd gpg list + + # List all configured GPG public keys in JSON format. + argocd gpg list -o json + + # List all configured GPG public keys in YAML format. + argocd gpg list -o yaml + `), + Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -72,6 +84,17 @@ func NewGPGGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var command = &cobra.Command{ Use: "get KEYID", Short: "Get the GPG public key with ID from the server", + Example: templates.Examples(` + # Get a GPG public key with the specified KEYID in wide format (default). + argocd gpg get KEYID + + # Get a GPG public key with the specified KEYID in JSON format. + argocd gpg get KEYID -o json + + # Get a GPG public key with the specified KEYID in YAML format. + argocd gpg get KEYID -o yaml + `), + Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -109,6 +132,11 @@ func NewGPGAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var command = &cobra.Command{ Use: "add", Short: "Adds a GPG public key to the server's keyring", + Example: templates.Examples(` + # Add a GPG public key to the server's keyring from a file. + argocd gpg add --from /path/to/keyfile + `), + Run: func(c *cobra.Command, args []string) { ctx := c.Context() diff --git a/cmd/argocd/commands/headless/headless.go b/cmd/argocd/commands/headless/headless.go index ba72cd5bd2072..eca3cb0fb498a 100644 --- a/cmd/argocd/commands/headless/headless.go +++ b/cmd/argocd/commands/headless/headless.go @@ -11,12 +11,14 @@ import ( "github.com/spf13/cobra" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize" + "github.com/argoproj/argo-cd/v2/common" "github.com/alicebob/miniredis/v2" - "github.com/go-redis/redis/v8" "github.com/golang/protobuf/ptypes/empty" + "github.com/redis/go-redis/v9" log "github.com/sirupsen/logrus" "github.com/spf13/pflag" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/kubernetes" cache2 "k8s.io/client-go/tools/cache" @@ -38,11 +40,14 @@ import ( ) type forwardCacheClient struct { - namespace string - context string - init sync.Once - client cache.CacheClient - err error + namespace string + context string + init sync.Once + client cache.CacheClient + compression cache.RedisCompressionType + err error + redisHaProxyName string + redisName string } func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) error { @@ -50,15 +55,17 @@ func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) overrides := clientcmd.ConfigOverrides{ CurrentContext: c.context, } + redisHaProxyPodLabelSelector := common.LabelKeyAppName + "=" + c.redisHaProxyName + redisPodLabelSelector := common.LabelKeyAppName + "=" + c.redisName redisPort, err := kubeutil.PortForward(6379, c.namespace, &overrides, - "app.kubernetes.io/name=argocd-redis-ha-haproxy", "app.kubernetes.io/name=argocd-redis") + redisHaProxyPodLabelSelector, redisPodLabelSelector) if err != nil { c.err = err return } redisClient := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", redisPort)}) - c.client = cache.NewRedisCache(redisClient, time.Hour, cache.RedisCompressionNone) + c.client = cache.NewRedisCache(redisClient, time.Hour, c.compression) }) if c.err != nil { return c.err @@ -72,6 +79,12 @@ func (c *forwardCacheClient) Set(item *cache.Item) error { }) } +func (c *forwardCacheClient) Rename(oldKey string, newKey string, expiration time.Duration) error { + return c.doLazy(func(client cache.CacheClient) error { + return client.Rename(oldKey, newKey, expiration) + }) +} + func (c *forwardCacheClient) Get(key string, obj interface{}) error { return c.doLazy(func(client cache.CacheClient) error { return client.Get(key, obj) @@ -97,11 +110,13 @@ func (c *forwardCacheClient) NotifyUpdated(key string) error { } type forwardRepoClientset struct { - namespace string - context string - init sync.Once - repoClientset repoapiclient.Clientset - err error + namespace string + context string + init sync.Once + repoClientset repoapiclient.Clientset + err error + repoServerName string + kubeClientset kubernetes.Interface } func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.RepoServerServiceClient, error) { @@ -109,7 +124,20 @@ func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.R overrides := clientcmd.ConfigOverrides{ CurrentContext: c.context, } - repoServerPort, err := kubeutil.PortForward(8081, c.namespace, &overrides, "app.kubernetes.io/name=argocd-repo-server") + repoServerName := c.repoServerName + repoServererviceLabelSelector := common.LabelKeyComponentRepoServer + "=" + common.LabelValueComponentRepoServer + repoServerServices, err := c.kubeClientset.CoreV1().Services(c.namespace).List(context.Background(), v1.ListOptions{LabelSelector: repoServererviceLabelSelector}) + if err != nil { + c.err = err + return + } + if len(repoServerServices.Items) > 0 { + if repoServerServicelabel, ok := repoServerServices.Items[0].Labels[common.LabelKeyAppName]; ok && repoServerServicelabel != "" { + repoServerName = repoServerServicelabel + } + } + repoServerPodLabelSelector := common.LabelKeyAppName + "=" + repoServerName + repoServerPort, err := kubeutil.PortForward(8081, c.namespace, &overrides, repoServerPodLabelSelector) if err != nil { c.err = err return @@ -126,36 +154,47 @@ func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.R func testAPI(ctx context.Context, clientOpts *apiclient.ClientOptions) error { apiClient, err := apiclient.NewClient(clientOpts) if err != nil { - return err + return fmt.Errorf("failed to create API client: %w", err) } closer, versionClient, err := apiClient.NewVersionClient() if err != nil { - return err + return fmt.Errorf("failed to create version client: %w", err) } defer io.Close(closer) _, err = versionClient.Version(ctx, &empty.Empty{}) - return err + if err != nil { + return fmt.Errorf("failed to get version: %w", err) + } + return nil } -// StartLocalServer allows executing command in a headless mode: on the fly starts Argo CD API server and -// changes provided client options to use started API server port -func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string) error { - flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError) - clientConfig := cli.AddKubectlFlagsToSet(flags) +// MaybeStartLocalServer allows executing command in a headless mode. If we're in core mode, starts the Argo CD API +// server on the fly and changes provided client options to use started API server port. +// +// If the clientOpts enables core mode, but the local config does not have core mode enabled, this function will +// not start the local server. +func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string, compression cache.RedisCompressionType, clientConfig clientcmd.ClientConfig) error { + if clientConfig == nil { + flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError) + clientConfig = cli.AddKubectlFlagsToSet(flags) + } startInProcessAPI := clientOpts.Core if !startInProcessAPI { + // Core mode is enabled on client options. Check the local config to see if we should start the API server. localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath) if err != nil { - return err + return fmt.Errorf("error reading local config: %w", err) } if localCfg != nil { configCtx, err := localCfg.ResolveContext(clientOpts.Context) if err != nil { - return err + return fmt.Errorf("error resolving context: %w", err) } + // There was a local config file, so determine whether core mode is enabled per the config file. startInProcessAPI = configCtx.Server.Core } } + // If we're in core mode, start the API server on the fly. if !startInProcessAPI { return nil } @@ -172,7 +211,7 @@ func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, addr := fmt.Sprintf("%s:0", *address) ln, err := net.Listen("tcp", addr) if err != nil { - return err + return fmt.Errorf("failed to listen on %q: %w", addr, err) } port = &ln.Addr().(*net.TCPAddr).Port io.Close(ln) @@ -180,27 +219,27 @@ func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, restConfig, err := clientConfig.ClientConfig() if err != nil { - return err + return fmt.Errorf("error creating client config: %w", err) } appClientset, err := appclientset.NewForConfig(restConfig) if err != nil { - return err + return fmt.Errorf("error creating app clientset: %w", err) } kubeClientset, err := kubernetes.NewForConfig(restConfig) if err != nil { - return err + return fmt.Errorf("error creating kubernetes clientset: %w", err) } namespace, _, err := clientConfig.Namespace() if err != nil { - return err + return fmt.Errorf("error getting namespace: %w", err) } mr, err := miniredis.Run() if err != nil { - return err + return fmt.Errorf("error running miniredis: %w", err) } - appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr}), time.Hour) + appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr, compression: compression, redisHaProxyName: clientOpts.RedisHaProxyName, redisName: clientOpts.RedisName}), time.Hour) srv := server.NewServer(ctx, server.ArgoCDServerOpts{ EnableGZip: false, Namespace: namespace, @@ -212,14 +251,14 @@ func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, KubeClientset: kubeClientset, Insecure: true, ListenHost: *address, - RepoClientset: &forwardRepoClientset{namespace: namespace, context: ctxStr}, + RepoClientset: &forwardRepoClientset{namespace: namespace, context: ctxStr, repoServerName: clientOpts.RepoServerName, kubeClientset: kubeClientset}, EnableProxyExtension: false, }) srv.Init(ctx) lns, err := srv.Listen() if err != nil { - return err + return fmt.Errorf("failed to listen: %w", err) } go srv.Run(ctx, lns) clientOpts.ServerAddr = fmt.Sprintf("%s:%d", *address, *port) @@ -227,6 +266,7 @@ func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, if !cache2.WaitForCacheSync(ctx.Done(), srv.Initialized) { log.Fatal("Timed out waiting for project cache to sync") } + tries := 5 for i := 0; i < tries; i++ { err = testAPI(ctx, clientOpts) @@ -235,7 +275,10 @@ func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, } time.Sleep(time.Second) } - return err + if err != nil { + return fmt.Errorf("all retries failed: %w", err) + } + return nil } // NewClientOrDie creates a new API client from a set of config options, or fails fatally if the new client creation fails. @@ -243,7 +286,9 @@ func NewClientOrDie(opts *apiclient.ClientOptions, c *cobra.Command) apiclient.C ctx := c.Context() ctxStr := initialize.RetrieveContextIfChanged(c.Flag("context")) - err := StartLocalServer(ctx, opts, ctxStr, nil, nil) + // If we're in core mode, start the API server on the fly and configure the client `opts` to use it. + // If we're not in core mode, this function call will do nothing. + err := MaybeStartLocalServer(ctx, opts, ctxStr, nil, nil, cache.RedisCompressionNone, nil) if err != nil { log.Fatal(err) } diff --git a/cmd/argocd/commands/initialize/cmd.go b/cmd/argocd/commands/initialize/cmd.go index 76a6470f07002..8f9da9f68783f 100644 --- a/cmd/argocd/commands/initialize/cmd.go +++ b/cmd/argocd/commands/initialize/cmd.go @@ -3,12 +3,11 @@ package initialize import ( "github.com/spf13/cobra" "github.com/spf13/pflag" - flag "github.com/spf13/pflag" "github.com/argoproj/argo-cd/v2/util/cli" ) -func RetrieveContextIfChanged(contextFlag *flag.Flag) string { +func RetrieveContextIfChanged(contextFlag *pflag.Flag) string { if contextFlag != nil && contextFlag.Changed { return contextFlag.Value.String() } diff --git a/cmd/argocd/commands/login.go b/cmd/argocd/commands/login.go index 2fc2ce3b32199..abb2b004291c2 100644 --- a/cmd/argocd/commands/login.go +++ b/cmd/argocd/commands/login.go @@ -106,6 +106,7 @@ argocd login cd.argoproj.io --core`, PortForwardNamespace: globalClientOpts.PortForwardNamespace, Headers: globalClientOpts.Headers, KubeOverrides: globalClientOpts.KubeOverrides, + ServerName: globalClientOpts.ServerName, } if ctxName == "" { @@ -175,11 +176,11 @@ argocd login cd.argoproj.io --core`, fmt.Printf("Context '%s' updated\n", ctxName) }, } - command.Flags().StringVar(&ctxName, "name", "", "name to use for the context") - command.Flags().StringVar(&username, "username", "", "the username of an account to authenticate") - command.Flags().StringVar(&password, "password", "", "the password of an account to authenticate") - command.Flags().BoolVar(&sso, "sso", false, "perform SSO login") - command.Flags().IntVar(&ssoPort, "sso-port", DefaultSSOLocalPort, "port to run local OAuth2 login application") + command.Flags().StringVar(&ctxName, "name", "", "Name to use for the context") + command.Flags().StringVar(&username, "username", "", "The username of an account to authenticate") + command.Flags().StringVar(&password, "password", "", "The password of an account to authenticate") + command.Flags().BoolVar(&sso, "sso", false, "Perform SSO login") + command.Flags().IntVar(&ssoPort, "sso-port", DefaultSSOLocalPort, "Port to run local OAuth2 login application") command.Flags(). BoolVar(&skipTestTLS, "skip-test-tls", false, "Skip testing whether the server is configured with TLS (this can help when the command hangs for no apparent reason)") return command diff --git a/cmd/argocd/commands/login_test.go b/cmd/argocd/commands/login_test.go index 1f3289f97d563..3a7411b4b7fa3 100644 --- a/cmd/argocd/commands/login_test.go +++ b/cmd/argocd/commands/login_test.go @@ -7,8 +7,6 @@ import ( "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) diff --git a/cmd/argocd/commands/logout.go b/cmd/argocd/commands/logout.go index e15450299347f..f64c57ccc89cc 100644 --- a/cmd/argocd/commands/logout.go +++ b/cmd/argocd/commands/logout.go @@ -18,6 +18,10 @@ func NewLogoutCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comma Use: "logout CONTEXT", Short: "Log out from Argo CD", Long: "Log out from Argo CD", + Example: `# To log out of argocd +$ argocd logout +# This can be helpful for security reasons or when you want to switch between different Argo CD contexts or accounts. +`, Run: func(c *cobra.Command, args []string) { if len(args) == 0 { c.HelpFunc()(c, args) diff --git a/cmd/argocd/commands/project.go b/cmd/argocd/commands/project.go index be29249613f4c..be7517b843375 100644 --- a/cmd/argocd/commands/project.go +++ b/cmd/argocd/commands/project.go @@ -1,6 +1,7 @@ package commands import ( + "context" "encoding/json" "fmt" "io" @@ -10,11 +11,10 @@ import ( "time" humanize "github.com/dustin/go-humanize" - "github.com/ghodss/yaml" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" @@ -26,6 +26,7 @@ import ( "github.com/argoproj/argo-cd/v2/util/git" "github.com/argoproj/argo-cd/v2/util/gpg" argoio "github.com/argoproj/argo-cd/v2/util/io" + "github.com/argoproj/argo-cd/v2/util/templates" ) type policyOpts struct { @@ -39,6 +40,19 @@ func NewProjectCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var command = &cobra.Command{ Use: "proj", Short: "Manage projects", + Example: templates.Examples(` + # List all available projects + argocd proj list + + # Create a new project with name PROJECT + argocd proj create PROJECT + + # Delete the project with name PROJECT + argocd proj delete PROJECT + + # Edit the information on project with name PROJECT + argocd proj edit PROJECT + `), Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) os.Exit(1) @@ -64,6 +78,8 @@ func NewProjectCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { command.AddCommand(NewProjectWindowsCommand(clientOpts)) command.AddCommand(NewProjectAddOrphanedIgnoreCommand(clientOpts)) command.AddCommand(NewProjectRemoveOrphanedIgnoreCommand(clientOpts)) + command.AddCommand(NewProjectAddSourceNamespace(clientOpts)) + command.AddCommand(NewProjectRemoveSourceNamespace(clientOpts)) return command } @@ -88,6 +104,13 @@ func NewProjectCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm var command = &cobra.Command{ Use: "create PROJECT", Short: "Create a project", + Example: templates.Examples(` + # Create a new project with name PROJECT + argocd proj create PROJECT + + # Create a new project with name PROJECT from a file or URL to a Kubernetes manifest + argocd proj create PROJECT -f FILE|URL + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -118,6 +141,13 @@ func NewProjectSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command var command = &cobra.Command{ Use: "set PROJECT", Short: "Set project parameters", + Example: templates.Examples(` + # Set project parameters with some allowed cluster resources [RES1,RES2,...] for project with name PROJECT + argocd proj set PROJECT --allow-cluster-resource [RES1,RES2,...] + + # Set project parameters with some denied namespaced resources [RES1,RES2,...] for project with name PROJECT + argocd proj set PROJECT ---deny-namespaced-resource [RES1,RES2,...] + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -151,6 +181,10 @@ func NewProjectAddSignatureKeyCommand(clientOpts *argocdclient.ClientOptions) *c var command = &cobra.Command{ Use: "add-signature-key PROJECT KEY-ID", Short: "Add GnuPG signature key to project", + Example: templates.Examples(` + # Add GnuPG signature key KEY-ID to project PROJECT + argocd proj add-signature-key PROJECT KEY-ID + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -189,6 +223,10 @@ func NewProjectRemoveSignatureKeyCommand(clientOpts *argocdclient.ClientOptions) var command = &cobra.Command{ Use: "remove-signature-key PROJECT KEY-ID", Short: "Remove GnuPG signature key from project", + Example: templates.Examples(` + # Remove GnuPG signature key KEY-ID from project PROJECT + argocd proj remove-signature-key PROJECT KEY-ID + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -239,6 +277,13 @@ func NewProjectAddDestinationCommand(clientOpts *argocdclient.ClientOptions) *co var command = &cobra.Command{ Use: "add-destination PROJECT SERVER/NAME NAMESPACE", Short: "Add project destination", + Example: templates.Examples(` + # Add project destination using a server URL (SERVER) in the specified namespace (NAMESPACE) on the project with name PROJECT + argocd proj add-destination PROJECT SERVER NAMESPACE + + # Add project destination using a server name (NAME) in the specified namespace (NAMESPACE) on the project with name PROJECT + argocd proj add-destination PROJECT NAME NAMESPACE --name + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -276,6 +321,10 @@ func NewProjectRemoveDestinationCommand(clientOpts *argocdclient.ClientOptions) var command = &cobra.Command{ Use: "remove-destination PROJECT SERVER NAMESPACE", Short: "Remove project destination", + Example: templates.Examples(` + # Remove the destination (SERVER) from the specified namespace (NAMESPACE) on the project with name PROJECT + argocd proj remove-destination PROJECT SERVER NAMESPACE + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -320,6 +369,13 @@ func NewProjectAddOrphanedIgnoreCommand(clientOpts *argocdclient.ClientOptions) var command = &cobra.Command{ Use: "add-orphaned-ignore PROJECT GROUP KIND", Short: "Add a resource to orphaned ignore list", + Example: templates.Examples(` + # Add a resource of the specified GROUP and KIND to orphaned ignore list on the project with name PROJECT + argocd proj add-orphaned-ignore PROJECT GROUP KIND + + # Add resources of the specified GROUP and KIND using a NAME pattern to orphaned ignore list on the project with name PROJECT + argocd proj add-orphaned-ignore PROJECT GROUP KIND --name NAME + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -363,8 +419,15 @@ func NewProjectRemoveOrphanedIgnoreCommand(clientOpts *argocdclient.ClientOption name string ) var command = &cobra.Command{ - Use: "remove-orphaned-ignore PROJECT GROUP KIND NAME", + Use: "remove-orphaned-ignore PROJECT GROUP KIND", Short: "Remove a resource from orphaned ignore list", + Example: templates.Examples(` + # Remove a resource of the specified GROUP and KIND from orphaned ignore list on the project with name PROJECT + argocd proj remove-orphaned-ignore PROJECT GROUP KIND + + # Remove resources of the specified GROUP and KIND using a NAME pattern from orphaned ignore list on the project with name PROJECT + argocd proj remove-orphaned-ignore PROJECT GROUP KIND --name NAME + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -411,6 +474,10 @@ func NewProjectAddSourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.C var command = &cobra.Command{ Use: "add-source PROJECT URL", Short: "Add project source repository", + Example: templates.Examples(` + # Add a source repository (URL) to the project with name PROJECT + argocd proj add-source PROJECT URL + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -444,6 +511,88 @@ func NewProjectAddSourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.C return command } +// NewProjectAddSourceNamespace returns a new instance of an `argocd proj add-source-namespace` command +func NewProjectAddSourceNamespace(clientOpts *argocdclient.ClientOptions) *cobra.Command { + var command = &cobra.Command{ + Use: "add-source-namespace PROJECT NAMESPACE", + Short: "Add source namespace to the AppProject", + Example: templates.Examples(` + # Add Kubernetes namespace as source namespace to the AppProject where application resources are allowed to be created in. + argocd proj add-source-namespace PROJECT NAMESPACE + `), + Run: func(c *cobra.Command, args []string) { + ctx := c.Context() + + if len(args) != 2 { + c.HelpFunc()(c, args) + os.Exit(1) + } + projName := args[0] + srcNamespace := args[1] + conn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie() + defer argoio.Close(conn) + + proj, err := projIf.Get(ctx, &projectpkg.ProjectQuery{Name: projName}) + errors.CheckError(err) + + for _, item := range proj.Spec.SourceNamespaces { + if item == "*" || item == srcNamespace { + fmt.Printf("Source namespace '*' already allowed in project\n") + return + } + } + proj.Spec.SourceNamespaces = append(proj.Spec.SourceNamespaces, srcNamespace) + _, err = projIf.Update(ctx, &projectpkg.ProjectUpdateRequest{Project: proj}) + errors.CheckError(err) + }, + } + return command +} + +// NewProjectRemoveSourceNamespace returns a new instance of an `argocd proj remove-source-namespace` command +func NewProjectRemoveSourceNamespace(clientOpts *argocdclient.ClientOptions) *cobra.Command { + var command = &cobra.Command{ + Use: "remove-source-namespace PROJECT NAMESPACE", + Short: "Removes the source namespace from the AppProject", + Example: templates.Examples(` + # Remove source NAMESPACE in PROJECT + argocd proj remove-source-namespace PROJECT NAMESPACE + `), + Run: func(c *cobra.Command, args []string) { + ctx := c.Context() + + if len(args) != 2 { + c.HelpFunc()(c, args) + os.Exit(1) + } + projName := args[0] + srcNamespace := args[1] + conn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie() + defer argoio.Close(conn) + + proj, err := projIf.Get(ctx, &projectpkg.ProjectQuery{Name: projName}) + errors.CheckError(err) + + index := -1 + for i, item := range proj.Spec.SourceNamespaces { + if item == srcNamespace && item != "*" { + index = i + break + } + } + if index == -1 { + fmt.Printf("Source namespace '%s' does not exist in project or cannot be removed\n", srcNamespace) + } else { + proj.Spec.SourceNamespaces = append(proj.Spec.SourceNamespaces[:index], proj.Spec.SourceNamespaces[index+1:]...) + _, err = projIf.Update(ctx, &projectpkg.ProjectUpdateRequest{Project: proj}) + errors.CheckError(err) + } + }, + } + + return command +} + func modifyResourcesList(list *[]metav1.GroupKind, add bool, listDesc string, group string, kind string) bool { if add { for _, item := range *list { @@ -453,7 +602,7 @@ func modifyResourcesList(list *[]metav1.GroupKind, add bool, listDesc string, gr } } fmt.Printf("Group '%s' and kind '%s' is added to %s resources\n", group, kind, listDesc) - *list = append(*list, v1.GroupKind{Group: group, Kind: kind}) + *list = append(*list, metav1.GroupKind{Group: group, Kind: kind}) return true } else { index := -1 @@ -473,7 +622,7 @@ func modifyResourcesList(list *[]metav1.GroupKind, add bool, listDesc string, gr } } -func modifyResourceListCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.ClientOptions, allow bool, namespacedList bool) *cobra.Command { +func modifyResourceListCmd(cmdUse, cmdDesc, examples string, clientOpts *argocdclient.ClientOptions, allow bool, namespacedList bool) *cobra.Command { var ( listType string defaultList string @@ -484,8 +633,9 @@ func modifyResourceListCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.Clie defaultList = "allow" } var command = &cobra.Command{ - Use: cmdUse, - Short: cmdDesc, + Use: cmdUse, + Short: cmdDesc, + Example: templates.Examples(examples), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -534,28 +684,44 @@ func modifyResourceListCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.Clie func NewProjectAllowNamespaceResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { use := "allow-namespace-resource PROJECT GROUP KIND" 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) + examples := ` + # Removes a namespaced API resource with specified GROUP and KIND from the deny list or add a namespaced API resource to the allow list for project PROJECT + argocd proj allow-namespace-resource PROJECT GROUP KIND + ` + return modifyResourceListCmd(use, desc, examples, 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 deny list or removes a namespaced API resource from the allow list" - return modifyResourceListCmd(use, desc, clientOpts, false, true) + examples := ` + # Adds a namespaced API resource with specified GROUP and KIND from the deny list or removes a namespaced API resource from the allow list for project PROJECT + argocd proj deny-namespace-resource PROJECT GROUP KIND + ` + return modifyResourceListCmd(use, desc, examples, 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 allow list and adds it to deny list" - return modifyResourceListCmd(use, desc, clientOpts, false, false) + examples := ` + # Removes a cluster-scoped API resource with specified GROUP and KIND from the allow list and adds it to deny list for project PROJECT + argocd proj deny-cluster-resource PROJECT GROUP KIND + ` + return modifyResourceListCmd(use, desc, examples, 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 allow list and removes it from deny list" - return modifyResourceListCmd(use, desc, clientOpts, true, false) + examples := ` + # Adds a cluster-scoped API resource with specified GROUP and KIND to the allow list and removes it from deny list for project PROJECT + argocd proj allow-cluster-resource PROJECT GROUP KIND + ` + return modifyResourceListCmd(use, desc, examples, clientOpts, true, false) } // NewProjectRemoveSourceCommand returns a new instance of an `argocd proj remove-src` command @@ -563,6 +729,10 @@ func NewProjectRemoveSourceCommand(clientOpts *argocdclient.ClientOptions) *cobr var command = &cobra.Command{ Use: "remove-source PROJECT URL", Short: "Remove project source repository", + Example: templates.Examples(` + # Remove URL source repository to project PROJECT + argocd proj remove-source PROJECT URL + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -603,6 +773,10 @@ func NewProjectDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm var command = &cobra.Command{ Use: "delete PROJECT", Short: "Delete project", + Example: templates.Examples(` + # Delete the project with name PROJECT + argocd proj delete PROJECT + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -646,6 +820,13 @@ func NewProjectListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman var command = &cobra.Command{ Use: "list", Short: "List projects", + Example: templates.Examples(` + # List all available projects + argocd proj list + + # List all available projects in yaml format + argocd proj list -o yaml + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -811,6 +992,14 @@ func NewProjectGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command var command = &cobra.Command{ Use: "get PROJECT", Short: "Get project details", + Example: templates.Examples(` + # Get details from project PROJECT + argocd proj get PROJECT + + # Get details from project PROJECT in yaml format + argocd proj get PROJECT -o yaml + + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -819,10 +1008,7 @@ func NewProjectGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command os.Exit(1) } projName := args[0] - conn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie() - defer argoio.Close(conn) - detailedProject, err := projIf.GetDetailedProject(ctx, &projectpkg.ProjectQuery{Name: projName}) - errors.CheckError(err) + detailedProject := getProject(c, clientOpts, ctx, projName) switch output { case "yaml", "json": @@ -839,10 +1025,22 @@ func NewProjectGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command return command } +func getProject(c *cobra.Command, clientOpts *argocdclient.ClientOptions, ctx context.Context, projName string) *projectpkg.DetailedProjectsResponse { + conn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie() + defer argoio.Close(conn) + detailedProject, err := projIf.GetDetailedProject(ctx, &projectpkg.ProjectQuery{Name: projName}) + errors.CheckError(err) + return detailedProject +} + func NewProjectEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var command = &cobra.Command{ Use: "edit PROJECT", Short: "Edit project", + Example: templates.Examples(` + # Edit the information on project with name PROJECT + argocd proj edit PROJECT + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() diff --git a/cmd/argocd/commands/project_role.go b/cmd/argocd/commands/project_role.go index 987e61914d858..5920bac0dc8e4 100644 --- a/cmd/argocd/commands/project_role.go +++ b/cmd/argocd/commands/project_role.go @@ -18,6 +18,7 @@ import ( "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/io" "github.com/argoproj/argo-cd/v2/util/jwt" + "github.com/argoproj/argo-cd/v2/util/templates" ) const ( @@ -56,6 +57,30 @@ func NewProjectRoleAddPolicyCommand(clientOpts *argocdclient.ClientOptions) *cob var command = &cobra.Command{ Use: "add-policy PROJECT ROLE-NAME", Short: "Add a policy to a project role", + Example: `# Before adding new policy +$ argocd proj role get test-project test-role +Role Name: test-role +Description: +Policies: +p, proj:test-project:test-role, projects, get, test-project, allow +JWT Tokens: +ID ISSUED-AT EXPIRES-AT +1696759698 2023-10-08T11:08:18+01:00 (3 hours ago) + +# Add a new policy to allow update to the project +$ argocd proj role add-policy test-project test-role -a update -p allow -o project + +# Policy should be updated +$ argocd proj role get test-project test-role +Role Name: test-role +Description: +Policies: +p, proj:test-project:test-role, projects, get, test-project, allow +p, proj:test-project:test-role, applications, update, test-project/project, allow +JWT Tokens: +ID ISSUED-AT EXPIRES-AT +1696759698 2023-10-08T11:08:18+01:00 (3 hours ago) +`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -93,6 +118,30 @@ func NewProjectRoleRemovePolicyCommand(clientOpts *argocdclient.ClientOptions) * var command = &cobra.Command{ Use: "remove-policy PROJECT ROLE-NAME", Short: "Remove a policy from a role within a project", + Example: `List the policy of the test-role before removing a policy +$ argocd proj role get test-project test-role +Role Name: test-role +Description: +Policies: +p, proj:test-project:test-role, projects, get, test-project, allow +p, proj:test-project:test-role, applications, update, test-project/project, allow +JWT Tokens: +ID ISSUED-AT EXPIRES-AT +1696759698 2023-10-08T11:08:18+01:00 (3 hours ago) + +# Remove the policy to allow update to objects +$ argocd proj role remove-policy test-project test-role -a update -p allow -o project + +# The role should be removed now. +$ argocd proj role get test-project test-role +Role Name: test-role +Description: +Policies: +p, proj:test-project:test-role, projects, get, test-project, allow +JWT Tokens: +ID ISSUED-AT EXPIRES-AT +1696759698 2023-10-08T11:08:18+01:00 (4 hours ago) +`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -140,6 +189,11 @@ func NewProjectRoleCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra. var command = &cobra.Command{ Use: "create PROJECT ROLE-NAME", Short: "Create a project role", + Example: templates.Examples(` + # Create a project role in the "my-project" project with the name "my-role". + argocd proj role create my-project my-role --description "My project role description" + `), + Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -174,8 +228,9 @@ func NewProjectRoleCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra. // NewProjectRoleDeleteCommand returns a new instance of an `argocd proj role delete` command func NewProjectRoleDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var command = &cobra.Command{ - Use: "delete PROJECT ROLE-NAME", - Short: "Delete a project role", + Use: "delete PROJECT ROLE-NAME", + Short: "Delete a project role", + Example: `$ argocd proj role delete test-project test-role`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -223,8 +278,15 @@ func NewProjectRoleCreateTokenCommand(clientOpts *argocdclient.ClientOptions) *c 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", + Example: `$ argocd proj role create-token test-project test-role +Create token succeeded for proj:test-project:test-role. + ID: f316c466-40bd-4cfd-8a8c-1392e92255d4 + Issued At: 2023-10-08T15:21:40+01:00 + Expires At: Never + Token: xxx +`, Aliases: []string{"token-create"}, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -288,8 +350,13 @@ func NewProjectRoleListTokensCommand(clientOpts *argocdclient.ClientOptions) *co useUnixTime bool ) var command = &cobra.Command{ - Use: "list-tokens PROJECT ROLE-NAME", - Short: "List tokens for a given role.", + Use: "list-tokens PROJECT ROLE-NAME", + Short: "List tokens for a given role.", + Example: `$ argocd proj role list-tokens test-project test-role +ID ISSUED AT EXPIRES AT +f316c466-40bd-4cfd-8a8c-1392e92255d4 2023-10-08T15:21:40+01:00 Never +fa9d3517-c52d-434c-9bff-215b38508842 2023-10-08T11:08:18+01:00 Never +`, Aliases: []string{"list-token", "token-list"}, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -339,8 +406,35 @@ func NewProjectRoleListTokensCommand(clientOpts *argocdclient.ClientOptions) *co // 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", + Example: `#Create project test-project +$ argocd proj create test-project + +# Create a role associated with test-project +$ argocd proj role create test-project test-role +Role 'test-role' created + +# Create test-role associated with test-project +$ argocd proj role create-token test-project test-role +Create token succeeded for proj:test-project:test-role. + ID: c312450e-12e1-4e0d-9f65-fac9cb027b32 + Issued At: 2023-10-08T13:58:57+01:00 + Expires At: Never + Token: xxx + +# Get test-role id to input into the delete-token command below +$ argocd proj role get test-project test-role +Role Name: test-role +Description: +Policies: +p, proj:test-project:test-role, projects, get, test-project, allow +JWT Tokens: +ID ISSUED-AT EXPIRES-AT +1696769937 2023-10-08T13:58:57+01:00 (6 minutes ago) + +$ argocd proj role delete-token test-project test-role 1696769937 +`, Aliases: []string{"token-delete", "remove-token"}, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -389,6 +483,15 @@ func NewProjectRoleListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co var command = &cobra.Command{ Use: "list PROJECT", Short: "List all the roles in a project", + Example: templates.Examples(` + # This command will list all the roles in argocd-project in a default table format. + argocd proj role list PROJECT + + # List the roles in the project in formats like json, yaml, wide, or name. + argocd proj role list PROJECT --output json + + `), + Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -424,6 +527,16 @@ func NewProjectRoleGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com var command = &cobra.Command{ Use: "get PROJECT ROLE-NAME", Short: "Get the details of a specific role", + Example: `$ argocd proj role get test-project test-role +Role Name: test-role +Description: +Policies: +p, proj:test-project:test-role, projects, get, test-project, allow +JWT Tokens: +ID ISSUED-AT EXPIRES-AT +1696774900 2023-10-08T15:21:40+01:00 (4 minutes ago) +1696759698 2023-10-08T11:08:18+01:00 (4 hours ago) +`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() diff --git a/cmd/argocd/commands/projectwindows.go b/cmd/argocd/commands/projectwindows.go index 0bc867cc6cf68..93843130ebb13 100644 --- a/cmd/argocd/commands/projectwindows.go +++ b/cmd/argocd/commands/projectwindows.go @@ -22,6 +22,18 @@ func NewProjectWindowsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com roleCommand := &cobra.Command{ Use: "windows", Short: "Manage a project's sync windows", + Example: ` +#Add a sync window to a project +argocd proj windows add my-project \ +--schedule "0 0 * * 1-5" \ +--duration 3600 \ +--prune + +#Delete a sync window from a project +argocd proj windows delete + +#List project sync windows +argocd proj windows list `, Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) os.Exit(1) @@ -42,6 +54,12 @@ func NewProjectWindowsDisableManualSyncCommand(clientOpts *argocdclient.ClientOp Use: "disable-manual-sync PROJECT ID", Short: "Disable manual sync for a sync window", Long: "Disable manual sync for a sync window. Requires ID which can be found by running \"argocd proj windows list PROJECT\"", + Example: ` +#Disable manual sync for a sync window for the Project +argocd proj windows disable-manual-sync PROJECT ID + +#Disbaling manual sync for a windows set on the default project with Id 0 +argocd proj windows disable-manual-sync default 0`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -79,6 +97,15 @@ func NewProjectWindowsEnableManualSyncCommand(clientOpts *argocdclient.ClientOpt Use: "enable-manual-sync PROJECT ID", Short: "Enable manual sync for a sync window", Long: "Enable manual sync for a sync window. Requires ID which can be found by running \"argocd proj windows list PROJECT\"", + Example: ` +#Enabling manual sync for a general case +argocd proj windows enable-manual-sync PROJECT ID + +#Enabling manual sync for a windows set on the default project with Id 2 +argocd proj windows enable-manual-sync default 2 + +#Enabling manual sync with a custom message +argocd proj windows enable-manual-sync my-app-project --message "Manual sync initiated by admin`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -125,6 +152,24 @@ func NewProjectWindowsAddWindowCommand(clientOpts *argocdclient.ClientOptions) * var command = &cobra.Command{ Use: "add PROJECT", Short: "Add a sync window to a project", + Example: ` +#Add a 1 hour allow sync window +argocd proj windows add PROJECT \ + --kind allow \ + --schedule "0 22 * * *" \ + --duration 1h \ + --applications "*" + +#Add a deny sync window with the ability to manually sync. +argocd proj windows add PROJECT \ + --kind deny \ + --schedule "30 10 * * *" \ + --duration 30m \ + --applications "prod-\\*,website" \ + --namespaces "default,\\*-prod" \ + --clusters "prod,staging" \ + --manual-sync + `, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -158,11 +203,17 @@ func NewProjectWindowsAddWindowCommand(clientOpts *argocdclient.ClientOptions) * return command } -// NewProjectWindowsAddWindowCommand returns a new instance of an `argocd proj windows delete` command +// NewProjectWindowsDeleteCommand returns a new instance of an `argocd proj windows delete` command func NewProjectWindowsDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var command = &cobra.Command{ Use: "delete PROJECT ID", Short: "Delete a sync window from a project. Requires ID which can be found by running \"argocd proj windows list PROJECT\"", + Example: ` +#Delete a sync window from a project (default) with ID 0 +argocd proj windows delete default 0 + +#Delete a sync window from a project (new-project) with ID 1 +argocd proj windows delete new-project 1`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -205,6 +256,10 @@ func NewProjectWindowsUpdateCommand(clientOpts *argocdclient.ClientOptions) *cob Use: "update PROJECT ID", Short: "Update a project sync window", Long: "Update a project sync window. Requires ID which can be found by running \"argocd proj windows list PROJECT\"", + Example: `# Change a sync window's schedule +argocd proj windows update PROJECT ID \ + --schedule "0 20 * * *" +`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -253,6 +308,15 @@ func NewProjectWindowsListCommand(clientOpts *argocdclient.ClientOptions) *cobra var command = &cobra.Command{ Use: "list PROJECT", Short: "List project sync windows", + Example: ` +#List project windows +argocd proj windows list PROJECT + +#List project windows in yaml format +argocd proj windows list PROJECT -o yaml + +#List project windows info for a project name (test-project) +argocd proj windows list test-project`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -285,8 +349,8 @@ func NewProjectWindowsListCommand(clientOpts *argocdclient.ClientOptions) *cobra func printSyncWindows(proj *v1alpha1.AppProject) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) var fmtStr string - headers := []interface{}{"ID", "STATUS", "KIND", "SCHEDULE", "DURATION", "APPLICATIONS", "NAMESPACES", "CLUSTERS", "MANUALSYNC"} - fmtStr = "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" + headers := []interface{}{"ID", "STATUS", "KIND", "SCHEDULE", "DURATION", "APPLICATIONS", "NAMESPACES", "CLUSTERS", "MANUALSYNC", "TIMEZONE"} + fmtStr = strings.Repeat("%s\t", len(headers)) + "\n" fmt.Fprintf(w, fmtStr, headers...) if proj.Spec.SyncWindows.HasWindows() { for i, window := range proj.Spec.SyncWindows { @@ -300,6 +364,7 @@ func printSyncWindows(proj *v1alpha1.AppProject) { formatListOutput(window.Namespaces), formatListOutput(window.Clusters), formatManualOutput(window.ManualSync), + window.TimeZone, } fmt.Fprintf(w, fmtStr, vals...) } diff --git a/cmd/argocd/commands/relogin.go b/cmd/argocd/commands/relogin.go index b4c1ef7fe9b81..92affe05b2e5b 100644 --- a/cmd/argocd/commands/relogin.go +++ b/cmd/argocd/commands/relogin.go @@ -84,8 +84,20 @@ func NewReloginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comm errors.CheckError(err) fmt.Printf("Context '%s' updated\n", localCfg.CurrentContext) }, + Example: ` +# Reinitiates the login with previous contexts +argocd relogin + +# Reinitiates the login with password +argocd relogin --password YOUR_PASSWORD + +# Configure direct access using Kubernetes API server +argocd login cd.argoproj.io --core + +# If user logged in with - "argocd login cd.argoproj.io" with sso login +# The command - "argocd relogin" will Reinitiates SSO login and updates the server context`, } - command.Flags().StringVar(&password, "password", "", "the password of an account to authenticate") - command.Flags().IntVar(&ssoPort, "sso-port", DefaultSSOLocalPort, "port to run local OAuth2 login application") + command.Flags().StringVar(&password, "password", "", "The password of an account to authenticate") + command.Flags().IntVar(&ssoPort, "sso-port", DefaultSSOLocalPort, "Port to run local OAuth2 login application") return command } diff --git a/cmd/argocd/commands/relogin_test.go b/cmd/argocd/commands/relogin_test.go new file mode 100644 index 0000000000000..eb6c4cd2d2f2d --- /dev/null +++ b/cmd/argocd/commands/relogin_test.go @@ -0,0 +1,64 @@ +package commands + +import ( + "strconv" + "testing" + + "github.com/stretchr/testify/assert" + + argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" +) + +func TestNewReloginCommand(t *testing.T) { + globalClientOpts := argocdclient.ClientOptions{ + ConfigPath: "/path/to/config", + } + + cmd := NewReloginCommand(&globalClientOpts) + + assert.Equal(t, "relogin", cmd.Use, "Unexpected command Use") + assert.Equal(t, "Refresh an expired authenticate token", cmd.Short, "Unexpected command Short") + assert.Equal(t, "Refresh an expired authenticate token", cmd.Long, "Unexpected command Long") + + // Assert command flags + passwordFlag := cmd.Flags().Lookup("password") + assert.NotNil(t, passwordFlag, "Expected flag --password to be defined") + assert.Equal(t, "", passwordFlag.Value.String(), "Unexpected default value for --password flag") + + ssoPortFlag := cmd.Flags().Lookup("sso-port") + port, err := strconv.Atoi(ssoPortFlag.Value.String()) + assert.NotNil(t, ssoPortFlag, "Expected flag --sso-port to be defined") + assert.NoError(t, err, "Failed to convert sso-port flag value to integer") + assert.Equal(t, 8085, port, "Unexpected default value for --sso-port flag") +} + +func TestNewReloginCommandWithGlobalClientOptions(t *testing.T) { + globalClientOpts := argocdclient.ClientOptions{ + ConfigPath: "/path/to/config", + ServerAddr: "https://argocd-server.example.com", + Insecure: true, + ClientCertFile: "/path/to/client-cert", + ClientCertKeyFile: "/path/to/client-cert-key", + GRPCWeb: true, + GRPCWebRootPath: "/path/to/grpc-web-root-path", + PlainText: true, + Headers: []string{"header1", "header2"}, + } + + cmd := NewReloginCommand(&globalClientOpts) + + assert.Equal(t, "relogin", cmd.Use, "Unexpected command Use") + assert.Equal(t, "Refresh an expired authenticate token", cmd.Short, "Unexpected command Short") + assert.Equal(t, "Refresh an expired authenticate token", cmd.Long, "Unexpected command Long") + + // Assert command flags + passwordFlag := cmd.Flags().Lookup("password") + assert.NotNil(t, passwordFlag, "Expected flag --password to be defined") + assert.Equal(t, "", passwordFlag.Value.String(), "Unexpected default value for --password flag") + + ssoPortFlag := cmd.Flags().Lookup("sso-port") + port, err := strconv.Atoi(ssoPortFlag.Value.String()) + assert.NotNil(t, ssoPortFlag, "Expected flag --sso-port to be defined") + assert.NoError(t, err, "Failed to convert sso-port flag value to integer") + assert.Equal(t, 8085, port, "Unexpected default value for --sso-port flag") +} diff --git a/cmd/argocd/commands/repo.go b/cmd/argocd/commands/repo.go index 1a540951c2231..1a5b4388fbeba 100644 --- a/cmd/argocd/commands/repo.go +++ b/cmd/argocd/commands/repo.go @@ -3,6 +3,7 @@ package commands import ( "fmt" "os" + "strconv" "text/tabwriter" log "github.com/sirupsen/logrus" @@ -28,6 +29,19 @@ func NewRepoCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { c.HelpFunc()(c, args) os.Exit(1) }, + Example: ` +# Add git repository connection parameters +argocd repo add git@git.example.com:repos/repo + +# Get a Configured Repository by URL +argocd repo get https://github.com/yourusername/your-repo.git + +# List Configured Repositories +argocd repo list + +# Remove Repository Credentials +argocd repo rm https://github.com/yourusername/your-repo.git +`, } command.AddCommand(NewRepoAddCommand(clientOpts)) @@ -50,6 +64,12 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { # Add a Git repository via SSH on a non-default port - need to use ssh:// style URLs here argocd repo add ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa + # Add a Git repository via SSH using socks5 proxy with no proxy credentials + argocd repo add ssh://git@github.com/argoproj/argocd-example-apps --ssh-private-key-path ~/id_rsa --proxy socks5://your.proxy.server.ip:1080 + + # Add a Git repository via SSH using socks5 proxy with proxy credentials + argocd repo add ssh://git@github.com/argoproj/argocd-example-apps --ssh-private-key-path ~/id_rsa --proxy socks5://username:password@your.proxy.server.ip:1080 + # Add a private Git repository via HTTPS using username/password and TLS client certificates: argocd repo add https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key @@ -160,6 +180,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { repoOpts.Repo.GithubAppInstallationId = repoOpts.GithubAppInstallationId repoOpts.Repo.GitHubAppEnterpriseBaseURL = repoOpts.GitHubAppEnterpriseBaseURL repoOpts.Repo.Proxy = repoOpts.Proxy + repoOpts.Repo.ForceHttpBasicAuth = repoOpts.ForceHttpBasicAuth if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" { errors.CheckError(fmt.Errorf("Must specify --name for repos of type 'helm'")) @@ -199,6 +220,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { Proxy: repoOpts.Proxy, Project: repoOpts.Repo.Project, GcpServiceAccountKey: repoOpts.Repo.GCPServiceAccountKey, + ForceHttpBasicAuth: repoOpts.Repo.ForceHttpBasicAuth, } _, err := repoIf.ValidateAccess(ctx, &repoAccessReq) errors.CheckError(err) @@ -248,15 +270,12 @@ func printRepoTable(repos appsv1.Repositories) { _, _ = fmt.Fprintf(w, "TYPE\tNAME\tREPO\tINSECURE\tOCI\tLFS\tCREDS\tSTATUS\tMESSAGE\tPROJECT\n") for _, r := range repos { var hasCreds string - if !r.HasCredentials() { - hasCreds = "false" + if r.InheritedCreds { + hasCreds = "inherited" } else { - if r.InheritedCreds { - hasCreds = "inherited" - } else { - hasCreds = "true" - } + hasCreds = strconv.FormatBool(r.HasCredentials()) } + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%v\t%v\t%s\t%s\t%s\t%s\n", r.Type, r.Name, r.Repo, r.IsInsecure(), r.EnableOCI, r.EnableLFS, hasCreds, r.ConnectionState.Status, r.ConnectionState.Message, r.Project) } _ = w.Flush() @@ -309,7 +328,7 @@ func NewRepoListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { }, } command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|url") - command.Flags().StringVar(&refresh, "refresh", "", "Force a cache refresh on connection status") + command.Flags().StringVar(&refresh, "refresh", "", "Force a cache refresh on connection status , must be one of: 'hard'") return command } @@ -360,6 +379,6 @@ func NewRepoGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { }, } command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|url") - command.Flags().StringVar(&refresh, "refresh", "", "Force a cache refresh on connection status") + command.Flags().StringVar(&refresh, "refresh", "", "Force a cache refresh on connection status , must be one of: 'hard'") return command } diff --git a/cmd/argocd/commands/repocreds.go b/cmd/argocd/commands/repocreds.go index 43b2e6f191570..e43b9713a2927 100644 --- a/cmd/argocd/commands/repocreds.go +++ b/cmd/argocd/commands/repocreds.go @@ -17,6 +17,7 @@ import ( "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/git" "github.com/argoproj/argo-cd/v2/util/io" + "github.com/argoproj/argo-cd/v2/util/templates" ) // NewRepoCredsCommand returns a new instance of an `argocd repocreds` command @@ -24,6 +25,16 @@ func NewRepoCredsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command var command = &cobra.Command{ Use: "repocreds", Short: "Manage repository connection parameters", + Example: templates.Examples(` + # Add credentials with user/pass authentication to use for all repositories under the specified URL + argocd repocreds add URL --username USERNAME --password PASSWORD + + # List all the configured repository credentials + argocd repocreds list + + # Remove credentials for the repositories with speficied URL + argocd repocreds rm URL + `), Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) os.Exit(1) @@ -175,6 +186,7 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma command.Flags().BoolVar(&repo.EnableOCI, "enable-oci", false, "Specifies whether helm-oci support should be enabled for this repo") command.Flags().StringVar(&repo.Type, "type", common.DefaultRepoType, "type of the repository, \"git\" or \"helm\"") command.Flags().StringVar(&gcpServiceAccountKeyPath, "gcp-service-account-key-path", "", "service account key for the Google Cloud Platform") + command.Flags().BoolVar(&repo.ForceHttpBasicAuth, "force-http-basic-auth", false, "whether to force basic auth when connecting via HTTP") return command } @@ -183,6 +195,10 @@ func NewRepoCredsRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co var command = &cobra.Command{ Use: "rm CREDSURL", Short: "Remove repository credentials", + Example: templates.Examples(` + # Remove credentials for the repositories with URL https://git.example.com/repos + argocd repocreds rm https://git.example.com/repos/ + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -230,6 +246,19 @@ func NewRepoCredsListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm var command = &cobra.Command{ Use: "list", Short: "List configured repository credentials", + Example: templates.Examples(` + # List all repo urls + argocd repocreds list + + # List all repo urls in json format + argocd repocreds list -o json + + # List all repo urls in yaml format + argocd repocreds list -o yaml + + # List all repo urls in url format + argocd repocreds list -o url + `), Run: func(c *cobra.Command, args []string) { ctx := c.Context() diff --git a/cmd/argocd/commands/root.go b/cmd/argocd/commands/root.go index 91ffde5997b3a..5c3b984e5bff5 100644 --- a/cmd/argocd/commands/root.go +++ b/cmd/argocd/commands/root.go @@ -1,15 +1,19 @@ package commands import ( + "fmt" + "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/admin" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" + "github.com/argoproj/argo-cd/v2/common" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" "github.com/argoproj/argo-cd/v2/util/cli" "github.com/argoproj/argo-cd/v2/util/config" + "github.com/argoproj/argo-cd/v2/util/env" "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/localconfig" ) @@ -55,7 +59,7 @@ func NewCommand() *cobra.Command { command.AddCommand(NewLogoutCommand(&clientOpts)) command.AddCommand(initialize.InitCommand(NewCertCommand(&clientOpts))) command.AddCommand(initialize.InitCommand(NewGPGCommand(&clientOpts))) - command.AddCommand(admin.NewAdminCommand()) + command.AddCommand(admin.NewAdminCommand(&clientOpts)) defaultLocalConfigPath, err := localconfig.DefaultLocalConfigPath() errors.CheckError(err) @@ -76,6 +80,11 @@ func NewCommand() *cobra.Command { command.PersistentFlags().StringVar(&clientOpts.PortForwardNamespace, "port-forward-namespace", config.GetFlag("port-forward-namespace", ""), "Namespace name which should be used for port forwarding") command.PersistentFlags().IntVar(&clientOpts.HttpRetryMax, "http-retry-max", 0, "Maximum number of retries to establish http connection to Argo CD server") command.PersistentFlags().BoolVar(&clientOpts.Core, "core", false, "If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server") + command.PersistentFlags().StringVar(&clientOpts.ServerName, "server-name", env.StringFromEnv(common.EnvServerName, common.DefaultServerName), fmt.Sprintf("Name of the Argo CD API server; set this or the %s environment variable when the server's name label differs from the default, for example when installing via the Helm chart", common.EnvServerName)) + command.PersistentFlags().StringVar(&clientOpts.AppControllerName, "controller-name", env.StringFromEnv(common.EnvAppControllerName, common.DefaultApplicationControllerName), fmt.Sprintf("Name of the Argo CD Application controller; set this or the %s environment variable when the controller's name label differs from the default, for example when installing via the Helm chart", common.EnvAppControllerName)) + command.PersistentFlags().StringVar(&clientOpts.RedisHaProxyName, "redis-haproxy-name", env.StringFromEnv(common.EnvRedisHaProxyName, common.DefaultRedisHaProxyName), fmt.Sprintf("Name of the Redis HA Proxy; set this or the %s environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart", common.EnvRedisHaProxyName)) + command.PersistentFlags().StringVar(&clientOpts.RedisName, "redis-name", env.StringFromEnv(common.EnvRedisName, common.DefaultRedisName), fmt.Sprintf("Name of the Redis deployment; set this or the %s environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart", common.EnvRedisName)) + command.PersistentFlags().StringVar(&clientOpts.RepoServerName, "repo-server-name", env.StringFromEnv(common.EnvRepoServerName, common.DefaultRepoServerName), fmt.Sprintf("Name of the Argo CD Repo server; set this or the %s environment variable when the server's name label differs from the default, for example when installing via the Helm chart", common.EnvRepoServerName)) clientOpts.KubeOverrides = &clientcmd.ConfigOverrides{} command.PersistentFlags().StringVar(&clientOpts.KubeOverrides.CurrentContext, "kube-context", "", "Directs the command to the given kube-context") diff --git a/cmd/argocd/commands/tree.go b/cmd/argocd/commands/tree.go new file mode 100644 index 0000000000000..5261adb5b7f4a --- /dev/null +++ b/cmd/argocd/commands/tree.go @@ -0,0 +1,168 @@ +package commands + +import ( + "fmt" + "strings" + "text/tabwriter" + "time" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/gitops-engine/pkg/health" + "k8s.io/apimachinery/pkg/util/duration" +) + +const ( + firstElemPrefix = `├─` + lastElemPrefix = `└─` + indent = " " + pipe = `│ ` +) + +func extractHealthStatusAndReason(node v1alpha1.ResourceNode) (healthStatus health.HealthStatusCode, reason string) { + if node.Health != nil { + healthStatus = node.Health.Status + reason = node.Health.Message + } + return +} + +func treeViewAppGet(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentToChildMap map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState, w *tabwriter.Writer) { + healthStatus, _ := extractHealthStatusAndReason(parent) + if mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] != nil { + value := mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] + _, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+value.Name, value.Status, value.Health, value.Message) + } else { + _, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+parent.Name, "", healthStatus, "") + } + chs := parentToChildMap[parent.UID] + for i, childUid := range chs { + var p string + switch i { + case len(chs) - 1: + p = prefix + lastElemPrefix + default: + p = prefix + firstElemPrefix + } + treeViewAppGet(p, uidToNodeMap, parentToChildMap, uidToNodeMap[childUid], mapNodeNameToResourceState, w) + } + +} + +func detailedTreeViewAppGet(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState, w *tabwriter.Writer) { + healthStatus, reason := extractHealthStatusAndReason(parent) + var age = "" + if parent.CreatedAt != nil { + age = duration.HumanDuration(time.Since(parent.CreatedAt.Time)) + } + + if mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] != nil { + value := mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] + _, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+value.Name, value.Status, value.Health, age, value.Message, reason) + } else { + _, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+parent.Name, "", healthStatus, age, "", reason) + + } + chs := parentChildMap[parent.UID] + for i, child := range chs { + var p string + switch i { + case len(chs) - 1: + p = prefix + lastElemPrefix + default: + p = prefix + firstElemPrefix + } + detailedTreeViewAppGet(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], mapNodeNameToResourceState, w) + } +} + +func treeViewAppResourcesNotOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) { + if len(parent.ParentRefs) == 0 { + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "No") + } + chs := parentChildMap[parent.UID] + for i, child := range chs { + var p string + switch i { + case len(chs) - 1: + p = prefix + lastElemPrefix + default: + p = prefix + firstElemPrefix + } + treeViewAppResourcesNotOrphaned(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], w) + } +} + +func treeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) { + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "Yes") + chs := parentChildMap[parent.UID] + for i, child := range chs { + var p string + switch i { + case len(chs) - 1: + p = prefix + lastElemPrefix + default: + p = prefix + firstElemPrefix + } + treeViewAppResourcesOrphaned(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], w) + } +} + +func detailedTreeViewAppResourcesNotOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) { + + if len(parent.ParentRefs) == 0 { + healthStatus, reason := extractHealthStatusAndReason(parent) + var age = "" + if parent.CreatedAt != nil { + age = duration.HumanDuration(time.Since(parent.CreatedAt.Time)) + } + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "No", age, healthStatus, reason) + } + chs := parentChildMap[parent.UID] + for i, child := range chs { + var p string + switch i { + case len(chs) - 1: + p = prefix + lastElemPrefix + default: + p = prefix + firstElemPrefix + } + detailedTreeViewAppResourcesNotOrphaned(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], w) + } +} + +func detailedTreeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) { + healthStatus, reason := extractHealthStatusAndReason(parent) + var age = "" + if parent.CreatedAt != nil { + age = duration.HumanDuration(time.Since(parent.CreatedAt.Time)) + } + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "Yes", age, healthStatus, reason) + + chs := parentChildMap[parent.UID] + for i, child := range chs { + var p string + switch i { + case len(chs) - 1: + p = prefix + lastElemPrefix + default: + p = prefix + firstElemPrefix + } + detailedTreeViewAppResourcesOrphaned(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], w) + } +} + +func printPrefix(p string) string { + + if strings.HasSuffix(p, firstElemPrefix) { + p = strings.Replace(p, firstElemPrefix, pipe, strings.Count(p, firstElemPrefix)-1) + } else { + p = strings.ReplaceAll(p, firstElemPrefix, pipe) + } + + if strings.HasSuffix(p, lastElemPrefix) { + p = strings.Replace(p, lastElemPrefix, strings.Repeat(" ", len([]rune(lastElemPrefix))), strings.Count(p, lastElemPrefix)-1) + } else { + p = strings.ReplaceAll(p, lastElemPrefix, strings.Repeat(" ", len([]rune(lastElemPrefix)))) + } + return p +} diff --git a/cmd/argocd/commands/tree_test.go b/cmd/argocd/commands/tree_test.go new file mode 100644 index 0000000000000..91ffb9b963d01 --- /dev/null +++ b/cmd/argocd/commands/tree_test.go @@ -0,0 +1,216 @@ +package commands + +import ( + "bytes" + "testing" + "text/tabwriter" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/stretchr/testify/assert" +) + +func TestTreeViewAppGet(t *testing.T) { + var parent v1alpha1.ResourceNode + parent.ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + objs := make(map[string]v1alpha1.ResourceNode) + objs["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = parent + var child v1alpha1.ResourceNode + child.ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + child.ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + + objs["75c30dce-1b66-414f-a86c-573a74be0f40"] = child + + childMapping := make(map[string][]string) + childMapping["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = []string{"75c30dce-1b66-414f-a86c-573a74be0f40"} + + stateMap := make(map[string]*resourceState) + stateMap["Rollout/numalogic-rollout-demo"] = &resourceState{ + Status: "Running", + Health: "Healthy", + Hook: "", + Message: "No Issues", + Name: "sandbox-rollout-numalogic-demo", + Kind: "Rollout", + Group: "argoproj.io", + } + + buf := &bytes.Buffer{} + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + treeViewAppGet("", objs, childMapping, parent, stateMap, w) + if err := w.Flush(); err != nil { + t.Fatal(err) + } + output := buf.String() + assert.Contains(t, output, "ReplicaSet") + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "numalogic-rollout") + assert.Contains(t, output, "Healthy") + assert.Contains(t, output, "No Issues") +} + +func TestTreeViewDetailedAppGet(t *testing.T) { + var parent v1alpha1.ResourceNode + parent.ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + objs := make(map[string]v1alpha1.ResourceNode) + objs["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = parent + var child v1alpha1.ResourceNode + child.ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + child.ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + child.Health = &v1alpha1.HealthStatus{Status: "Degraded", Message: "Readiness Gate failed"} + objs["75c30dce-1b66-414f-a86c-573a74be0f40"] = child + + childMapping := make(map[string][]string) + childMapping["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = []string{"75c30dce-1b66-414f-a86c-573a74be0f40"} + + stateMap := make(map[string]*resourceState) + stateMap["Rollout/numalogic-rollout-demo"] = &resourceState{ + Status: "Running", + Health: "Healthy", + Hook: "", + Message: "No Issues", + Name: "sandbox-rollout-numalogic-demo", + Kind: "Rollout", + Group: "argoproj.io", + } + + buf := &bytes.Buffer{} + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + detailedTreeViewAppGet("", objs, childMapping, parent, stateMap, w) + if err := w.Flush(); err != nil { + t.Fatal(err) + } + + output := buf.String() + + assert.Contains(t, output, "ReplicaSet") + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "numalogic-rollout") + assert.Contains(t, output, "Healthy") + assert.Contains(t, output, "No Issues") + assert.Contains(t, output, "Degraded") + assert.Contains(t, output, "Readiness Gate failed") +} + +func TestTreeViewAppResources(t *testing.T) { + var parent v1alpha1.ResourceNode + parent.ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + objs := make(map[string]v1alpha1.ResourceNode) + objs["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = parent + var child v1alpha1.ResourceNode + child.ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + child.ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + + objs["75c30dce-1b66-414f-a86c-573a74be0f40"] = child + + childMapping := make(map[string][]string) + childMapping["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = []string{"75c30dce-1b66-414f-a86c-573a74be0f40"} + + buf := &bytes.Buffer{} + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + + treeViewAppResourcesNotOrphaned("", objs, childMapping, parent, w) + + var orphan v1alpha1.ResourceNode + orphan.ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcdnk457d5", UID: "75c30dce-1b66-41hf-a86c-573a74be0f40"} + objsOrphan := make(map[string]v1alpha1.ResourceNode) + objsOrphan["75c30dce-1b66-41hf-a86c-573a74be0f40"] = orphan + orphanchildMapping := make(map[string][]string) + orphanParent := orphan + + treeViewAppResourcesOrphaned("", objsOrphan, orphanchildMapping, orphanParent, w) + if err := w.Flush(); err != nil { + t.Fatal(err) + } + output := buf.String() + + assert.Contains(t, output, "ReplicaSet") + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "numalogic-rollout") + assert.Contains(t, output, "argoproj.io") + assert.Contains(t, output, "No") + assert.Contains(t, output, "Yes") + assert.Contains(t, output, "numalogic-rollout-demo-5dcdnk457d5") +} + +func TestTreeViewDetailedAppResources(t *testing.T) { + var parent v1alpha1.ResourceNode + parent.ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + objs := make(map[string]v1alpha1.ResourceNode) + objs["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = parent + var child v1alpha1.ResourceNode + child.ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + child.ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + objs["75c30dce-1b66-414f-a86c-573a74be0f40"] = child + childMapping := make(map[string][]string) + childMapping["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = []string{"75c30dce-1b66-414f-a86c-573a74be0f40"} + buf := &bytes.Buffer{} + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + detailedTreeViewAppResourcesNotOrphaned("", objs, childMapping, parent, w) + var orphan v1alpha1.ResourceNode + orphan.ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcdnk457d5", UID: "75c30dce-1b66-41hf-a86c-573a74be0f40"} + orphan.Health = &v1alpha1.HealthStatus{ + Status: "Degraded", + Message: "Readiness Gate failed", + } + objsOrphan := make(map[string]v1alpha1.ResourceNode) + objsOrphan["75c30dce-1b66-41hf-a86c-573a74be0f40"] = orphan + + orphanchildMapping := make(map[string][]string) + orphanParent := orphan + detailedTreeViewAppResourcesOrphaned("", objsOrphan, orphanchildMapping, orphanParent, w) + if err := w.Flush(); err != nil { + t.Fatal(err) + } + output := buf.String() + + assert.Contains(t, output, "ReplicaSet") + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "numalogic-rollout") + assert.Contains(t, output, "argoproj.io") + assert.Contains(t, output, "No") + assert.Contains(t, output, "Yes") + assert.Contains(t, output, "numalogic-rollout-demo-5dcdnk457d5") + assert.Contains(t, output, "Degraded") + assert.Contains(t, output, "Readiness Gate failed") +} + +func TestPrintPrefix(t *testing.T) { + tests := []struct { + input string + expected string + name string + }{ + { + input: "", + expected: "", + name: "empty string", + }, + { + input: firstElemPrefix, + expected: firstElemPrefix, + name: "only first element prefix", + }, + { + input: lastElemPrefix, + expected: lastElemPrefix, + name: "only last element prefix", + }, + { + input: firstElemPrefix + firstElemPrefix, + expected: pipe + firstElemPrefix, + name: "double first element prefix", + }, + { + input: firstElemPrefix + lastElemPrefix, + expected: pipe + lastElemPrefix, + name: "first then last element prefix", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := printPrefix(test.input) + assert.Equal(t, test.expected, got) + }) + } +} diff --git a/cmd/argocd/commands/version.go b/cmd/argocd/commands/version.go index 8f3d5b1abfe11..8c69c4195c3ad 100644 --- a/cmd/argocd/commands/version.go +++ b/cmd/argocd/commands/version.go @@ -116,6 +116,9 @@ func printClientVersion(version *common.Version, short bool) string { output += fmt.Sprintf(" GoVersion: %s\n", version.GoVersion) output += fmt.Sprintf(" Compiler: %s\n", version.Compiler) output += fmt.Sprintf(" Platform: %s\n", version.Platform) + if version.ExtraBuildInfo != "" { + output += fmt.Sprintf(" ExtraBuildInfo: %s\n", version.ExtraBuildInfo) + } return output } @@ -147,6 +150,9 @@ func printServerVersion(version *version.VersionMessage, short bool) string { if version.Platform != "" { output += fmt.Sprintf(" Platform: %s\n", version.Platform) } + if version.ExtraBuildInfo != "" { + output += fmt.Sprintf(" ExtraBuildInfo: %s\n", version.ExtraBuildInfo) + } if version.KustomizeVersion != "" { output += fmt.Sprintf(" Kustomize Version: %s\n", version.KustomizeVersion) } diff --git a/cmd/argocd/commands/version_test.go b/cmd/argocd/commands/version_test.go index 88aa689b48669..3312e5ad958b6 100644 --- a/cmd/argocd/commands/version_test.go +++ b/cmd/argocd/commands/version_test.go @@ -12,7 +12,7 @@ import ( func TestShortVersionClient(t *testing.T) { buf := new(bytes.Buffer) cmd := NewVersionCmd(&argocdclient.ClientOptions{}, nil) - cmd.SetOutput(buf) + cmd.SetOut(buf) cmd.SetArgs([]string{"version", "--short", "--client"}) err := cmd.Execute() if err != nil { @@ -26,7 +26,7 @@ func TestShortVersion(t *testing.T) { serverVersion := &version.VersionMessage{Version: "v99.99.99+unknown"} buf := new(bytes.Buffer) cmd := NewVersionCmd(&argocdclient.ClientOptions{}, serverVersion) - cmd.SetOutput(buf) + cmd.SetOut(buf) cmd.SetArgs([]string{"argocd", "version", "--short"}) err := cmd.Execute() if err != nil { diff --git a/cmd/util/app.go b/cmd/util/app.go index e80950533b054..b1693689004c4 100644 --- a/cmd/util/app.go +++ b/cmd/util/app.go @@ -64,11 +64,14 @@ type AppOptions struct { jsonnetExtVarCode []string jsonnetLibs []string kustomizeImages []string + kustomizeReplicas []string kustomizeVersion string kustomizeCommonLabels []string kustomizeCommonAnnotations []string + kustomizeLabelWithoutSelector bool kustomizeForceCommonLabels bool kustomizeForceCommonAnnotations bool + kustomizeNamespace string pluginEnvs []string Validate bool directoryExclude string @@ -77,6 +80,7 @@ type AppOptions struct { retryBackoffDuration time.Duration retryBackoffMaxDuration time.Duration retryBackoffFactor int64 + ref string } func AddAppFlags(command *cobra.Command, opts *AppOptions) { @@ -101,7 +105,7 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) { 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)") command.Flags().BoolVar(&opts.helmSkipCrds, "helm-skip-crds", false, "Skip helm crd installation step") command.Flags().StringVar(&opts.project, "project", "", "Application project name") - command.Flags().StringVar(&opts.syncPolicy, "sync-policy", "", "Set the sync policy (one of: none, automated (aliases of automated: auto, automatic))") + command.Flags().StringVar(&opts.syncPolicy, "sync-policy", "", "Set the sync policy (one of: manual (aliases of manual: none), automated (aliases of automated: auto, automatic))") command.Flags().StringArrayVar(&opts.syncOptions, "sync-option", []string{}, "Add or remove a sync option, 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") @@ -117,93 +121,52 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) { command.Flags().StringArrayVar(&opts.jsonnetExtVarCode, "jsonnet-ext-var-code", []string{}, "Jsonnet ext var") command.Flags().StringArrayVar(&opts.jsonnetLibs, "jsonnet-libs", []string{}, "Additional jsonnet libs (prefixed by repoRoot)") command.Flags().StringArrayVar(&opts.kustomizeImages, "kustomize-image", []string{}, "Kustomize images (e.g. --kustomize-image node:8.15.0 --kustomize-image mysql=mariadb,alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d)") + command.Flags().StringArrayVar(&opts.kustomizeReplicas, "kustomize-replica", []string{}, "Kustomize replicas (e.g. --kustomize-replica my-development=2 --kustomize-replica my-statefulset=4)") command.Flags().StringArrayVar(&opts.pluginEnvs, "plugin-env", []string{}, "Additional plugin envs") command.Flags().BoolVar(&opts.Validate, "validate", true, "Validation of repo and cluster") command.Flags().StringArrayVar(&opts.kustomizeCommonLabels, "kustomize-common-label", []string{}, "Set common labels in Kustomize") command.Flags().StringArrayVar(&opts.kustomizeCommonAnnotations, "kustomize-common-annotation", []string{}, "Set common labels in Kustomize") + command.Flags().BoolVar(&opts.kustomizeLabelWithoutSelector, "kustomize-label-without-selector", false, "Do not apply common label to selectors or templates") command.Flags().BoolVar(&opts.kustomizeForceCommonLabels, "kustomize-force-common-label", false, "Force common labels in Kustomize") command.Flags().BoolVar(&opts.kustomizeForceCommonAnnotations, "kustomize-force-common-annotation", false, "Force common annotations in Kustomize") + command.Flags().StringVar(&opts.kustomizeNamespace, "kustomize-namespace", "", "Kustomize namespace") command.Flags().StringVar(&opts.directoryExclude, "directory-exclude", "", "Set glob expression used to exclude files from application source path") command.Flags().StringVar(&opts.directoryInclude, "directory-include", "", "Set glob expression used to include files from application source path") command.Flags().Int64Var(&opts.retryLimit, "sync-retry-limit", 0, "Max number of allowed sync retries") command.Flags().DurationVar(&opts.retryBackoffDuration, "sync-retry-backoff-duration", argoappv1.DefaultSyncRetryDuration, "Sync retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h)") command.Flags().DurationVar(&opts.retryBackoffMaxDuration, "sync-retry-backoff-max-duration", argoappv1.DefaultSyncRetryMaxDuration, "Max sync retry backoff duration. Input needs to be a duration (e.g. 2m, 1h)") command.Flags().Int64Var(&opts.retryBackoffFactor, "sync-retry-backoff-factor", argoappv1.DefaultSyncRetryFactor, "Factor multiplies the base duration after each failed sync retry") + command.Flags().StringVar(&opts.ref, "ref", "", "Ref is reference to another source within sources field") } -func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, appOpts *AppOptions) int { +func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, appOpts *AppOptions, index int) int { visited := 0 if flags == nil { return visited } + source := spec.GetSourcePtr(index) + if source == nil { + source = &argoappv1.ApplicationSource{} + } + source, visited = ConstructSource(source, *appOpts, flags) + if spec.HasMultipleSources() { + if index == 0 { + spec.Sources[index] = *source + } else if index > 0 { + spec.Sources[index-1] = *source + } else { + spec.Sources = append(spec.Sources, *source) + } + } else { + spec.Source = source + } flags.Visit(func(f *pflag.Flag) { visited++ - source := spec.GetSourcePtr() - if source == nil { - source = &argoappv1.ApplicationSource{} - } + switch f.Name { - case "repo": - source.RepoURL = appOpts.repoURL - case "path": - source.Path = appOpts.appPath - case "helm-chart": - source.Chart = appOpts.chart - case "revision": - source.TargetRevision = appOpts.revision case "revision-history-limit": i := int64(appOpts.revisionHistoryLimit) spec.RevisionHistoryLimit = &i - case "values": - setHelmOpt(source, helmOpts{valueFiles: appOpts.valuesFiles}) - case "ignore-missing-value-files": - setHelmOpt(source, helmOpts{ignoreMissingValueFiles: appOpts.ignoreMissingValueFiles}) - case "values-literal-file": - var data []byte - - // read uri - parsedURL, err := url.ParseRequestURI(appOpts.values) - if err != nil || !(parsedURL.Scheme == "http" || parsedURL.Scheme == "https") { - data, err = os.ReadFile(appOpts.values) - } else { - data, err = config.ReadRemoteFile(appOpts.values) - } - errors.CheckError(err) - setHelmOpt(source, helmOpts{values: string(data)}) - case "release-name": - setHelmOpt(source, helmOpts{releaseName: appOpts.releaseName}) - case "helm-version": - setHelmOpt(source, helmOpts{version: appOpts.helmVersion}) - case "helm-pass-credentials": - setHelmOpt(source, helmOpts{passCredentials: appOpts.helmPassCredentials}) - case "helm-set": - setHelmOpt(source, helmOpts{helmSets: appOpts.helmSets}) - case "helm-set-string": - setHelmOpt(source, helmOpts{helmSetStrings: appOpts.helmSetStrings}) - case "helm-set-file": - setHelmOpt(source, helmOpts{helmSetFiles: appOpts.helmSetFiles}) - case "helm-skip-crds": - setHelmOpt(source, helmOpts{skipCrds: appOpts.helmSkipCrds}) - case "directory-recurse": - if source.Directory != nil { - source.Directory.Recurse = appOpts.directoryRecurse - } else { - source.Directory = &argoappv1.ApplicationSourceDirectory{Recurse: appOpts.directoryRecurse} - } - case "directory-exclude": - if source.Directory != nil { - source.Directory.Exclude = appOpts.directoryExclude - } else { - source.Directory = &argoappv1.ApplicationSourceDirectory{Exclude: appOpts.directoryExclude} - } - case "directory-include": - if source.Directory != nil { - source.Directory.Include = appOpts.directoryInclude - } else { - source.Directory = &argoappv1.ApplicationSourceDirectory{Include: appOpts.directoryInclude} - } - case "config-management-plugin": - source.Plugin = &argoappv1.ApplicationSourcePlugin{Name: appOpts.configManagementPlugin} case "dest-name": spec.Destination.Name = appOpts.destName case "dest-server": @@ -212,41 +175,9 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap spec.Destination.Namespace = appOpts.destNamespace case "project": spec.Project = appOpts.project - case "nameprefix": - setKustomizeOpt(source, kustomizeOpts{namePrefix: appOpts.namePrefix}) - case "namesuffix": - setKustomizeOpt(source, kustomizeOpts{nameSuffix: appOpts.nameSuffix}) - case "kustomize-image": - setKustomizeOpt(source, kustomizeOpts{images: appOpts.kustomizeImages}) - case "kustomize-version": - setKustomizeOpt(source, kustomizeOpts{version: appOpts.kustomizeVersion}) - case "kustomize-common-label": - parsedLabels, err := label.Parse(appOpts.kustomizeCommonLabels) - errors.CheckError(err) - setKustomizeOpt(source, kustomizeOpts{commonLabels: parsedLabels}) - case "kustomize-common-annotation": - parsedAnnotations, err := label.Parse(appOpts.kustomizeCommonAnnotations) - errors.CheckError(err) - setKustomizeOpt(source, kustomizeOpts{commonAnnotations: parsedAnnotations}) - case "kustomize-force-common-label": - setKustomizeOpt(source, kustomizeOpts{forceCommonLabels: appOpts.kustomizeForceCommonLabels}) - case "kustomize-force-common-annotation": - setKustomizeOpt(source, kustomizeOpts{forceCommonAnnotations: appOpts.kustomizeForceCommonAnnotations}) - case "jsonnet-tla-str": - setJsonnetOpt(source, appOpts.jsonnetTlaStr, false) - case "jsonnet-tla-code": - setJsonnetOpt(source, appOpts.jsonnetTlaCode, true) - case "jsonnet-ext-var-str": - setJsonnetOptExtVar(source, appOpts.jsonnetExtVarStr, false) - case "jsonnet-ext-var-code": - setJsonnetOptExtVar(source, appOpts.jsonnetExtVarCode, true) - case "jsonnet-libs": - setJsonnetOptLibs(source, appOpts.jsonnetLibs) - case "plugin-env": - setPluginOptEnvs(source, appOpts.pluginEnvs) case "sync-policy": switch appOpts.syncPolicy { - case "none": + case "none", "manual": if spec.SyncPolicy != nil { spec.SyncPolicy.Automated = nil } @@ -287,7 +218,7 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap Backoff: &argoappv1.Backoff{ Duration: appOpts.retryBackoffDuration.String(), MaxDuration: appOpts.retryBackoffMaxDuration.String(), - Factor: pointer.Int64Ptr(appOpts.retryBackoffFactor), + Factor: pointer.Int64(appOpts.retryBackoffFactor), }, } } else if appOpts.retryLimit == 0 { @@ -300,7 +231,6 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap log.Fatalf("Invalid sync-retry-limit [%d]", appOpts.retryLimit) } } - spec.Source = source }) if flags.Changed("auto-prune") { if spec.SyncPolicy == nil || spec.SyncPolicy.Automated == nil { @@ -328,11 +258,14 @@ type kustomizeOpts struct { namePrefix string nameSuffix string images []string + replicas []string version string commonLabels map[string]string commonAnnotations map[string]string + labelWithoutSelector bool forceCommonLabels bool forceCommonAnnotations bool + namespace string } func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) { @@ -348,12 +281,18 @@ func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) { if opts.nameSuffix != "" { src.Kustomize.NameSuffix = opts.nameSuffix } + if opts.namespace != "" { + src.Kustomize.Namespace = opts.namespace + } if opts.commonLabels != nil { src.Kustomize.CommonLabels = opts.commonLabels } if opts.commonAnnotations != nil { src.Kustomize.CommonAnnotations = opts.commonAnnotations } + if opts.labelWithoutSelector { + src.Kustomize.LabelWithoutSelector = opts.labelWithoutSelector + } if opts.forceCommonLabels { src.Kustomize.ForceCommonLabels = opts.forceCommonLabels } @@ -363,6 +302,14 @@ func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) { for _, image := range opts.images { src.Kustomize.MergeImage(argoappv1.KustomizeImage(image)) } + for _, replica := range opts.replicas { + r, err := argoappv1.NewKustomizeReplica(replica) + if err != nil { + log.Fatal(err) + } + src.Kustomize.MergeReplica(*r) + } + if src.Kustomize.IsZero() { src.Kustomize = nil } @@ -406,7 +353,10 @@ func setHelmOpt(src *argoappv1.ApplicationSource, opts helmOpts) { src.Helm.IgnoreMissingValueFiles = opts.ignoreMissingValueFiles } if len(opts.values) > 0 { - src.Helm.Values = opts.values + err := src.Helm.SetValuesString(opts.values) + if err != nil { + log.Fatal(err) + } } if opts.releaseName != "" { src.Helm.ReleaseName = opts.releaseName @@ -474,11 +424,11 @@ func setJsonnetOptLibs(src *argoappv1.ApplicationSource, libs []string) { // SetParameterOverrides updates an existing or appends a new parameter override in the application // The app is assumed to be a helm app and is expected to be in the form: // param=value -func SetParameterOverrides(app *argoappv1.Application, parameters []string) { +func SetParameterOverrides(app *argoappv1.Application, parameters []string, index int) { if len(parameters) == 0 { return } - source := app.Spec.GetSource() + source := app.Spec.GetSourcePtr(index) var sourceType argoappv1.ApplicationSourceType if st, _ := source.ExplicitType(); st != nil { sourceType = *st @@ -576,7 +526,7 @@ func constructAppsBaseOnName(appName string, labels, annotations, args []string, } appName = args[0] } - appName, appNs := argo.ParseAppQualifiedName(appName, "") + appName, appNs := argo.ParseFromQualifiedName(appName, "") app = &argoappv1.Application{ TypeMeta: v1.TypeMeta{ Kind: application.ApplicationKind, @@ -590,8 +540,8 @@ func constructAppsBaseOnName(appName string, labels, annotations, args []string, Source: &argoappv1.ApplicationSource{}, }, } - SetAppSpecOptions(flags, &app.Spec, &appOpts) - SetParameterOverrides(app, appOpts.Parameters) + SetAppSpecOptions(flags, &app.Spec, &appOpts, 0) + SetParameterOverrides(app, appOpts.Parameters, 0) mergeLabels(app, labels) setAnnotations(app, annotations) return []*argoappv1.Application{ @@ -616,10 +566,15 @@ func constructAppsFromFileUrl(fileURL, appName string, labels, annotations, args if app.Name == "" { return nil, fmt.Errorf("app.Name is empty. --name argument can be used to provide app.Name") } - SetAppSpecOptions(flags, &app.Spec, &appOpts) - SetParameterOverrides(app, appOpts.Parameters) + mergeLabels(app, labels) setAnnotations(app, annotations) + + // do not allow overrides for applications with multiple sources + if !app.Spec.HasMultipleSources() { + SetAppSpecOptions(flags, &app.Spec, &appOpts, 0) + SetParameterOverrides(app, appOpts.Parameters, 0) + } } return apps, nil } @@ -630,9 +585,117 @@ func ConstructApps(fileURL, appName string, labels, annotations, args []string, } else if fileURL != "" { return constructAppsFromFileUrl(fileURL, appName, labels, annotations, args, appOpts, flags) } + return constructAppsBaseOnName(appName, labels, annotations, args, appOpts, flags) } +func ConstructSource(source *argoappv1.ApplicationSource, appOpts AppOptions, flags *pflag.FlagSet) (*argoappv1.ApplicationSource, int) { + visited := 0 + flags.Visit(func(f *pflag.Flag) { + visited++ + switch f.Name { + case "repo": + source.RepoURL = appOpts.repoURL + case "path": + source.Path = appOpts.appPath + case "helm-chart": + source.Chart = appOpts.chart + case "revision": + source.TargetRevision = appOpts.revision + case "values": + setHelmOpt(source, helmOpts{valueFiles: appOpts.valuesFiles}) + case "ignore-missing-value-files": + setHelmOpt(source, helmOpts{ignoreMissingValueFiles: appOpts.ignoreMissingValueFiles}) + case "values-literal-file": + var data []byte + // read uri + parsedURL, err := url.ParseRequestURI(appOpts.values) + if err != nil || !(parsedURL.Scheme == "http" || parsedURL.Scheme == "https") { + data, err = os.ReadFile(appOpts.values) + } else { + data, err = config.ReadRemoteFile(appOpts.values) + } + errors.CheckError(err) + setHelmOpt(source, helmOpts{values: string(data)}) + case "release-name": + setHelmOpt(source, helmOpts{releaseName: appOpts.releaseName}) + case "helm-version": + setHelmOpt(source, helmOpts{version: appOpts.helmVersion}) + case "helm-pass-credentials": + setHelmOpt(source, helmOpts{passCredentials: appOpts.helmPassCredentials}) + case "helm-set": + setHelmOpt(source, helmOpts{helmSets: appOpts.helmSets}) + case "helm-set-string": + setHelmOpt(source, helmOpts{helmSetStrings: appOpts.helmSetStrings}) + case "helm-set-file": + setHelmOpt(source, helmOpts{helmSetFiles: appOpts.helmSetFiles}) + case "helm-skip-crds": + setHelmOpt(source, helmOpts{skipCrds: appOpts.helmSkipCrds}) + case "directory-recurse": + if source.Directory != nil { + source.Directory.Recurse = appOpts.directoryRecurse + } else { + source.Directory = &argoappv1.ApplicationSourceDirectory{Recurse: appOpts.directoryRecurse} + } + case "directory-exclude": + if source.Directory != nil { + source.Directory.Exclude = appOpts.directoryExclude + } else { + source.Directory = &argoappv1.ApplicationSourceDirectory{Exclude: appOpts.directoryExclude} + } + case "directory-include": + if source.Directory != nil { + source.Directory.Include = appOpts.directoryInclude + } else { + source.Directory = &argoappv1.ApplicationSourceDirectory{Include: appOpts.directoryInclude} + } + case "config-management-plugin": + source.Plugin = &argoappv1.ApplicationSourcePlugin{Name: appOpts.configManagementPlugin} + case "nameprefix": + setKustomizeOpt(source, kustomizeOpts{namePrefix: appOpts.namePrefix}) + case "namesuffix": + setKustomizeOpt(source, kustomizeOpts{nameSuffix: appOpts.nameSuffix}) + case "kustomize-image": + setKustomizeOpt(source, kustomizeOpts{images: appOpts.kustomizeImages}) + case "kustomize-replica": + setKustomizeOpt(source, kustomizeOpts{replicas: appOpts.kustomizeReplicas}) + case "kustomize-version": + setKustomizeOpt(source, kustomizeOpts{version: appOpts.kustomizeVersion}) + case "kustomize-namespace": + setKustomizeOpt(source, kustomizeOpts{namespace: appOpts.kustomizeNamespace}) + case "kustomize-common-label": + parsedLabels, err := label.Parse(appOpts.kustomizeCommonLabels) + errors.CheckError(err) + setKustomizeOpt(source, kustomizeOpts{commonLabels: parsedLabels}) + case "kustomize-common-annotation": + parsedAnnotations, err := label.Parse(appOpts.kustomizeCommonAnnotations) + errors.CheckError(err) + setKustomizeOpt(source, kustomizeOpts{commonAnnotations: parsedAnnotations}) + case "kustomize-label-without-selector": + setKustomizeOpt(source, kustomizeOpts{labelWithoutSelector: appOpts.kustomizeLabelWithoutSelector}) + case "kustomize-force-common-label": + setKustomizeOpt(source, kustomizeOpts{forceCommonLabels: appOpts.kustomizeForceCommonLabels}) + case "kustomize-force-common-annotation": + setKustomizeOpt(source, kustomizeOpts{forceCommonAnnotations: appOpts.kustomizeForceCommonAnnotations}) + case "jsonnet-tla-str": + setJsonnetOpt(source, appOpts.jsonnetTlaStr, false) + case "jsonnet-tla-code": + setJsonnetOpt(source, appOpts.jsonnetTlaCode, true) + case "jsonnet-ext-var-str": + setJsonnetOptExtVar(source, appOpts.jsonnetExtVarStr, false) + case "jsonnet-ext-var-code": + setJsonnetOptExtVar(source, appOpts.jsonnetExtVarCode, true) + case "jsonnet-libs": + setJsonnetOptLibs(source, appOpts.jsonnetLibs) + case "plugin-env": + setPluginOptEnvs(source, appOpts.pluginEnvs) + case "ref": + source.Ref = appOpts.ref + } + }) + return source, visited +} + func mergeLabels(app *argoappv1.Application, labels []string) { mapLabels, err := label.Parse(labels) errors.CheckError(err) @@ -664,7 +727,7 @@ func setAnnotations(app *argoappv1.Application, annotations []string) { } } -// liveObjects deserializes the list of live states into unstructured objects +// LiveObjects deserializes the list of live states into unstructured objects func LiveObjects(resources []*argoappv1.ResourceDiff) ([]*unstructured.Unstructured, error) { objs := make([]*unstructured.Unstructured, len(resources)) for i, resState := range resources { diff --git a/cmd/util/app_test.go b/cmd/util/app_test.go index 2e8ddc9e0fee7..5e95eeb388634 100644 --- a/cmd/util/app_test.go +++ b/cmd/util/app_test.go @@ -9,7 +9,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + + "k8s.io/apimachinery/pkg/util/intstr" ) func Test_setHelmOpt(t *testing.T) { @@ -86,11 +87,32 @@ func Test_setKustomizeOpt(t *testing.T) { setKustomizeOpt(&src, kustomizeOpts{images: []string{"org/image:v1", "org/image:v2"}}) assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{Images: v1alpha1.KustomizeImages{v1alpha1.KustomizeImage("org/image:v2")}}, src.Kustomize) }) + t.Run("Replicas", func(t *testing.T) { + src := v1alpha1.ApplicationSource{} + testReplicasString := []string{"my-deployment=2", "my-statefulset=4"} + testReplicas := v1alpha1.KustomizeReplicas{ + { + Name: "my-deployment", + Count: intstr.FromInt(2), + }, + { + Name: "my-statefulset", + Count: intstr.FromInt(4), + }, + } + setKustomizeOpt(&src, kustomizeOpts{replicas: testReplicasString}) + assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{Replicas: testReplicas}, src.Kustomize) + }) t.Run("Version", func(t *testing.T) { src := v1alpha1.ApplicationSource{} setKustomizeOpt(&src, kustomizeOpts{version: "v0.1"}) assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{Version: "v0.1"}, src.Kustomize) }) + t.Run("Namespace", func(t *testing.T) { + src := v1alpha1.ApplicationSource{} + setKustomizeOpt(&src, kustomizeOpts{namespace: "custom-namespace"}) + assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{Namespace: "custom-namespace"}, src.Kustomize) + }) t.Run("Common labels", func(t *testing.T) { src := v1alpha1.ApplicationSource{} setKustomizeOpt(&src, kustomizeOpts{commonLabels: map[string]string{"foo1": "bar1", "foo2": "bar2"}}) @@ -101,6 +123,11 @@ func Test_setKustomizeOpt(t *testing.T) { 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) }) + t.Run("Label Without Selector", func(t *testing.T) { + src := v1alpha1.ApplicationSource{} + setKustomizeOpt(&src, kustomizeOpts{commonLabels: map[string]string{"foo1": "bar1", "foo2": "bar2"}, labelWithoutSelector: true}) + assert.Equal(t, &v1alpha1.ApplicationSourceKustomize{CommonLabels: map[string]string{"foo1": "bar1", "foo2": "bar2"}, LabelWithoutSelector: true}, src.Kustomize) + }) } func Test_setJsonnetOpt(t *testing.T) { @@ -143,7 +170,16 @@ func (f *appOptionsFixture) SetFlag(key, value string) error { if err != nil { return err } - _ = SetAppSpecOptions(f.command.Flags(), f.spec, f.options) + _ = SetAppSpecOptions(f.command.Flags(), f.spec, f.options, 0) + return err +} + +func (f *appOptionsFixture) SetFlagWithSourceIndex(key, value string, index int) error { + err := f.command.Flags().Set(key, value) + if err != nil { + return err + } + _ = SetAppSpecOptions(f.command.Flags(), f.spec, f.options, index) return err } @@ -191,6 +227,59 @@ func Test_setAppSpecOptions(t *testing.T) { assert.NoError(t, f.SetFlag("sync-retry-limit", "0")) assert.Nil(t, f.spec.SyncPolicy.Retry) }) + t.Run("Kustomize", func(t *testing.T) { + assert.NoError(t, f.SetFlag("kustomize-replica", "my-deployment=2")) + assert.NoError(t, f.SetFlag("kustomize-replica", "my-statefulset=4")) + assert.Equal(t, f.spec.Source.Kustomize.Replicas, v1alpha1.KustomizeReplicas{{Name: "my-deployment", Count: intstr.FromInt(2)}, {Name: "my-statefulset", Count: intstr.FromInt(4)}}) + }) +} + +func newMultiSourceAppOptionsFixture() *appOptionsFixture { + fixture := &appOptionsFixture{ + spec: &v1alpha1.ApplicationSpec{ + Sources: v1alpha1.ApplicationSources{ + v1alpha1.ApplicationSource{}, + v1alpha1.ApplicationSource{}, + }, + }, + command: &cobra.Command{}, + options: &AppOptions{}, + } + AddAppFlags(fixture.command, fixture.options) + return fixture +} + +func Test_setAppSpecOptionsMultiSourceApp(t *testing.T) { + f := newMultiSourceAppOptionsFixture() + index := 0 + index1 := 1 + index2 := 2 + t.Run("SyncPolicy", func(t *testing.T) { + assert.NoError(t, f.SetFlagWithSourceIndex("sync-policy", "automated", index1)) + assert.NotNil(t, f.spec.SyncPolicy.Automated) + + f.spec.SyncPolicy = nil + assert.NoError(t, f.SetFlagWithSourceIndex("sync-policy", "automatic", index1)) + assert.NotNil(t, f.spec.SyncPolicy.Automated) + }) + t.Run("Helm - Index 0", func(t *testing.T) { + assert.NoError(t, f.SetFlagWithSourceIndex("helm-version", "v2", index)) + assert.Equal(t, len(f.spec.GetSources()), 2) + assert.Equal(t, f.spec.GetSources()[index].Helm.Version, "v2") + }) + t.Run("Kustomize", func(t *testing.T) { + assert.NoError(t, f.SetFlagWithSourceIndex("kustomize-replica", "my-deployment=2", index1)) + assert.Equal(t, f.spec.Sources[index1-1].Kustomize.Replicas, v1alpha1.KustomizeReplicas{{Name: "my-deployment", Count: intstr.FromInt(2)}}) + assert.NoError(t, f.SetFlagWithSourceIndex("kustomize-replica", "my-deployment=4", index2)) + assert.Equal(t, f.spec.Sources[index2-1].Kustomize.Replicas, v1alpha1.KustomizeReplicas{{Name: "my-deployment", Count: intstr.FromInt(4)}}) + }) + t.Run("Helm", func(t *testing.T) { + assert.NoError(t, f.SetFlagWithSourceIndex("helm-version", "v2", index1)) + assert.NoError(t, f.SetFlagWithSourceIndex("helm-version", "v3", index2)) + assert.Equal(t, len(f.spec.GetSources()), 2) + assert.Equal(t, f.spec.GetSources()[index1-1].Helm.Version, "v2") + assert.Equal(t, f.spec.GetSources()[index2-1].Helm.Version, "v3") + }) } func Test_setAnnotations(t *testing.T) { @@ -266,7 +355,7 @@ func TestReadAppsFromURI(t *testing.T) { _, _ = file.WriteString(appsYaml) _ = file.Sync() - apps := make([]*argoappv1.Application, 0) + apps := make([]*v1alpha1.Application, 0) err = readAppsFromURI(file.Name(), &apps) assert.NoError(t, err) assert.Equal(t, 2, len(apps)) diff --git a/cmd/util/applicationset.go b/cmd/util/applicationset.go index 97e40cedcdac2..2b096aa6aa036 100644 --- a/cmd/util/applicationset.go +++ b/cmd/util/applicationset.go @@ -61,6 +61,6 @@ func readAppset(yml []byte, appsets *[]*argoprojiov1alpha1.ApplicationSet) error *appsets = append(*appsets, &appset) } - - return fmt.Errorf("error reading app set: %w", err) + // we reach here if there is no error found while reading the Application Set + return nil } diff --git a/cmd/util/applicationset_test.go b/cmd/util/applicationset_test.go index 78c07f7e0d005..c15e58a61af14 100644 --- a/cmd/util/applicationset_test.go +++ b/cmd/util/applicationset_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" ) -var appSet string = `apiVersion: argoproj.io/v1alpha1 +var appSet = `apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: guestbook @@ -31,10 +31,10 @@ spec: ` func TestReadAppSet(t *testing.T) { - appsets := []*argoprojiov1alpha1.ApplicationSet{} - err := readAppset([]byte(appSet), &appsets) + var appSets []*argoprojiov1alpha1.ApplicationSet + err := readAppset([]byte(appSet), &appSets) if err != nil { t.Logf("Failed reading appset file") } - assert.Equal(t, len(appsets), 1) + assert.Equal(t, len(appSets), 1) } diff --git a/cmd/util/cluster.go b/cmd/util/cluster.go index 1da0e53709993..dffb52e775a97 100644 --- a/cmd/util/cluster.go +++ b/cmd/util/cluster.go @@ -1,6 +1,7 @@ package util import ( + "context" "fmt" "os" "sort" @@ -8,13 +9,25 @@ import ( "text/tabwriter" "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + clientcmdapiv1 "k8s.io/client-go/tools/clientcmd/api/v1" + "sigs.k8s.io/yaml" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/util/errors" ) +type ClusterEndpoint string + +const ( + KubeConfigEndpoint ClusterEndpoint = "kubeconfig" + KubePublicEndpoint ClusterEndpoint = "kube-public" + KubeInternalEndpoint ClusterEndpoint = "internal" +) + func PrintKubeContexts(ca clientcmd.ConfigAccess) { config, err := ca.GetStartingConfig() errors.CheckError(err) @@ -102,11 +115,36 @@ func NewCluster(name string, namespaces []string, clusterResources bool, conf *r return &clst } +// GetKubePublicEndpoint returns the kubernetes apiserver endpoint as published +// in the kube-public. +func GetKubePublicEndpoint(client kubernetes.Interface) (string, error) { + clusterInfo, err := client.CoreV1().ConfigMaps("kube-public").Get(context.TODO(), "cluster-info", metav1.GetOptions{}) + if err != nil { + return "", err + } + kubeconfig, ok := clusterInfo.Data["kubeconfig"] + if !ok { + return "", fmt.Errorf("cluster-info does not contain a public kubeconfig") + } + // Parse Kubeconfig and get server address + config := &clientcmdapiv1.Config{} + err = yaml.Unmarshal([]byte(kubeconfig), config) + if err != nil { + return "", fmt.Errorf("failed to parse cluster-info kubeconfig: %v", err) + } + if len(config.Clusters) == 0 { + return "", fmt.Errorf("cluster-info kubeconfig does not have any clusters") + } + + return config.Clusters[0].Cluster.Server, nil +} + type ClusterOptions struct { InCluster bool Upsert bool ServiceAccount string AwsRoleArn string + AwsProfile string AwsClusterName string SystemNamespace string Namespaces []string @@ -119,12 +157,20 @@ type ClusterOptions struct { ExecProviderEnv map[string]string ExecProviderAPIVersion string ExecProviderInstallHint string + ClusterEndpoint string +} + +// InClusterEndpoint returns true if ArgoCD should reference the in-cluster +// endpoint when registering the target cluster. +func (o ClusterOptions) InClusterEndpoint() bool { + return o.InCluster || o.ClusterEndpoint == string(KubeInternalEndpoint) } func AddClusterFlags(command *cobra.Command, opts *ClusterOptions) { command.Flags().BoolVar(&opts.InCluster, "in-cluster", false, "Indicates Argo CD resides inside this cluster and should connect using the internal k8s hostname (kubernetes.default.svc)") command.Flags().StringVar(&opts.AwsClusterName, "aws-cluster-name", "", "AWS Cluster name if set then aws cli eks token command will be used to access cluster") command.Flags().StringVar(&opts.AwsRoleArn, "aws-role-arn", "", "Optional AWS role arn. If set then AWS IAM Authenticator assumes a role to perform cluster operations instead of the default AWS credential provider chain.") + command.Flags().StringVar(&opts.AwsProfile, "aws-profile", "", "Optional AWS profile. If set then AWS IAM Authenticator uses this profile to perform cluster operations instead of the default AWS credential provider chain.") command.Flags().StringArrayVar(&opts.Namespaces, "namespace", nil, "List of namespaces which are allowed to manage") command.Flags().BoolVar(&opts.ClusterResources, "cluster-resources", false, "Indicates if cluster level resources should be managed. The setting is used only if list of managed namespaces is not empty.") command.Flags().StringVar(&opts.Name, "name", "", "Overwrite the cluster name") @@ -135,4 +181,5 @@ func AddClusterFlags(command *cobra.Command, opts *ClusterOptions) { command.Flags().StringToStringVar(&opts.ExecProviderEnv, "exec-command-env", nil, "Environment vars to set when running the --exec-command executable") command.Flags().StringVar(&opts.ExecProviderAPIVersion, "exec-command-api-version", "", "Preferred input version of the ExecInfo for the --exec-command executable") command.Flags().StringVar(&opts.ExecProviderInstallHint, "exec-command-install-hint", "", "Text shown to the user when the --exec-command executable doesn't seem to be present") + command.Flags().StringVar(&opts.ClusterEndpoint, "cluster-endpoint", "", "Cluster endpoint to use. Can be one of the following: 'kubeconfig', 'kube-public', or 'internal'.") } diff --git a/cmd/util/cluster_test.go b/cmd/util/cluster_test.go index 6afea6fa7c17c..37e05bf6e58cb 100644 --- a/cmd/util/cluster_test.go +++ b/cmd/util/cluster_test.go @@ -5,7 +5,13 @@ import ( "testing" "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/rest" + clientcmdapiv1 "k8s.io/client-go/tools/clientcmd/api/v1" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -69,3 +75,109 @@ func Test_newCluster(t *testing.T) { assert.Nil(t, clusterWithBearerToken.Labels) assert.Nil(t, clusterWithBearerToken.Annotations) } + +func TestGetKubePublicEndpoint(t *testing.T) { + cases := []struct { + name string + clusterInfo *corev1.ConfigMap + expectedEndpoint string + expectError bool + }{ + { + name: "has public endpoint", + clusterInfo: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "kube-public", + Name: "cluster-info", + }, + Data: map[string]string{ + "kubeconfig": kubeconfigFixture("https://test-cluster:6443"), + }, + }, + expectedEndpoint: "https://test-cluster:6443", + }, + { + name: "no cluster-info", + expectError: true, + }, + { + name: "no kubeconfig in cluster-info", + clusterInfo: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "kube-public", + Name: "cluster-info", + }, + Data: map[string]string{ + "argo": "the project, not the movie", + }, + }, + expectError: true, + }, + { + name: "no clusters in cluster-info kubeconfig", + clusterInfo: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "kube-public", + Name: "cluster-info", + }, + Data: map[string]string{ + "kubeconfig": kubeconfigFixture(""), + }, + }, + expectError: true, + }, + { + name: "can't parse kubeconfig", + clusterInfo: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "kube-public", + Name: "cluster-info", + }, + Data: map[string]string{ + "kubeconfig": "this is not valid YAML", + }, + }, + expectError: true, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + objects := []runtime.Object{} + if tc.clusterInfo != nil { + objects = append(objects, tc.clusterInfo) + } + clientset := fake.NewSimpleClientset(objects...) + endpoint, err := GetKubePublicEndpoint(clientset) + if err != nil && !tc.expectError { + t.Fatalf("unexpected error: %v", err) + } + if err == nil && tc.expectError { + t.Error("expected error to be returned, received none") + } + if endpoint != tc.expectedEndpoint { + t.Errorf("expected endpoint %s, got %s", tc.expectedEndpoint, endpoint) + } + }) + } + +} + +func kubeconfigFixture(endpoint string) string { + kubeconfig := &clientcmdapiv1.Config{} + if len(endpoint) > 0 { + kubeconfig.Clusters = []clientcmdapiv1.NamedCluster{ + { + Name: "test-kube", + Cluster: clientcmdapiv1.Cluster{ + Server: endpoint, + }, + }, + } + } + configYAML, err := yaml.Marshal(kubeconfig) + if err != nil { + return "" + } + return string(configYAML) +} diff --git a/cmd/util/project.go b/cmd/util/project.go index 12e875492baa9..fa446ceb3b41c 100644 --- a/cmd/util/project.go +++ b/cmd/util/project.go @@ -94,7 +94,7 @@ func (opts *ProjectOpts) GetDestinations() []v1alpha1.ApplicationDestination { return destinations } -// TODO: Get configured keys and emit warning when a key is specified that is not configured +// GetSignatureKeys TODO: Get configured keys and emit warning when a key is specified that is not configured func (opts *ProjectOpts) GetSignatureKeys() []v1alpha1.SignatureKey { signatureKeys := make([]v1alpha1.SignatureKey, 0) for _, keyStr := range opts.SignatureKeys { @@ -115,7 +115,7 @@ func GetOrphanedResourcesSettings(flagSet *pflag.FlagSet, opts ProjectOpts) *v1a if opts.orphanedResourcesEnabled || warnChanged { settings := v1alpha1.OrphanedResourcesMonitorSettings{} if warnChanged { - settings.Warn = pointer.BoolPtr(opts.orphanedResourcesWarn) + settings.Warn = pointer.Bool(opts.orphanedResourcesWarn) } return &settings } @@ -138,7 +138,10 @@ func readProjFromURI(fileURL string, proj *v1alpha1.AppProject) error { } else { err = config.UnmarshalRemoteFile(fileURL, &proj) } - return fmt.Errorf("error reading proj from uri: %w", err) + if err != nil { + return fmt.Errorf("error reading proj from uri: %w", err) + } + return nil } func SetProjSpecOptions(flags *pflag.FlagSet, spec *v1alpha1.AppProjectSpec, projOpts *ProjectOpts) int { diff --git a/cmd/util/repo.go b/cmd/util/repo.go index 87023c3a97206..b60c30a071311 100644 --- a/cmd/util/repo.go +++ b/cmd/util/repo.go @@ -23,6 +23,7 @@ type RepoOptions struct { GitHubAppEnterpriseBaseURL string Proxy string GCPServiceAccountKeyPath string + ForceHttpBasicAuth bool } func AddRepoFlags(command *cobra.Command, opts *RepoOptions) { @@ -44,4 +45,5 @@ func AddRepoFlags(command *cobra.Command, opts *RepoOptions) { command.Flags().StringVar(&opts.GitHubAppEnterpriseBaseURL, "github-app-enterprise-base-url", "", "base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3") command.Flags().StringVar(&opts.Proxy, "proxy", "", "use proxy to access repository") command.Flags().StringVar(&opts.GCPServiceAccountKeyPath, "gcp-service-account-key-path", "", "service account key for the Google Cloud Platform") + command.Flags().BoolVar(&opts.ForceHttpBasicAuth, "force-http-basic-auth", false, "whether to force use of basic auth when connecting repository via HTTP") } diff --git a/cmpserver/apiclient/clientset.go b/cmpserver/apiclient/clientset.go index 6b4c19f0261ff..025625ff8092e 100644 --- a/cmpserver/apiclient/clientset.go +++ b/cmpserver/apiclient/clientset.go @@ -7,7 +7,6 @@ import ( grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" log "github.com/sirupsen/logrus" - "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -47,8 +46,8 @@ func NewConnection(address string) (*grpc.ClientConn, error) { grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)), grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(unaryInterceptors...)), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(MaxGRPCMessageSize), grpc.MaxCallSendMsgSize(MaxGRPCMessageSize)), - grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()), - grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()), + grpc.WithUnaryInterceptor(grpc_util.OTELUnaryClientInterceptor()), + grpc.WithStreamInterceptor(grpc_util.OTELStreamClientInterceptor()), } dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) diff --git a/cmpserver/apiclient/plugin.pb.go b/cmpserver/apiclient/plugin.pb.go index 4c08ac2511edc..29ebca3ae3afc 100644 --- a/cmpserver/apiclient/plugin.pb.go +++ b/cmpserver/apiclient/plugin.pb.go @@ -318,6 +318,7 @@ func (m *ManifestResponse) GetSourceType() string { type RepositoryResponse struct { IsSupported bool `protobuf:"varint,1,opt,name=isSupported,proto3" json:"isSupported,omitempty"` + IsDiscoveryEnabled bool `protobuf:"varint,2,opt,name=isDiscoveryEnabled,proto3" json:"isDiscoveryEnabled,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -363,6 +364,13 @@ func (m *RepositoryResponse) GetIsSupported() bool { return false } +func (m *RepositoryResponse) GetIsDiscoveryEnabled() bool { + if m != nil { + return m.IsDiscoveryEnabled + } + return false +} + // ParametersAnnouncementResponse contains a list of announcements. This list represents all the parameters which a CMP // is able to accept. type ParametersAnnouncementResponse struct { @@ -472,42 +480,43 @@ func init() { func init() { proto.RegisterFile("cmpserver/plugin/plugin.proto", fileDescriptor_b21875a7079a06ed) } var fileDescriptor_b21875a7079a06ed = []byte{ - // 558 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0xc1, 0x6e, 0xd3, 0x4c, - 0x10, 0xae, 0x9b, 0xb4, 0x4d, 0x26, 0x95, 0xfe, 0x68, 0xf5, 0x0b, 0x4c, 0xd4, 0x86, 0xe0, 0x03, - 0xca, 0x85, 0x44, 0x32, 0x88, 0x1b, 0x12, 0x2d, 0x2a, 0xad, 0x40, 0x41, 0xd1, 0x96, 0x0b, 0xdc, - 0xb6, 0xce, 0x24, 0x59, 0x6a, 0xef, 0x2e, 0xeb, 0xb5, 0xa5, 0xc0, 0x85, 0xf7, 0xe0, 0x01, 0x78, - 0x15, 0x8e, 0x3c, 0x02, 0xca, 0x93, 0x20, 0xaf, 0xed, 0xd8, 0xa2, 0x6d, 0x38, 0x79, 0xe6, 0x9b, - 0x99, 0x6f, 0xbf, 0x9d, 0x99, 0x35, 0x1c, 0x07, 0x91, 0x8a, 0x51, 0xa7, 0xa8, 0xc7, 0x2a, 0x4c, - 0x16, 0x5c, 0x14, 0x9f, 0x91, 0xd2, 0xd2, 0x48, 0xb2, 0x9f, 0x7b, 0xbd, 0xb3, 0x05, 0x37, 0xcb, - 0xe4, 0x6a, 0x14, 0xc8, 0x68, 0xcc, 0xf4, 0x42, 0x2a, 0x2d, 0x3f, 0x59, 0xe3, 0x49, 0x30, 0x1b, - 0xa7, 0xfe, 0x58, 0xa3, 0x92, 0x05, 0x8d, 0x35, 0xb9, 0x91, 0x7a, 0x55, 0x33, 0x73, 0x3a, 0xef, - 0x9b, 0x03, 0xdd, 0x13, 0xa5, 0x2e, 0x8d, 0x46, 0x16, 0x51, 0xfc, 0x9c, 0x60, 0x6c, 0xc8, 0x0b, - 0x68, 0x45, 0x68, 0xd8, 0x8c, 0x19, 0xe6, 0x3a, 0x03, 0x67, 0xd8, 0xf1, 0x1f, 0x8e, 0x0a, 0x11, - 0x13, 0x26, 0xf8, 0x1c, 0x63, 0x53, 0xa4, 0x4e, 0x8a, 0xb4, 0x8b, 0x1d, 0xba, 0x29, 0x21, 0x1e, - 0x34, 0xe7, 0x3c, 0x44, 0x77, 0xd7, 0x96, 0x1e, 0x96, 0xa5, 0xaf, 0x79, 0x88, 0x17, 0x3b, 0xd4, - 0xc6, 0x4e, 0xdb, 0x70, 0xa0, 0x73, 0x0a, 0xef, 0x87, 0x03, 0xf7, 0xef, 0xa0, 0x25, 0x2e, 0x1c, - 0x30, 0xa5, 0xde, 0xb1, 0x08, 0xad, 0x90, 0x36, 0x2d, 0x5d, 0xd2, 0x07, 0x60, 0x4a, 0x51, 0x0c, - 0xa7, 0xcc, 0x2c, 0xed, 0x51, 0x6d, 0x5a, 0x43, 0x48, 0x0f, 0x5a, 0xc1, 0x12, 0x83, 0xeb, 0x38, - 0x89, 0xdc, 0x86, 0x8d, 0x6e, 0x7c, 0x42, 0xa0, 0x19, 0xf3, 0x2f, 0xe8, 0x36, 0x07, 0xce, 0xb0, - 0x41, 0xad, 0x4d, 0x3c, 0x68, 0xa0, 0x48, 0xdd, 0xbd, 0x41, 0x63, 0xd8, 0xf1, 0xbb, 0xa5, 0xe6, - 0x33, 0x91, 0x9e, 0x09, 0xa3, 0x57, 0x34, 0x0b, 0x7a, 0xcf, 0xa0, 0x55, 0x02, 0x19, 0x87, 0xa8, - 0x64, 0x59, 0x9b, 0xfc, 0x0f, 0x7b, 0x29, 0x0b, 0x13, 0x2c, 0xe4, 0xe4, 0x8e, 0x37, 0x85, 0x6e, - 0x75, 0xbd, 0x58, 0x49, 0x11, 0x23, 0x39, 0x82, 0x76, 0x54, 0x60, 0xb1, 0xeb, 0x0c, 0x1a, 0xc3, - 0x36, 0xad, 0x80, 0xec, 0x6e, 0xb1, 0x4c, 0x74, 0x80, 0xef, 0x57, 0xaa, 0x24, 0xab, 0x21, 0xde, - 0x73, 0x20, 0x74, 0x33, 0xc8, 0x0d, 0xe7, 0x00, 0x3a, 0x3c, 0xbe, 0x4c, 0x94, 0x92, 0xda, 0xe0, - 0xcc, 0x0a, 0x6b, 0xd1, 0x3a, 0xe4, 0x7d, 0x85, 0xfe, 0x94, 0x69, 0x16, 0xa1, 0x41, 0x1d, 0x9f, - 0x08, 0x21, 0x13, 0x11, 0x60, 0x84, 0xa2, 0xd2, 0xf5, 0x01, 0xee, 0xa9, 0x32, 0xa3, 0x9e, 0x90, - 0x8b, 0xec, 0xf8, 0x8f, 0x46, 0xb5, 0x0d, 0x9a, 0xde, 0x96, 0x49, 0xef, 0x20, 0xf0, 0x8e, 0xa0, - 0x99, 0x6d, 0x40, 0xd6, 0xa4, 0x60, 0x99, 0x88, 0x6b, 0x2b, 0xf0, 0x90, 0xe6, 0x8e, 0xff, 0x7d, - 0x17, 0x8e, 0x5f, 0x49, 0x31, 0xe7, 0x8b, 0x09, 0x13, 0x6c, 0x61, 0x6b, 0xa6, 0x76, 0x06, 0x97, - 0xa8, 0x53, 0x1e, 0x20, 0x79, 0x03, 0xdd, 0x73, 0x14, 0xa8, 0x99, 0xc1, 0xb2, 0x9d, 0xc4, 0x2d, - 0xe7, 0xf4, 0xf7, 0x0a, 0xf7, 0xdc, 0x9b, 0x0b, 0x9b, 0x5f, 0xd1, 0xdb, 0x19, 0x3a, 0xe4, 0x2d, - 0xfc, 0x37, 0x61, 0x26, 0x58, 0x56, 0x5d, 0xdc, 0x42, 0xd5, 0x2b, 0x23, 0x37, 0x7b, 0x6e, 0xc9, - 0x18, 0x3c, 0x38, 0x47, 0x73, 0x7b, 0x63, 0xb7, 0xd0, 0x3e, 0x2e, 0x23, 0xdb, 0x47, 0x92, 0x1d, - 0x71, 0xfa, 0xf2, 0xe7, 0xba, 0xef, 0xfc, 0x5a, 0xf7, 0x9d, 0xdf, 0xeb, 0xbe, 0xf3, 0xd1, 0xff, - 0xc7, 0xd3, 0xaf, 0x7e, 0x20, 0x4c, 0xf1, 0x20, 0xe4, 0x28, 0xcc, 0xd5, 0xbe, 0x7d, 0xee, 0x4f, - 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x33, 0x34, 0xb3, 0x95, 0x5e, 0x04, 0x00, 0x00, + // 576 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xdd, 0x6e, 0x12, 0x4f, + 0x14, 0xc0, 0xbb, 0x85, 0xb6, 0x70, 0x68, 0xf2, 0x27, 0x93, 0x7f, 0x74, 0x25, 0x2d, 0xe2, 0x5e, + 0x18, 0x6e, 0x84, 0x04, 0xbd, 0x35, 0xb1, 0x55, 0x6c, 0xa3, 0xc1, 0x90, 0xa9, 0x37, 0x7a, 0x37, + 0x1d, 0x0e, 0x30, 0x76, 0x77, 0x66, 0x9c, 0x99, 0xdd, 0x04, 0xbd, 0xf1, 0x3d, 0x7c, 0x00, 0x5f, + 0xc5, 0x4b, 0x1f, 0xc1, 0xf4, 0x49, 0x0c, 0xb3, 0xbb, 0x40, 0x6c, 0x8b, 0x57, 0x7b, 0x3e, 0x7f, + 0x7b, 0xbe, 0x32, 0x70, 0xcc, 0x13, 0x6d, 0xd1, 0x64, 0x68, 0xfa, 0x3a, 0x4e, 0x67, 0x42, 0x16, + 0x9f, 0x9e, 0x36, 0xca, 0x29, 0xb2, 0x9f, 0x6b, 0xad, 0xe1, 0x4c, 0xb8, 0x79, 0x7a, 0xd9, 0xe3, + 0x2a, 0xe9, 0x33, 0x33, 0x53, 0xda, 0xa8, 0x4f, 0x5e, 0x78, 0xc2, 0x27, 0xfd, 0x6c, 0xd0, 0x37, + 0xa8, 0x55, 0x81, 0xf1, 0xa2, 0x70, 0xca, 0x2c, 0x36, 0xc4, 0x1c, 0x17, 0x7d, 0x0b, 0xa0, 0x79, + 0xa2, 0xf5, 0x85, 0x33, 0xc8, 0x12, 0x8a, 0x9f, 0x53, 0xb4, 0x8e, 0x3c, 0x87, 0x5a, 0x82, 0x8e, + 0x4d, 0x98, 0x63, 0x61, 0xd0, 0x09, 0xba, 0x8d, 0xc1, 0xc3, 0x5e, 0x51, 0xc4, 0x88, 0x49, 0x31, + 0x45, 0xeb, 0x8a, 0xd0, 0x51, 0x11, 0x76, 0xbe, 0x43, 0x57, 0x29, 0x24, 0x82, 0xea, 0x54, 0xc4, + 0x18, 0xee, 0xfa, 0xd4, 0xc3, 0x32, 0xf5, 0xb5, 0x88, 0xf1, 0x7c, 0x87, 0x7a, 0xdf, 0x69, 0x1d, + 0x0e, 0x4c, 0x8e, 0x88, 0x7e, 0x04, 0x70, 0xff, 0x0e, 0x2c, 0x09, 0xe1, 0x80, 0x69, 0xfd, 0x8e, + 0x25, 0xe8, 0x0b, 0xa9, 0xd3, 0x52, 0x25, 0x6d, 0x00, 0xa6, 0x35, 0xc5, 0x78, 0xcc, 0xdc, 0xdc, + 0xff, 0xaa, 0x4e, 0x37, 0x2c, 0xa4, 0x05, 0x35, 0x3e, 0x47, 0x7e, 0x65, 0xd3, 0x24, 0xac, 0x78, + 0xef, 0x4a, 0x27, 0x04, 0xaa, 0x56, 0x7c, 0xc1, 0xb0, 0xda, 0x09, 0xba, 0x15, 0xea, 0x65, 0x12, + 0x41, 0x05, 0x65, 0x16, 0xee, 0x75, 0x2a, 0xdd, 0xc6, 0xa0, 0x59, 0xd6, 0x3c, 0x94, 0xd9, 0x50, + 0x3a, 0xb3, 0xa0, 0x4b, 0x67, 0xf4, 0x0c, 0x6a, 0xa5, 0x61, 0xc9, 0x90, 0xeb, 0xb2, 0xbc, 0x4c, + 0xfe, 0x87, 0xbd, 0x8c, 0xc5, 0x29, 0x16, 0xe5, 0xe4, 0x4a, 0x34, 0x86, 0xe6, 0xba, 0x3d, 0xab, + 0x95, 0xb4, 0x48, 0x8e, 0xa0, 0x9e, 0x14, 0x36, 0x1b, 0x06, 0x9d, 0x4a, 0xb7, 0x4e, 0xd7, 0x86, + 0x65, 0x6f, 0x56, 0xa5, 0x86, 0xe3, 0xfb, 0x85, 0x2e, 0x61, 0x1b, 0x96, 0x68, 0x0a, 0x84, 0xae, + 0x16, 0xb9, 0x62, 0x76, 0xa0, 0x21, 0xec, 0x45, 0xaa, 0xb5, 0x32, 0x0e, 0x27, 0xbe, 0xb0, 0x1a, + 0xdd, 0x34, 0x91, 0x1e, 0x10, 0x61, 0x5f, 0x09, 0xcb, 0x55, 0x86, 0x66, 0x31, 0x94, 0xec, 0x32, + 0xc6, 0x89, 0xe7, 0xd7, 0xe8, 0x2d, 0x9e, 0xe8, 0x2b, 0xb4, 0xc7, 0xcc, 0xb0, 0x04, 0x1d, 0x1a, + 0x7b, 0x22, 0xa5, 0x4a, 0x25, 0xc7, 0x04, 0xe5, 0xba, 0x8f, 0x0f, 0x70, 0x4f, 0x97, 0x11, 0x9b, + 0x01, 0x79, 0x53, 0x8d, 0xc1, 0xa3, 0xde, 0xc6, 0xc5, 0x8d, 0x6f, 0x8b, 0xa4, 0x77, 0x00, 0xa2, + 0x23, 0xa8, 0x2e, 0x2f, 0x66, 0x39, 0x54, 0x3e, 0x4f, 0xe5, 0x95, 0x6f, 0xe8, 0x90, 0xe6, 0xca, + 0xe0, 0xfb, 0x2e, 0x1c, 0xbf, 0x54, 0x72, 0x2a, 0x66, 0x23, 0x26, 0xd9, 0xcc, 0xe7, 0x8c, 0xfd, + 0xce, 0x2e, 0xd0, 0x64, 0x82, 0x23, 0x79, 0x03, 0xcd, 0x33, 0x94, 0x68, 0x98, 0xc3, 0x72, 0xfc, + 0x24, 0x2c, 0xf7, 0xfa, 0xf7, 0xc9, 0xb7, 0xc2, 0x9b, 0x07, 0x9e, 0xb7, 0x18, 0xed, 0x74, 0x03, + 0xf2, 0x16, 0xfe, 0x1b, 0x31, 0xc7, 0xe7, 0xeb, 0xa9, 0x6f, 0x41, 0xb5, 0x4a, 0xcf, 0xcd, 0x1d, + 0x79, 0x18, 0x83, 0x07, 0x67, 0xe8, 0x6e, 0x1f, 0xec, 0x16, 0xec, 0xe3, 0xd2, 0xb3, 0x7d, 0x25, + 0xcb, 0x5f, 0x9c, 0xbe, 0xf8, 0x79, 0xdd, 0x0e, 0x7e, 0x5d, 0xb7, 0x83, 0xdf, 0xd7, 0xed, 0xe0, + 0xe3, 0xe0, 0x1f, 0x4f, 0xc5, 0xfa, 0xc1, 0x61, 0x5a, 0xf0, 0x58, 0xa0, 0x74, 0x97, 0xfb, 0xfe, + 0x79, 0x78, 0xfa, 0x27, 0x00, 0x00, 0xff, 0xff, 0x23, 0x88, 0x8e, 0xd3, 0x8e, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1025,6 +1034,16 @@ func (m *RepositoryResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.IsDiscoveryEnabled { + i-- + if m.IsDiscoveryEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } if m.IsSupported { i-- if m.IsSupported { @@ -1247,6 +1266,9 @@ func (m *RepositoryResponse) Size() (n int) { if m.IsSupported { n += 2 } + if m.IsDiscoveryEnabled { + n += 2 + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -1893,6 +1915,26 @@ func (m *RepositoryResponse) Unmarshal(dAtA []byte) error { } } m.IsSupported = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsDiscoveryEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlugin + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsDiscoveryEnabled = bool(v != 0) default: iNdEx = preIndex skippy, err := skipPlugin(dAtA[iNdEx:]) diff --git a/cmpserver/plugin/config.go b/cmpserver/plugin/config.go index 9f05043240c83..faa718ff9fd2e 100644 --- a/cmpserver/plugin/config.go +++ b/cmpserver/plugin/config.go @@ -27,14 +27,19 @@ type PluginConfigSpec struct { Generate Command `json:"generate"` Discover Discover `json:"discover"` Parameters Parameters `yaml:"parameters"` + PreserveFileMode bool `json:"preserveFileMode,omitempty"` } -//Discover holds find and fileName +// Discover holds find and fileName type Discover struct { Find Find `json:"find"` FileName string `json:"fileName"` } +func (d Discover) IsDefined() bool { + return d.FileName != "" || d.Find.Glob != "" || len(d.Find.Command.Command) > 0 +} + // Command holds binary path and arguments list type Command struct { Command []string `json:"command,omitempty"` @@ -84,9 +89,7 @@ func ValidatePluginConfig(config PluginConfig) error { if len(config.Spec.Generate.Command) == 0 { return fmt.Errorf("invalid plugin configuration file. spec.generate command should be non-empty") } - if config.Spec.Discover.Find.Glob == "" && len(config.Spec.Discover.Find.Command.Command) == 0 && config.Spec.Discover.FileName == "" { - return fmt.Errorf("invalid plugin configuration file. atleast one of discover.find.command or discover.find.glob or discover.fineName should be non-empty") - } + // discovery field is optional as apps can now specify plugin names directly return nil } diff --git a/cmpserver/plugin/config_test.go b/cmpserver/plugin/config_test.go new file mode 100644 index 0000000000000..9e22dab1d3741 --- /dev/null +++ b/cmpserver/plugin/config_test.go @@ -0,0 +1,215 @@ +package plugin + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/argoproj/argo-cd/v2/common" +) + +func Test_IsDefined(t *testing.T) { + testCases := []struct { + name string + discover Discover + expected bool + }{ + { + name: "empty discover", + discover: Discover{}, + expected: false, + }, + { + name: "discover with find", + discover: Discover{ + Find: Find{ + Glob: "glob", + }, + }, + expected: true, + }, + { + name: "discover with fileName", + discover: Discover{ + FileName: "fileName", + }, + expected: true, + }, + { + name: "discover with empty command", + discover: Discover{ + Find: Find{ + Command: Command{ + Command: []string{}, + }, + }, + }, + expected: false, + }, + { + name: "discover with command", + discover: Discover{ + Find: Find{ + Command: Command{ + Command: []string{"command"}, + }, + }, + }, + expected: true, + }, + } + + for _, tc := range testCases { + tcc := tc + t.Run(tcc.name, func(t *testing.T) { + t.Parallel() + + actual := tcc.discover.IsDefined() + assert.Equal(t, tcc.expected, actual) + }) + } +} + +func Test_ReadPluginConfig(t *testing.T) { + testCases := []struct { + name string + fileContents string + expected *PluginConfig + expectedErr string + }{ + { + name: "empty metadata", + fileContents: ` +metadata: +`, + expected: nil, + expectedErr: "invalid plugin configuration file. metadata.name should be non-empty.", + }, + { + name: "empty metadata name", + fileContents: ` +metadata: + name: "" +`, + expected: nil, + expectedErr: "invalid plugin configuration file. metadata.name should be non-empty.", + }, + { + name: "invalid kind", + fileContents: ` +kind: invalid +metadata: + name: name +`, + expected: nil, + expectedErr: "invalid plugin configuration file. kind should be ConfigManagementPlugin, found invalid", + }, + { + name: "empty generate command", + fileContents: ` +kind: ConfigManagementPlugin +metadata: + name: name +`, + expected: nil, + expectedErr: "invalid plugin configuration file. spec.generate command should be non-empty", + }, + { + name: "valid config", + fileContents: ` +kind: ConfigManagementPlugin +metadata: + name: name +spec: + generate: + command: [command] +`, + expected: &PluginConfig{ + TypeMeta: v1.TypeMeta{ + Kind: ConfigManagementPluginKind, + }, + Metadata: v1.ObjectMeta{ + Name: "name", + }, + Spec: PluginConfigSpec{ + Generate: Command{ + Command: []string{"command"}, + }, + }, + }, + }, + } + + for _, tc := range testCases { + tcc := tc + t.Run(tcc.name, func(t *testing.T) { + t.Parallel() + // write test string to temporary file + tempDir := t.TempDir() + tempFile, err := os.Create(filepath.Join(tempDir, "plugin.yaml")) + require.NoError(t, err) + err = tempFile.Close() + require.NoError(t, err) + err = os.WriteFile(tempFile.Name(), []byte(tcc.fileContents), 0644) + require.NoError(t, err) + config, err := ReadPluginConfig(tempDir) + if tcc.expectedErr != "" { + assert.EqualError(t, err, tcc.expectedErr) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tcc.expected, config) + }) + } +} + +func Test_PluginConfig_Address(t *testing.T) { + testCases := []struct { + name string + config *PluginConfig + expected string + }{ + { + name: "no version specified", + config: &PluginConfig{ + TypeMeta: v1.TypeMeta{ + Kind: ConfigManagementPluginKind, + }, + Metadata: v1.ObjectMeta{ + Name: "name", + }, + }, + expected: "name", + }, + { + name: "version specified", + config: &PluginConfig{ + TypeMeta: v1.TypeMeta{ + Kind: ConfigManagementPluginKind, + }, + Metadata: v1.ObjectMeta{ + Name: "name", + }, + Spec: PluginConfigSpec{ + Version: "version", + }, + }, + expected: "name-version", + }, + } + + for _, tc := range testCases { + tcc := tc + t.Run(tcc.name, func(t *testing.T) { + t.Parallel() + actual := tcc.config.Address() + expectedAddress := fmt.Sprintf("%s/%s.sock", common.GetPluginSockFilePath(), tcc.expected) + assert.Equal(t, expectedAddress, actual) + }) + } +} diff --git a/cmpserver/plugin/plugin.go b/cmpserver/plugin/plugin.go index 5ac87d4ed5274..ca1e7592218ea 100644 --- a/cmpserver/plugin/plugin.go +++ b/cmpserver/plugin/plugin.go @@ -9,8 +9,10 @@ import ( "os" "os/exec" "path/filepath" + "strconv" "strings" "time" + "unicode" "github.com/argoproj/pkg/rand" @@ -22,6 +24,7 @@ import ( "github.com/argoproj/argo-cd/v2/util/io/files" "github.com/argoproj/gitops-engine/pkg/utils/kube" + "github.com/cyphar/filepath-securejoin" "github.com/mattn/go-zglob" log "github.com/sirupsen/logrus" ) @@ -73,9 +76,8 @@ func runCommand(ctx context.Context, command Command, path string, env []string) } logCtx := log.WithFields(log.Fields{"execID": execId}) - // log in a way we can copy-and-paste into a terminal - args := strings.Join(cmd.Args, " ") - logCtx.WithFields(log.Fields{"dir": cmd.Dir}).Info(args) + argsToLog := getCommandArgsToLog(cmd) + logCtx.WithFields(log.Fields{"dir": cmd.Dir}).Info(argsToLog) var stdout bytes.Buffer var stderr bytes.Buffer @@ -95,6 +97,14 @@ func runCommand(ctx context.Context, command Command, path string, env []string) <-ctx.Done() // Kill by group ID to make sure child processes are killed. The - tells `kill` that it's a group ID. // Since we didn't set Pgid in SysProcAttr, the group ID is the same as the process ID. https://pkg.go.dev/syscall#SysProcAttr + + // Sending a TERM signal first to allow any potential cleanup if needed, and then sending a KILL signal + _ = sysCallTerm(-cmd.Process.Pid) + + // modify cleanup timeout to allow process to cleanup + cleanupTimeout := 5 * time.Second + time.Sleep(cleanupTimeout) + _ = sysCallKill(-cmd.Process.Pid) }() @@ -106,14 +116,47 @@ func runCommand(ctx context.Context, command Command, path string, env []string) logCtx.WithFields(log.Fields{"duration": duration}).Debug(output) if err != nil { - err := newCmdError(args, errors.New(err.Error()), strings.TrimSpace(stderr.String())) + err := newCmdError(argsToLog, errors.New(err.Error()), strings.TrimSpace(stderr.String())) logCtx.Error(err.Error()) return strings.TrimSuffix(output, "\n"), err } + logCtx = logCtx.WithFields(log.Fields{ + "stderr": stderr.String(), + "command": command, + }) + if len(output) == 0 { + logCtx.Warn("Plugin command returned zero output") + } else { + // Log stderr even on successfull commands to help develop plugins + logCtx.Info("Plugin command successfull") + } + return strings.TrimSuffix(output, "\n"), nil } +// getCommandArgsToLog represents the given command in a way that we can copy-and-paste into a terminal +func getCommandArgsToLog(cmd *exec.Cmd) string { + var argsToLog []string + for _, arg := range cmd.Args { + containsSpace := false + for _, r := range arg { + if unicode.IsSpace(r) { + containsSpace = true + break + } + } + if containsSpace { + // add quotes and escape any internal quotes + argsToLog = append(argsToLog, strconv.Quote(arg)) + } else { + argsToLog = append(argsToLog, arg) + } + } + args := strings.Join(argsToLog, " ") + return args +} + type CmdError struct { Args string Stderr string @@ -153,7 +196,7 @@ func getTempDirMustCleanup(baseDir string) (workDir string, cleanup func(), err if err := os.RemoveAll(workDir); err != nil { log.WithFields(map[string]interface{}{ common.SecurityField: common.SecurityHigh, - common.SecurityCWEField: 459, + common.SecurityCWEField: common.SecurityCWEIncompleteCleanup, }).Errorf("Failed to clean up temp directory: %s", err) } } @@ -184,7 +227,7 @@ func (s *Service) generateManifestGeneric(stream GenerateManifestStream) error { } defer cleanup() - metadata, err := cmp.ReceiveRepoStream(ctx, stream, workDir) + metadata, err := cmp.ReceiveRepoStream(ctx, stream, workDir, s.initConstants.PluginConfig.Spec.PreserveFileMode) if err != nil { return fmt.Errorf("generate manifest error receiving stream: %w", err) } @@ -268,16 +311,16 @@ func (s *Service) matchRepositoryGeneric(stream MatchRepositoryStream) error { } defer cleanup() - metadata, err := cmp.ReceiveRepoStream(bufferedCtx, stream, workDir) + metadata, err := cmp.ReceiveRepoStream(bufferedCtx, stream, workDir, s.initConstants.PluginConfig.Spec.PreserveFileMode) if err != nil { return fmt.Errorf("match repository error receiving stream: %w", err) } - isSupported, err := s.matchRepository(bufferedCtx, workDir, metadata.GetEnv()) + isSupported, isDiscoveryEnabled, err := s.matchRepository(bufferedCtx, workDir, metadata.GetEnv(), metadata.GetAppRelPath()) if err != nil { return fmt.Errorf("match repository error: %w", err) } - repoResponse := &apiclient.RepositoryResponse{IsSupported: isSupported} + repoResponse := &apiclient.RepositoryResponse{IsSupported: isSupported, IsDiscoveryEnabled: isDiscoveryEnabled} err = stream.SendAndClose(repoResponse) if err != nil { @@ -286,50 +329,55 @@ func (s *Service) matchRepositoryGeneric(stream MatchRepositoryStream) error { return nil } -func (s *Service) matchRepository(ctx context.Context, workdir string, envEntries []*apiclient.EnvEntry) (bool, error) { +func (s *Service) matchRepository(ctx context.Context, workdir string, envEntries []*apiclient.EnvEntry, appRelPath string) (isSupported bool, isDiscoveryEnabled bool, err error) { config := s.initConstants.PluginConfig + + appPath, err := securejoin.SecureJoin(workdir, appRelPath) + if err != nil { + log.WithFields(map[string]interface{}{ + common.SecurityField: common.SecurityHigh, + common.SecurityCWEField: common.SecurityCWEIncompleteCleanup, + }).Errorf("error joining workdir %q and appRelPath %q: %v", workdir, appRelPath, err) + } + if config.Spec.Discover.FileName != "" { log.Debugf("config.Spec.Discover.FileName is provided") - pattern := filepath.Join(workdir, config.Spec.Discover.FileName) + pattern := filepath.Join(appPath, config.Spec.Discover.FileName) matches, err := filepath.Glob(pattern) if err != nil { e := fmt.Errorf("error finding filename match for pattern %q: %w", pattern, err) log.Debug(e) - return false, e + return false, true, e } - return len(matches) > 0, nil + return len(matches) > 0, true, nil } if config.Spec.Discover.Find.Glob != "" { log.Debugf("config.Spec.Discover.Find.Glob is provided") - pattern := filepath.Join(workdir, config.Spec.Discover.Find.Glob) + pattern := filepath.Join(appPath, config.Spec.Discover.Find.Glob) // filepath.Glob doesn't have '**' support hence selecting third-party lib // https://github.com/golang/go/issues/11862 matches, err := zglob.Glob(pattern) if err != nil { e := fmt.Errorf("error finding glob match for pattern %q: %w", pattern, err) log.Debug(e) - return false, e + return false, true, e } - if len(matches) > 0 { - return true, nil - } - return false, nil + return len(matches) > 0, true, nil } - log.Debugf("Going to try runCommand.") - env := append(os.Environ(), environ(envEntries)...) - - find, err := runCommand(ctx, config.Spec.Discover.Find.Command, workdir, env) - if err != nil { - return false, fmt.Errorf("error running find command: %w", err) + if len(config.Spec.Discover.Find.Command.Command) > 0 { + log.Debugf("Going to try runCommand.") + env := append(os.Environ(), environ(envEntries)...) + find, err := runCommand(ctx, config.Spec.Discover.Find.Command, appPath, env) + if err != nil { + return false, true, fmt.Errorf("error running find command: %w", err) + } + return find != "", true, nil } - if find != "" { - return true, nil - } - return false, nil + return false, false, nil } // ParametersAnnouncementStream defines an interface able to send/receive a stream of parameter announcements. @@ -349,7 +397,7 @@ func (s *Service) GetParametersAnnouncement(stream apiclient.ConfigManagementPlu } defer cleanup() - metadata, err := cmp.ReceiveRepoStream(bufferedCtx, stream, workDir) + metadata, err := cmp.ReceiveRepoStream(bufferedCtx, stream, workDir, s.initConstants.PluginConfig.Spec.PreserveFileMode) if err != nil { return fmt.Errorf("parameters announcement error receiving stream: %w", err) } @@ -358,7 +406,7 @@ func (s *Service) GetParametersAnnouncement(stream apiclient.ConfigManagementPlu return fmt.Errorf("illegal appPath: out of workDir bound") } - repoResponse, err := getParametersAnnouncement(bufferedCtx, appPath, s.initConstants.PluginConfig.Spec.Parameters.Static, s.initConstants.PluginConfig.Spec.Parameters.Dynamic) + repoResponse, err := getParametersAnnouncement(bufferedCtx, appPath, s.initConstants.PluginConfig.Spec.Parameters.Static, s.initConstants.PluginConfig.Spec.Parameters.Dynamic, metadata.GetEnv()) if err != nil { return fmt.Errorf("get parameters announcement error: %w", err) } @@ -370,11 +418,12 @@ func (s *Service) GetParametersAnnouncement(stream apiclient.ConfigManagementPlu return nil } -func getParametersAnnouncement(ctx context.Context, appDir string, announcements []*repoclient.ParameterAnnouncement, command Command) (*apiclient.ParametersAnnouncementResponse, error) { +func getParametersAnnouncement(ctx context.Context, appDir string, announcements []*repoclient.ParameterAnnouncement, command Command, envEntries []*apiclient.EnvEntry) (*apiclient.ParametersAnnouncementResponse, error) { augmentedAnnouncements := announcements if len(command.Command) > 0 { - stdout, err := runCommand(ctx, command, appDir, os.Environ()) + env := append(os.Environ(), environ(envEntries)...) + stdout, err := runCommand(ctx, command, appDir, env) if err != nil { return nil, fmt.Errorf("error executing dynamic parameter output command: %w", err) } diff --git a/cmpserver/plugin/plugin.proto b/cmpserver/plugin/plugin.proto index b6dc23232e8a1..16d4268d5939f 100644 --- a/cmpserver/plugin/plugin.proto +++ b/cmpserver/plugin/plugin.proto @@ -44,6 +44,7 @@ message ManifestResponse { message RepositoryResponse { bool isSupported = 1; + bool isDiscoveryEnabled = 2; } // ParametersAnnouncementResponse contains a list of announcements. This list represents all the parameters which a CMP diff --git a/cmpserver/plugin/plugin_test.go b/cmpserver/plugin/plugin_test.go index c14de4ccf6328..b253dc414cbdc 100644 --- a/cmpserver/plugin/plugin_test.go +++ b/cmpserver/plugin/plugin_test.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "os" + "os/exec" "path" "path/filepath" "testing" @@ -99,11 +100,12 @@ func TestMatchRepository(t *testing.T) { f := setup(t, withDiscover(d)) // when - match, err := f.service.matchRepository(context.Background(), f.path, f.env) + match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then assert.NoError(t, err) assert.True(t, match) + assert.True(t, discovery) }) t.Run("will not match plugin by filename if file not found", func(t *testing.T) { // given @@ -113,11 +115,12 @@ func TestMatchRepository(t *testing.T) { f := setup(t, withDiscover(d)) // when - match, err := f.service.matchRepository(context.Background(), f.path, f.env) + match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then assert.NoError(t, err) assert.False(t, match) + assert.True(t, discovery) }) t.Run("will not match a pattern with a syntax error", func(t *testing.T) { // given @@ -127,7 +130,7 @@ func TestMatchRepository(t *testing.T) { f := setup(t, withDiscover(d)) // when - _, err := f.service.matchRepository(context.Background(), f.path, f.env) + _, _, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then assert.ErrorContains(t, err, "syntax error") @@ -142,11 +145,12 @@ func TestMatchRepository(t *testing.T) { f := setup(t, withDiscover(d)) // when - match, err := f.service.matchRepository(context.Background(), f.path, f.env) + match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then assert.NoError(t, err) assert.True(t, match) + assert.True(t, discovery) }) t.Run("will not match plugin by glob if not found", func(t *testing.T) { // given @@ -158,11 +162,12 @@ func TestMatchRepository(t *testing.T) { f := setup(t, withDiscover(d)) // when - match, err := f.service.matchRepository(context.Background(), f.path, f.env) + match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then assert.NoError(t, err) assert.False(t, match) + assert.True(t, discovery) }) t.Run("will throw an error for a bad pattern", func(t *testing.T) { // given @@ -174,7 +179,7 @@ func TestMatchRepository(t *testing.T) { f := setup(t, withDiscover(d)) // when - _, err := f.service.matchRepository(context.Background(), f.path, f.env) + _, _, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then assert.ErrorContains(t, err, "error finding glob match for pattern") @@ -191,11 +196,12 @@ func TestMatchRepository(t *testing.T) { f := setup(t, withDiscover(d)) // when - match, err := f.service.matchRepository(context.Background(), f.path, f.env) + match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then assert.NoError(t, err) assert.True(t, match) + assert.True(t, discovery) }) t.Run("will not match plugin by command when returns no output", func(t *testing.T) { // given @@ -209,11 +215,11 @@ func TestMatchRepository(t *testing.T) { f := setup(t, withDiscover(d)) // when - match, err := f.service.matchRepository(context.Background(), f.path, f.env) - + match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then assert.NoError(t, err) assert.False(t, match) + assert.True(t, discovery) }) t.Run("will match plugin because env var defined", func(t *testing.T) { // given @@ -227,11 +233,12 @@ func TestMatchRepository(t *testing.T) { f := setup(t, withDiscover(d)) // when - match, err := f.service.matchRepository(context.Background(), f.path, f.env) + match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then assert.NoError(t, err) assert.True(t, match) + assert.True(t, discovery) }) t.Run("will not match plugin because no env var defined", func(t *testing.T) { // given @@ -246,11 +253,12 @@ func TestMatchRepository(t *testing.T) { f := setup(t, withDiscover(d)) // when - match, err := f.service.matchRepository(context.Background(), f.path, f.env) + match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then assert.NoError(t, err) assert.False(t, match) + assert.True(t, discovery) }) t.Run("will not match plugin by command when command fails", func(t *testing.T) { // given @@ -264,11 +272,25 @@ func TestMatchRepository(t *testing.T) { f := setup(t, withDiscover(d)) // when - match, err := f.service.matchRepository(context.Background(), f.path, f.env) + match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") // then assert.Error(t, err) assert.False(t, match) + assert.True(t, discovery) + }) + t.Run("will not match plugin as discovery is not set", func(t *testing.T) { + // given + d := Discover{} + f := setup(t, withDiscover(d)) + + // when + match, discovery, err := f.service.matchRepository(context.Background(), f.path, f.env, ".") + + // then + assert.NoError(t, err) + assert.False(t, match) + assert.False(t, discovery) }) } @@ -347,6 +369,28 @@ func TestRunCommandEmptyCommand(t *testing.T) { assert.ErrorContains(t, err, "Command is empty") } +// TestRunCommandContextTimeoutWithGracefulTermination makes sure that the process is given enough time to cleanup before sending SIGKILL. +func TestRunCommandContextTimeoutWithCleanup(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 900*time.Millisecond) + defer cancel() + + // Use a subshell so there's a child command. + // This command sleeps for 4 seconds which is currently less than the 5 second delay between SIGTERM and SIGKILL signal and then exits successfully. + command := Command{ + Command: []string{"sh", "-c"}, + Args: []string{`(trap 'echo "cleanup completed"; exit' TERM; sleep 4)`}, + } + + before := time.Now() + output, err := runCommand(ctx, command, "", []string{}) + after := time.Now() + + assert.Error(t, err) // The command should time out, causing an error. + assert.Less(t, after.Sub(before), 1*time.Second) + // The command should still have completed the cleanup after termination. + assert.Contains(t, output, "cleanup completed") +} + func Test_getParametersAnnouncement_empty_command(t *testing.T) { staticYAML := ` - name: static-a @@ -359,7 +403,7 @@ func Test_getParametersAnnouncement_empty_command(t *testing.T) { Command: []string{"echo"}, Args: []string{`[]`}, } - res, err := getParametersAnnouncement(context.Background(), "", *static, command) + res, err := getParametersAnnouncement(context.Background(), "", *static, command, []*apiclient.EnvEntry{}) require.NoError(t, err) assert.Equal(t, []*repoclient.ParameterAnnouncement{{Name: "static-a"}, {Name: "static-b"}}, res.ParameterAnnouncements) } @@ -373,7 +417,7 @@ func Test_getParametersAnnouncement_no_command(t *testing.T) { err := yaml.Unmarshal([]byte(staticYAML), static) require.NoError(t, err) command := Command{} - res, err := getParametersAnnouncement(context.Background(), "", *static, command) + res, err := getParametersAnnouncement(context.Background(), "", *static, command, []*apiclient.EnvEntry{}) require.NoError(t, err) assert.Equal(t, []*repoclient.ParameterAnnouncement{{Name: "static-a"}, {Name: "static-b"}}, res.ParameterAnnouncements) } @@ -390,7 +434,7 @@ func Test_getParametersAnnouncement_static_and_dynamic(t *testing.T) { Command: []string{"echo"}, Args: []string{`[{"name": "dynamic-a"}, {"name": "dynamic-b"}]`}, } - res, err := getParametersAnnouncement(context.Background(), "", *static, command) + res, err := getParametersAnnouncement(context.Background(), "", *static, command, []*apiclient.EnvEntry{}) require.NoError(t, err) expected := []*repoclient.ParameterAnnouncement{ {Name: "dynamic-a"}, @@ -406,7 +450,7 @@ func Test_getParametersAnnouncement_invalid_json(t *testing.T) { Command: []string{"echo"}, Args: []string{`[`}, } - _, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command) + _, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command, []*apiclient.EnvEntry{}) assert.Error(t, err) assert.Contains(t, err.Error(), "unexpected end of JSON input") } @@ -416,7 +460,7 @@ func Test_getParametersAnnouncement_bad_command(t *testing.T) { Command: []string{"exit"}, Args: []string{"1"}, } - _, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command) + _, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command, []*apiclient.EnvEntry{}) assert.Error(t, err) assert.Contains(t, err.Error(), "error executing dynamic parameter output command") } @@ -708,16 +752,17 @@ func TestService_GetParametersAnnouncement(t *testing.T) { require.NoError(t, err) t.Run("successful response", func(t *testing.T) { - s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", nil) + s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", []string{"MUST_BE_SET=yep"}) require.NoError(t, err) err = service.GetParametersAnnouncement(s) require.NoError(t, err) require.NotNil(t, s.response) - require.Len(t, s.response.ParameterAnnouncements, 1) - assert.Equal(t, repoclient.ParameterAnnouncement{Name: "test-param", String_: "test-value"}, *s.response.ParameterAnnouncements[0]) + require.Len(t, s.response.ParameterAnnouncements, 2) + assert.Equal(t, repoclient.ParameterAnnouncement{Name: "dynamic-test-param", String_: "yep"}, *s.response.ParameterAnnouncements[0]) + assert.Equal(t, repoclient.ParameterAnnouncement{Name: "test-param", String_: "test-value"}, *s.response.ParameterAnnouncements[1]) }) t.Run("out of bounds app", func(t *testing.T) { - s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", nil) + s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", []string{"MUST_BE_SET=yep"}) require.NoError(t, err) // set a malicious app path on the metadata s.metadataRequest.Request.(*apiclient.AppStreamRequest_Metadata).Metadata.AppRelPath = "../out-of-bounds" @@ -725,4 +770,38 @@ func TestService_GetParametersAnnouncement(t *testing.T) { require.ErrorContains(t, err, "illegal appPath") require.Nil(t, s.response) }) + t.Run("fails when script fails", func(t *testing.T) { + s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", []string{"WRONG_ENV_VAR=oops"}) + require.NoError(t, err) + err = service.GetParametersAnnouncement(s) + require.ErrorContains(t, err, "error executing dynamic parameter output command") + require.Nil(t, s.response) + }) +} + +func Test_getCommandArgsToLog(t *testing.T) { + testCases := []struct { + name string + args []string + expected string + }{ + { + name: "no spaces", + args: []string{"sh", "-c", "cat"}, + expected: "sh -c cat", + }, + { + name: "spaces", + args: []string{"sh", "-c", `echo "hello world"`}, + expected: `sh -c "echo \"hello world\""`, + }, + } + + for _, tc := range testCases { + tcc := tc + t.Run(tcc.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tcc.expected, getCommandArgsToLog(exec.Command(tcc.args[0], tcc.args[1:]...))) + }) + } } diff --git a/cmpserver/plugin/plugin_unix.go b/cmpserver/plugin/plugin_unix.go index a9dc157bc7ef8..ea6b7b5493910 100644 --- a/cmpserver/plugin/plugin_unix.go +++ b/cmpserver/plugin/plugin_unix.go @@ -14,3 +14,7 @@ func newSysProcAttr(setpgid bool) *syscall.SysProcAttr { func sysCallKill(pid int) error { return syscall.Kill(pid, syscall.SIGKILL) } + +func sysCallTerm(pid int) error { + return syscall.Kill(pid, syscall.SIGTERM) +} diff --git a/cmpserver/plugin/plugin_windows.go b/cmpserver/plugin/plugin_windows.go index 2a188e61f6f9e..b8873a9793601 100644 --- a/cmpserver/plugin/plugin_windows.go +++ b/cmpserver/plugin/plugin_windows.go @@ -14,3 +14,7 @@ func newSysProcAttr(setpgid bool) *syscall.SysProcAttr { func sysCallKill(pid int) error { return nil } + +func sysCallTerm(pid int) error { + return nil +} diff --git a/cmpserver/plugin/testdata/kustomize/config/plugin.yaml b/cmpserver/plugin/testdata/kustomize/config/plugin.yaml index addc30c7b4c21..bdca45e9ae45e 100644 --- a/cmpserver/plugin/testdata/kustomize/config/plugin.yaml +++ b/cmpserver/plugin/testdata/kustomize/config/plugin.yaml @@ -5,9 +5,15 @@ metadata: spec: version: v1.0 init: - command: [kustomize, version] + command: [sh, -c] + args: + - | + kustomize version generate: - command: [sh, -c, "kustomize build"] + command: [sh, -c] + args: + - | + kustomize build discover: find: command: [sh, -c, find . -name kustomization.yaml] @@ -16,3 +22,12 @@ spec: static: - name: test-param string: test-value + dynamic: + command: [sh, -c] + args: + - | + # Make sure env vars are making it to the plugin. + if [ -z "$MUST_BE_SET" ]; then + exit 1 + fi + echo "[{\"name\": \"dynamic-test-param\", \"string\": \"$MUST_BE_SET\"}]" diff --git a/cmpserver/server.go b/cmpserver/server.go index bbb493f6b1d66..1d07e531394d3 100644 --- a/cmpserver/server.go +++ b/cmpserver/server.go @@ -65,7 +65,7 @@ func NewServer(initConstants plugin.CMPServerInitConstants) (*ArgoCDCMPServer, e grpc.MaxSendMsgSize(apiclient.MaxGRPCMessageSize), grpc.KeepaliveEnforcementPolicy( keepalive.EnforcementPolicy{ - MinTime: common.GRPCKeepAliveEnforcementMinimum, + MinTime: common.GetGRPCKeepAliveEnforcementMinimum(), }, ), } diff --git a/common/common.go b/common/common.go index 5c76bd8f54f32..f4b176946bcbd 100644 --- a/common/common.go +++ b/common/common.go @@ -1,12 +1,20 @@ package common import ( + "errors" "os" "path/filepath" "strconv" "time" "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// Component names +const ( + ApplicationController = "argocd-application-controller" ) // Default service addresses and URLS of Argo CD internal services @@ -26,11 +34,13 @@ const ( ArgoCDNotificationsConfigMapName = "argocd-notifications-cm" ArgoCDNotificationsSecretName = "argocd-notifications-secret" ArgoCDRBACConfigMapName = "argocd-rbac-cm" - // Contains SSH known hosts data for connecting repositories. Will get mounted as volume to pods + // ArgoCDKnownHostsConfigMapName contains SSH known hosts data for connecting repositories. Will get mounted as volume to pods ArgoCDKnownHostsConfigMapName = "argocd-ssh-known-hosts-cm" - // Contains TLS certificate data for connecting repositories. Will get mounted as volume to pods + // ArgoCDTLSCertsConfigMapName contains TLS certificate data for connecting repositories. Will get mounted as volume to pods ArgoCDTLSCertsConfigMapName = "argocd-tls-certs-cm" ArgoCDGPGKeysConfigMapName = "argocd-gpg-keys-cm" + // ArgoCDAppControllerShardConfigMapName contains the application controller to shard mapping + ArgoCDAppControllerShardConfigMapName = "argocd-app-controller-shard-cm" ) // Some default configurables @@ -48,28 +58,32 @@ const ( DefaultPortRepoServerMetrics = 8084 ) -// Default listener address for ArgoCD components +// DefaultAddressAPIServer for ArgoCD components const ( - DefaultAddressAPIServer = "localhost" + DefaultAddressAdminDashboard = "localhost" + DefaultAddressAPIServer = "0.0.0.0" + DefaultAddressAPIServerMetrics = "0.0.0.0" + DefaultAddressRepoServer = "0.0.0.0" + DefaultAddressRepoServerMetrics = "0.0.0.0" ) // Default paths on the pod's file system const ( - // The default path where TLS certificates for repositories are located + // DefaultPathTLSConfig is the default path where TLS certificates for repositories are located DefaultPathTLSConfig = "/app/config/tls" - // The default path where SSH known hosts are stored + // DefaultPathSSHConfig is the default path where SSH known hosts are stored DefaultPathSSHConfig = "/app/config/ssh" - // Default name for the SSH known hosts file + // DefaultSSHKnownHostsName is the Default name for the SSH known hosts file DefaultSSHKnownHostsName = "ssh_known_hosts" - // Default path to GnuPG home directory + // DefaultGnuPgHomePath is the Default path to GnuPG home directory DefaultGnuPgHomePath = "/app/config/gpg/keys" - // Default path to repo server TLS endpoint config + // DefaultAppConfigPath is the Default path to repo server TLS endpoint config DefaultAppConfigPath = "/app/config" - // Default path to cmp server plugin socket file + // DefaultPluginSockFilePath is the Default path to cmp server plugin socket file DefaultPluginSockFilePath = "/home/argocd/cmp-server/plugins" - // Default path to cmp server plugin configuration file + // DefaultPluginConfigFilePath is the Default path to cmp server plugin configuration file DefaultPluginConfigFilePath = "/home/argocd/cmp-server/config" - // Plugin Config File is a ConfigManagementPlugin manifest located inside the plugin container + // PluginConfigFileName is the Plugin Config File is a ConfigManagementPlugin manifest located inside the plugin container PluginConfigFileName = "plugin.yaml" ) @@ -96,6 +110,14 @@ const ( // PasswordPatten is the default password patten PasswordPatten = `^.{8,32}$` + + // LegacyShardingAlgorithm is the default value for Sharding Algorithm it uses an `uid` based distribution (non-uniform) + LegacyShardingAlgorithm = "legacy" + // RoundRobinShardingAlgorithm is a flag value that can be opted for Sharding Algorithm it uses an equal distribution accross all shards + RoundRobinShardingAlgorithm = "round-robin" + // AppControllerHeartbeatUpdateRetryCount is the retry count for updating the Shard Mapping to the Shard Mapping ConfigMap used by Application Controller + AppControllerHeartbeatUpdateRetryCount = 3 + DefaultShardingAlgorithm = LegacyShardingAlgorithm ) // Dex related constants @@ -125,10 +147,16 @@ const ( // LabelKeyAppInstance is the label key to use to uniquely identify the instance of an application // The Argo CD application name is used as the instance name LabelKeyAppInstance = "app.kubernetes.io/instance" + // LabelKeyAppName is the label key to use to uniquely identify the name of the Kubernetes application + LabelKeyAppName = "app.kubernetes.io/name" + // LabelKeyAutoLabelClusterInfo if set to true will automatically add extra labels from the cluster info (currently it only adds a k8s version label) + LabelKeyAutoLabelClusterInfo = "argocd.argoproj.io/auto-label-cluster-info" // LabelKeyLegacyApplicationName is the legacy label (v0.10 and below) and is superseded by 'app.kubernetes.io/instance' LabelKeyLegacyApplicationName = "applications.argoproj.io/app-name" // LabelKeySecretType contains the type of argocd secret (currently: 'cluster', 'repository', 'repo-config' or 'repo-creds') LabelKeySecretType = "argocd.argoproj.io/secret-type" + // LabelKeyClusterKubernetesVersion contains the kubernetes version of the cluster secret if it has been enabled + LabelKeyClusterKubernetesVersion = "argocd.argoproj.io/kubernetes-version" // LabelValueSecretTypeCluster indicates a secret type of cluster LabelValueSecretTypeCluster = "cluster" // LabelValueSecretTypeRepository indicates a secret type of repository @@ -136,7 +164,7 @@ const ( // LabelValueSecretTypeRepoCreds indicates a secret type of repository credentials LabelValueSecretTypeRepoCreds = "repo-creds" - // The Argo CD application name is used as the instance name + // AnnotationKeyAppInstance is the Argo CD application name is used as the instance name AnnotationKeyAppInstance = "argocd.argoproj.io/tracking-id" // AnnotationCompareOptions is a comma-separated list of options for comparison @@ -156,6 +184,14 @@ const ( // Ex: "http://grafana.example.com/d/yu5UH4MMz/deployments" // Ex: "Go to Dashboard|http://grafana.example.com/d/yu5UH4MMz/deployments" AnnotationKeyLinkPrefix = "link.argocd.argoproj.io/" + + // AnnotationKeyAppSkipReconcile tells the Application to skip the Application controller reconcile. + // Skip reconcile when the value is "true" or any other string values that can be strconv.ParseBool() to be true. + AnnotationKeyAppSkipReconcile = "argocd.argoproj.io/skip-reconcile" + // LabelKeyComponentRepoServer is the label key to identify the component as repo-server + LabelKeyComponentRepoServer = "app.kubernetes.io/component" + // LabelValueComponentRepoServer is the label value for the repo-server component + LabelValueComponentRepoServer = "repo-server" ) // Environment variables for tuning and debugging Argo CD @@ -164,19 +200,19 @@ const ( EnvVarSSODebug = "ARGOCD_SSO_DEBUG" // EnvVarRBACDebug is an environment variable to enable additional RBAC debugging in the API server EnvVarRBACDebug = "ARGOCD_RBAC_DEBUG" - // Overrides the location where SSH known hosts for repo access data is stored + // EnvVarSSHDataPath overrides the location where SSH known hosts for repo access data is stored EnvVarSSHDataPath = "ARGOCD_SSH_DATA_PATH" - // Overrides the location where TLS certificate for repo access data is stored + // EnvVarTLSDataPath overrides the location where TLS certificate for repo access data is stored EnvVarTLSDataPath = "ARGOCD_TLS_DATA_PATH" - // Specifies number of git remote operations attempts count + // EnvGitAttemptsCount specifies number of git remote operations attempts count EnvGitAttemptsCount = "ARGOCD_GIT_ATTEMPTS_COUNT" - // Specifices max duration of git remote operation retry + // EnvGitRetryMaxDuration specifices max duration of git remote operation retry EnvGitRetryMaxDuration = "ARGOCD_GIT_RETRY_MAX_DURATION" - // Specifies duration of git remote operation retry + // EnvGitRetryDuration specifies duration of git remote operation retry EnvGitRetryDuration = "ARGOCD_GIT_RETRY_DURATION" - // Specifies fator of git remote operation retry + // EnvGitRetryFactor specifies fator of git remote operation retry EnvGitRetryFactor = "ARGOCD_GIT_RETRY_FACTOR" - // Overrides git submodule support, true by default + // EnvGitSubmoduleEnabled overrides git submodule support, true by default EnvGitSubmoduleEnabled = "ARGOCD_GIT_MODULES_ENABLED" // EnvGnuPGHome is the path to ArgoCD's GnuPG keyring for signature verification EnvGnuPGHome = "ARGOCD_GNUPGHOME" @@ -190,20 +226,28 @@ const ( EnvPauseGenerationRequests = "ARGOCD_PAUSE_GEN_REQUESTS" // EnvControllerReplicas is the number of controller replicas EnvControllerReplicas = "ARGOCD_CONTROLLER_REPLICAS" + // EnvControllerHeartbeatTime will update the heartbeat for application controller to claim shard + EnvControllerHeartbeatTime = "ARGOCD_CONTROLLER_HEARTBEAT_TIME" // EnvControllerShard is the shard number that should be handled by controller EnvControllerShard = "ARGOCD_CONTROLLER_SHARD" + // EnvControllerShardingAlgorithm is the distribution sharding algorithm to be used: legacy or round-robin + EnvControllerShardingAlgorithm = "ARGOCD_CONTROLLER_SHARDING_ALGORITHM" + //EnvEnableDynamicClusterDistribution enables dynamic sharding (ALPHA) + EnvEnableDynamicClusterDistribution = "ARGOCD_ENABLE_DYNAMIC_CLUSTER_DISTRIBUTION" // EnvEnableGRPCTimeHistogramEnv enables gRPC metrics collection EnvEnableGRPCTimeHistogramEnv = "ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM" // EnvGithubAppCredsExpirationDuration controls the caching of Github app credentials. This value is in minutes (default: 60) EnvGithubAppCredsExpirationDuration = "ARGOCD_GITHUB_APP_CREDS_EXPIRATION_DURATION" // EnvHelmIndexCacheDuration controls how the helm repository index file is cached for (default: 0) EnvHelmIndexCacheDuration = "ARGOCD_HELM_INDEX_CACHE_DURATION" - // EnvRepoServerConfigPath allows to override the configuration path for repo server + // EnvAppConfigPath allows to override the configuration path for repo server EnvAppConfigPath = "ARGOCD_APP_CONF_PATH" // EnvLogFormat log format that is defined by `--logformat` option EnvLogFormat = "ARGOCD_LOG_FORMAT" // EnvLogLevel log level that is defined by `--loglevel` option EnvLogLevel = "ARGOCD_LOG_LEVEL" + // EnvLogFormatEnableFullTimestamp enables the FullTimestamp option in logs + EnvLogFormatEnableFullTimestamp = "ARGOCD_LOG_FORMAT_ENABLE_FULL_TIMESTAMP" // EnvMaxCookieNumber max number of chunks a cookie can be broken into EnvMaxCookieNumber = "ARGOCD_MAX_COOKIE_NUMBER" // EnvPluginSockFilePath allows to override the pluginSockFilePath for repo server and cmp server @@ -212,6 +256,23 @@ const ( EnvCMPChunkSize = "ARGOCD_CMP_CHUNK_SIZE" // EnvCMPWorkDir defines the full path of the work directory used by the CMP server EnvCMPWorkDir = "ARGOCD_CMP_WORKDIR" + // EnvGPGDataPath overrides the location where GPG keyring for signature verification is stored + EnvGPGDataPath = "ARGOCD_GPG_DATA_PATH" + // EnvServerName is the name of the Argo CD server component, as specified by the value under the LabelKeyAppName label key. + EnvServerName = "ARGOCD_SERVER_NAME" + // EnvRepoServerName is the name of the Argo CD repo server component, as specified by the value under the LabelKeyAppName label key. + EnvRepoServerName = "ARGOCD_REPO_SERVER_NAME" + // EnvAppControllerName is the name of the Argo CD application controller component, as specified by the value under the LabelKeyAppName label key. + EnvAppControllerName = "ARGOCD_APPLICATION_CONTROLLER_NAME" + // EnvRedisName is the name of the Argo CD redis component, as specified by the value under the LabelKeyAppName label key. + EnvRedisName = "ARGOCD_REDIS_NAME" + // EnvRedisHaProxyName is the name of the Argo CD Redis HA proxy component, as specified by the value under the LabelKeyAppName label key. + EnvRedisHaProxyName = "ARGOCD_REDIS_HAPROXY_NAME" + // EnvGRPCKeepAliveMin defines the GRPCKeepAliveEnforcementMinimum, used in the grpc.KeepaliveEnforcementPolicy. Expects a "Duration" format (e.g. 10s). + EnvGRPCKeepAliveMin = "ARGOCD_GRPC_KEEP_ALIVE_MIN" + // EnvServerSideDiff defines the env var used to enable ServerSide Diff feature. + // If defined, value must be "true" or "false". + EnvServerSideDiff = "ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF" ) // Config Management Plugin related constants @@ -222,13 +283,7 @@ const ( // DefaultCMPWorkDirName defines the work directory name used by the cmp-server DefaultCMPWorkDirName = "_cmp_server" - ConfigMapPluginDeprecationWarning = "argocd-cm plugins are deprecated, and support will be removed in v2.6. Upgrade your plugin to be installed via sidecar. https://argo-cd.readthedocs.io/en/stable/user-guide/config-management-plugins/" - - ConfigMapPluginCLIDeprecationWarning = "spec.plugin.name is set, which means this Application uses a plugin installed in the " + - "argocd-cm ConfigMap. Installing plugins via that ConfigMap is deprecated in Argo CD v2.5. " + - "Starting in Argo CD v2.6, this Application will fail to sync. Contact your Argo CD admin " + - "to make sure an upgrade plan is in place. More info: " + - "https://argo-cd.readthedocs.io/en/latest/operator-manual/upgrading/2.4-2.5/" + ConfigMapPluginDeprecationWarning = "argocd-cm plugins are deprecated, and support will be removed in v2.7. Upgrade your plugin to be installed via sidecar. https://argo-cd.readthedocs.io/en/stable/user-guide/config-management-plugins/" ) const ( @@ -253,6 +308,16 @@ const ( DefaultGitRetryFactor = int64(2) ) +// Constants represent the pod selector labels of the Argo CD component names. These values are determined by the +// installation manifests. +const ( + DefaultServerName = "argocd-server" + DefaultRepoServerName = "argocd-repo-server" + DefaultApplicationControllerName = "argocd-application-controller" + DefaultRedisName = "argocd-redis" + DefaultRedisHaProxyName = "argocd-redis-ha-haproxy" +) + // GetGnuPGHomePath retrieves the path to use for GnuPG home directory, which is either taken from GNUPGHOME environment or a default value func GetGnuPGHomePath() string { if gnuPgHome := os.Getenv(EnvGnuPGHome); gnuPgHome == "" { @@ -295,24 +360,49 @@ func GetCMPWorkDir() string { } const ( - // AnnotationApplicationRefresh is an annotation that is added when an ApplicationSet is requested to be refreshed by a webhook. The ApplicationSet controller will remove this annotation at the end of reconciliation. + // AnnotationApplicationSetRefresh is an annotation that is added when an ApplicationSet is requested to be refreshed by a webhook. The ApplicationSet controller will remove this annotation at the end of reconciliation. AnnotationApplicationSetRefresh = "argocd.argoproj.io/application-set-refresh" ) // gRPC settings const ( - GRPCKeepAliveEnforcementMinimum = 10 * time.Second - // Keep alive is 2x enforcement minimum to ensure network jitter does not introduce ENHANCE_YOUR_CALM errors - GRPCKeepAliveTime = 2 * GRPCKeepAliveEnforcementMinimum + defaultGRPCKeepAliveEnforcementMinimum = 10 * time.Second ) +func GetGRPCKeepAliveEnforcementMinimum() time.Duration { + if GRPCKeepAliveMinStr := os.Getenv(EnvGRPCKeepAliveMin); GRPCKeepAliveMinStr != "" { + GRPCKeepAliveMin, err := time.ParseDuration(GRPCKeepAliveMinStr) + if err != nil { + logrus.Warnf("invalid env var value for %s: cannot parse: %s. Default value %s will be used.", EnvGRPCKeepAliveMin, err, defaultGRPCKeepAliveEnforcementMinimum) + return defaultGRPCKeepAliveEnforcementMinimum + } + return GRPCKeepAliveMin + } + return defaultGRPCKeepAliveEnforcementMinimum +} + +func GetGRPCKeepAliveTime() time.Duration { + // GRPCKeepAliveTime is 2x enforcement minimum to ensure network jitter does not introduce ENHANCE_YOUR_CALM errors + return 2 * GetGRPCKeepAliveEnforcementMinimum() +} + // Security severity logging const ( - SecurityField = "security" - SecurityCWEField = "CWE" - SecurityEmergency = 5 // Indicates unmistakably malicious events that should NEVER occur accidentally and indicates an active attack (i.e. brute forcing, DoS) - SecurityCritical = 4 // Indicates any malicious or exploitable event that had a side effect (i.e. secrets being left behind on the filesystem) - SecurityHigh = 3 // Indicates likely malicious events but one that had no side effects or was blocked (i.e. out of bounds symlinks in repos) - SecurityMedium = 2 // Could indicate malicious events, but has a high likelihood of being user/system error (i.e. access denied) - SecurityLow = 1 // Unexceptional entries (i.e. successful access logs) + SecurityField = "security" + // SecurityCWEField is the logs field for the CWE associated with a log line. CWE stands for Common Weakness Enumeration. See https://cwe.mitre.org/ + SecurityCWEField = "CWE" + SecurityCWEIncompleteCleanup = 459 + SecurityCWEMissingReleaseOfFileDescriptor = 775 + SecurityEmergency = 5 // Indicates unmistakably malicious events that should NEVER occur accidentally and indicates an active attack (i.e. brute forcing, DoS) + SecurityCritical = 4 // Indicates any malicious or exploitable event that had a side effect (i.e. secrets being left behind on the filesystem) + SecurityHigh = 3 // Indicates likely malicious events but one that had no side effects or was blocked (i.e. out of bounds symlinks in repos) + SecurityMedium = 2 // Could indicate malicious events, but has a high likelihood of being user/system error (i.e. access denied) + SecurityLow = 1 // Unexceptional entries (i.e. successful access logs) ) + +// TokenVerificationError is a generic error message for a failure to verify a JWT +const TokenVerificationError = "failed to verify the token" + +var TokenVerificationErr = errors.New(TokenVerificationError) + +var PermissionDeniedAPIError = status.Error(codes.PermissionDenied, "permission denied") diff --git a/common/common_test.go b/common/common_test.go new file mode 100644 index 0000000000000..5632c1e7a78cc --- /dev/null +++ b/common/common_test.go @@ -0,0 +1,46 @@ +package common + +import ( + "fmt" + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +// Test env var not set for EnvGRPCKeepAliveMin +func Test_GRPCKeepAliveMinNotSet(t *testing.T) { + grpcKeepAliveMin := GetGRPCKeepAliveEnforcementMinimum() + grpcKeepAliveExpectedMin := defaultGRPCKeepAliveEnforcementMinimum + assert.Equal(t, grpcKeepAliveExpectedMin, grpcKeepAliveMin) + + grpcKeepAliveTime := GetGRPCKeepAliveTime() + assert.Equal(t, 2*grpcKeepAliveExpectedMin, grpcKeepAliveTime) +} + +// Test valid env var set for EnvGRPCKeepAliveMin +func Test_GRPCKeepAliveMinIsSet(t *testing.T) { + numSeconds := 15 + os.Setenv(EnvGRPCKeepAliveMin, fmt.Sprintf("%ds", numSeconds)) + + grpcKeepAliveMin := GetGRPCKeepAliveEnforcementMinimum() + grpcKeepAliveExpectedMin := time.Duration(numSeconds) * time.Second + assert.Equal(t, grpcKeepAliveExpectedMin, grpcKeepAliveMin) + + grpcKeepAliveTime := GetGRPCKeepAliveTime() + assert.Equal(t, 2*grpcKeepAliveExpectedMin, grpcKeepAliveTime) +} + +// Test invalid env var set for EnvGRPCKeepAliveMin +func Test_GRPCKeepAliveMinIncorrectlySet(t *testing.T) { + numSeconds := 15 + os.Setenv(EnvGRPCKeepAliveMin, fmt.Sprintf("%d", numSeconds)) + + grpcKeepAliveMin := GetGRPCKeepAliveEnforcementMinimum() + grpcKeepAliveExpectedMin := defaultGRPCKeepAliveEnforcementMinimum + assert.Equal(t, grpcKeepAliveExpectedMin, grpcKeepAliveMin) + + grpcKeepAliveTime := GetGRPCKeepAliveTime() + assert.Equal(t, 2*grpcKeepAliveExpectedMin, grpcKeepAliveTime) +} diff --git a/common/version.go b/common/version.go index 8598f98c3171d..e8caf37a30601 100644 --- a/common/version.go +++ b/common/version.go @@ -16,6 +16,7 @@ var ( gitTag = "" // output from `git describe --exact-match --tags HEAD` (if clean tree state) gitTreeState = "" // determined from `git status --porcelain`. either 'clean' or 'dirty' kubectlVersion = "" // determined from go.mod file + extraBuildInfo = "" // extra build information for vendors to populate during build ) // Version contains Argo version information @@ -29,6 +30,7 @@ type Version struct { Compiler string Platform string KubectlVersion string + ExtraBuildInfo string } func (v Version) String() string { @@ -66,6 +68,7 @@ func GetVersion() Version { versionStr += "+unknown" } } + return Version{ Version: versionStr, BuildDate: buildDate, @@ -76,5 +79,6 @@ func GetVersion() Version { Compiler: runtime.Compiler, Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), KubectlVersion: kubectlVersion, + ExtraBuildInfo: extraBuildInfo, } } diff --git a/controller/appcontroller.go b/controller/appcontroller.go index 58d0f0aa74bf5..9d89b6e6b37d6 100644 --- a/controller/appcontroller.go +++ b/controller/appcontroller.go @@ -3,8 +3,10 @@ package controller import ( "context" "encoding/json" + goerrors "errors" "fmt" "math" + "math/rand" "net/http" "reflect" "runtime/debug" @@ -18,6 +20,7 @@ import ( "github.com/argoproj/gitops-engine/pkg/diff" "github.com/argoproj/gitops-engine/pkg/health" synccommon "github.com/argoproj/gitops-engine/pkg/sync/common" + resourceutil "github.com/argoproj/gitops-engine/pkg/sync/resource" "github.com/argoproj/gitops-engine/pkg/utils/kube" jsonpatch "github.com/evanphx/json-patch" log "github.com/sirupsen/logrus" @@ -33,12 +36,16 @@ import ( "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/informers" + informerv1 "k8s.io/client-go/informers/apps/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" + "github.com/argoproj/argo-cd/v2/common" statecache "github.com/argoproj/argo-cd/v2/controller/cache" "github.com/argoproj/argo-cd/v2/controller/metrics" + "github.com/argoproj/argo-cd/v2/controller/sharding" "github.com/argoproj/argo-cd/v2/pkg/apis/application" appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" @@ -47,16 +54,23 @@ import ( "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/util/argo" argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff" + "github.com/argoproj/argo-cd/v2/util/env" + + kubeerrors "k8s.io/apimachinery/pkg/api/errors" + + "github.com/argoproj/argo-cd/v2/pkg/ratelimiter" appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate" "github.com/argoproj/argo-cd/v2/util/db" "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/glob" + "github.com/argoproj/argo-cd/v2/util/helm" logutils "github.com/argoproj/argo-cd/v2/util/log" settings_util "github.com/argoproj/argo-cd/v2/util/settings" ) const ( - updateOperationStateTimeout = 1 * time.Second + updateOperationStateTimeout = 1 * time.Second + defaultDeploymentInformerResyncDuration = 10 * time.Second // orphanedIndex contains application which monitor orphaned resources by namespace orphanedIndex = "orphaned" ) @@ -103,6 +117,7 @@ type ApplicationController struct { stateCache statecache.LiveStateCache statusRefreshTimeout time.Duration statusHardRefreshTimeout time.Duration + statusRefreshJitter time.Duration selfHealTimeout time.Duration repoClientset apiclient.Clientset db db.ArgoDB @@ -111,9 +126,13 @@ type ApplicationController struct { refreshRequestedAppsMutex *sync.Mutex metricsServer *metrics.MetricsServer kubectlSemaphore *semaphore.Weighted - clusterFilter func(cluster *appv1.Cluster) bool + clusterSharding sharding.ClusterShardingCache projByNameCache sync.Map applicationNamespaces []string + + // dynamicClusterDistributionEnabled if disabled deploymentInformer is never initialized + dynamicClusterDistributionEnabled bool + deploymentInformer informerv1.DeploymentInformer } // NewApplicationController creates new instance of ApplicationController. @@ -127,39 +146,50 @@ func NewApplicationController( kubectl kube.Kubectl, appResyncPeriod time.Duration, appHardResyncPeriod time.Duration, + appResyncJitter time.Duration, selfHealTimeout time.Duration, + repoErrorGracePeriod time.Duration, metricsPort int, metricsCacheExpiration time.Duration, metricsApplicationLabels []string, kubectlParallelismLimit int64, persistResourceHealth bool, - clusterFilter func(cluster *appv1.Cluster) bool, + clusterSharding sharding.ClusterShardingCache, applicationNamespaces []string, + rateLimiterConfig *ratelimiter.AppControllerRateLimiterConfig, + serverSideDiff bool, + dynamicClusterDistributionEnabled bool, ) (*ApplicationController, error) { - log.Infof("appResyncPeriod=%v, appHardResyncPeriod=%v", appResyncPeriod, appHardResyncPeriod) + log.Infof("appResyncPeriod=%v, appHardResyncPeriod=%v, appResyncJitter=%v", appResyncPeriod, appHardResyncPeriod, appResyncJitter) db := db.NewDB(namespace, settingsMgr, kubeClientset) + if rateLimiterConfig == nil { + rateLimiterConfig = ratelimiter.GetDefaultAppRateLimiterConfig() + log.Info("Using default workqueue rate limiter config") + } ctrl := ApplicationController{ - cache: argoCache, - namespace: namespace, - kubeClientset: kubeClientset, - kubectl: kubectl, - applicationClientset: applicationClientset, - repoClientset: repoClientset, - appRefreshQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "app_reconciliation_queue"), - appOperationQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "app_operation_processing_queue"), - projectRefreshQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "project_reconciliation_queue"), - appComparisonTypeRefreshQueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), - db: db, - statusRefreshTimeout: appResyncPeriod, - statusHardRefreshTimeout: appHardResyncPeriod, - refreshRequestedApps: make(map[string]CompareWith), - refreshRequestedAppsMutex: &sync.Mutex{}, - auditLogger: argo.NewAuditLogger(namespace, kubeClientset, "argocd-application-controller"), - settingsMgr: settingsMgr, - selfHealTimeout: selfHealTimeout, - clusterFilter: clusterFilter, - projByNameCache: sync.Map{}, - applicationNamespaces: applicationNamespaces, + cache: argoCache, + namespace: namespace, + kubeClientset: kubeClientset, + kubectl: kubectl, + applicationClientset: applicationClientset, + repoClientset: repoClientset, + appRefreshQueue: workqueue.NewNamedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), "app_reconciliation_queue"), + appOperationQueue: workqueue.NewNamedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), "app_operation_processing_queue"), + projectRefreshQueue: workqueue.NewNamedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), "project_reconciliation_queue"), + appComparisonTypeRefreshQueue: workqueue.NewRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig)), + db: db, + statusRefreshTimeout: appResyncPeriod, + statusHardRefreshTimeout: appHardResyncPeriod, + statusRefreshJitter: appResyncJitter, + refreshRequestedApps: make(map[string]CompareWith), + refreshRequestedAppsMutex: &sync.Mutex{}, + auditLogger: argo.NewAuditLogger(namespace, kubeClientset, common.ApplicationController), + settingsMgr: settingsMgr, + selfHealTimeout: selfHealTimeout, + clusterSharding: clusterSharding, + projByNameCache: sync.Map{}, + applicationNamespaces: applicationNamespaces, + dynamicClusterDistributionEnabled: dynamicClusterDistributionEnabled, } if kubectlParallelismLimit > 0 { ctrl.kubectlSemaphore = semaphore.NewWeighted(kubectlParallelismLimit) @@ -168,10 +198,11 @@ func NewApplicationController( appInformer, appLister := ctrl.newApplicationInformerAndLister() indexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc} projInformer := v1alpha1.NewAppProjectInformer(applicationClientset, namespace, appResyncPeriod, indexers) - projInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + var err error + _, err = projInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { if key, err := cache.MetaNamespaceKeyFunc(obj); err == nil { - ctrl.projectRefreshQueue.Add(key) + ctrl.projectRefreshQueue.AddRateLimited(key) if projMeta, ok := obj.(metav1.Object); ok { ctrl.InvalidateProjectsCache(projMeta.GetName()) } @@ -180,7 +211,7 @@ func NewApplicationController( }, UpdateFunc: func(old, new interface{}) { if key, err := cache.MetaNamespaceKeyFunc(new); err == nil { - ctrl.projectRefreshQueue.Add(key) + ctrl.projectRefreshQueue.AddRateLimited(key) if projMeta, ok := new.(metav1.Object); ok { ctrl.InvalidateProjectsCache(projMeta.GetName()) } @@ -188,6 +219,7 @@ func NewApplicationController( }, DeleteFunc: func(obj interface{}) { if key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj); err == nil { + // immediately push to queue for deletes ctrl.projectRefreshQueue.Add(key) if projMeta, ok := obj.(metav1.Object); ok { ctrl.InvalidateProjectsCache(projMeta.GetName()) @@ -195,11 +227,46 @@ func NewApplicationController( } }, }) - metricsAddr := fmt.Sprintf("0.0.0.0:%d", metricsPort) - var err error - ctrl.metricsServer, err = metrics.NewMetricsServer(metricsAddr, appLister, ctrl.canProcessApp, func(r *http.Request) error { + if err != nil { + return nil, err + } + + factory := informers.NewSharedInformerFactoryWithOptions(ctrl.kubeClientset, defaultDeploymentInformerResyncDuration, informers.WithNamespace(settingsMgr.GetNamespace())) + + var deploymentInformer informerv1.DeploymentInformer + + // only initialize deployment informer if dynamic distribution is enabled + if dynamicClusterDistributionEnabled { + deploymentInformer = factory.Apps().V1().Deployments() + } + + readinessHealthCheck := func(r *http.Request) error { + if dynamicClusterDistributionEnabled { + applicationControllerName := env.StringFromEnv(common.EnvAppControllerName, common.DefaultApplicationControllerName) + appControllerDeployment, err := deploymentInformer.Lister().Deployments(settingsMgr.GetNamespace()).Get(applicationControllerName) + if err != nil { + if kubeerrors.IsNotFound(err) { + appControllerDeployment = nil + } else { + return fmt.Errorf("error retrieving Application Controller Deployment: %s", err) + } + } + if appControllerDeployment != nil { + if appControllerDeployment.Spec.Replicas != nil && int(*appControllerDeployment.Spec.Replicas) <= 0 { + return fmt.Errorf("application controller deployment replicas is not set or is less than 0, replicas: %d", appControllerDeployment.Spec.Replicas) + } + shard := env.ParseNumFromEnv(common.EnvControllerShard, -1, -math.MaxInt32, math.MaxInt32) + if _, err := sharding.GetOrUpdateShardFromConfigMap(kubeClientset.(*kubernetes.Clientset), settingsMgr, int(*appControllerDeployment.Spec.Replicas), shard); err != nil { + return fmt.Errorf("error while updating the heartbeat for to the Shard Mapping ConfigMap: %s", err) + } + } + } return nil - }, metricsApplicationLabels) + } + + metricsAddr := fmt.Sprintf("0.0.0.0:%d", metricsPort) + + ctrl.metricsServer, err = metrics.NewMetricsServer(metricsAddr, appLister, ctrl.canProcessApp, readinessHealthCheck, metricsApplicationLabels) if err != nil { return nil, err } @@ -209,11 +276,12 @@ func NewApplicationController( return nil, err } } - stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settingsMgr, kubectl, ctrl.metricsServer, ctrl.handleObjectUpdated, clusterFilter, argo.NewResourceTracking()) - appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.settingsMgr, stateCache, projInformer, ctrl.metricsServer, argoCache, ctrl.statusRefreshTimeout, argo.NewResourceTracking(), persistResourceHealth) + stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settingsMgr, kubectl, ctrl.metricsServer, ctrl.handleObjectUpdated, clusterSharding, argo.NewResourceTracking()) + appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.settingsMgr, stateCache, projInformer, ctrl.metricsServer, argoCache, ctrl.statusRefreshTimeout, argo.NewResourceTracking(), persistResourceHealth, repoErrorGracePeriod, serverSideDiff) ctrl.appInformer = appInformer ctrl.appLister = appLister ctrl.projInformer = projInformer + ctrl.deploymentInformer = deploymentInformer ctrl.appStateManager = appStateManager ctrl.stateCache = stateCache @@ -226,10 +294,12 @@ func (ctrl *ApplicationController) InvalidateProjectsCache(names ...string) { ctrl.projByNameCache.Delete(name) } } else { - ctrl.projByNameCache.Range(func(key, _ interface{}) bool { - ctrl.projByNameCache.Delete(key) - return true - }) + if ctrl != nil { + ctrl.projByNameCache.Range(func(key, _ interface{}) bool { + ctrl.projByNameCache.Delete(key) + return true + }) + } } } @@ -335,7 +405,7 @@ func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]b } if !ctrl.canProcessApp(obj) { - // Don't force refresh app if app belongs to a different controller shard + // Don't force refresh app if app belongs to a different controller shard or is outside the allowed namespaces. continue } @@ -351,17 +421,20 @@ func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]b level = CompareWithRecent } - // Additional check for debug level so we don't need to evaluate the - // format string in case of non-debug scenarios - if log.GetLevel() >= log.DebugLevel { - var resKey string - if ref.Namespace != "" { - resKey = ref.Namespace + "/" + ref.Name - } else { - resKey = "(cluster-scoped)/" + ref.Name - } - log.Debugf("Refreshing app %s for change in cluster of object %s of type %s/%s", appKey, resKey, ref.APIVersion, ref.Kind) + namespace := ref.Namespace + if ref.Namespace == "" { + namespace = "(cluster-scoped)" } + log.WithFields(log.Fields{ + "application": appKey, + "level": level, + "namespace": namespace, + "name": ref.Name, + "api-version": ref.APIVersion, + "kind": ref.Kind, + "server": app.Spec.Destination.Server, + "cluster-name": app.Spec.Destination.Name, + }).Debug("Requesting app refresh caused by object update") ctrl.requestAppRefresh(app.QualifiedName(), &level, nil) } @@ -437,13 +510,13 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed if err != nil { return nil, fmt.Errorf("failed to unmarshal live state of managed resources: %w", err) } - var target = &unstructured.Unstructured{} - err = json.Unmarshal([]byte(managedResource.TargetState), &target) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal target state of managed resources: %w", err) - } if live == nil { + var target = &unstructured.Unstructured{} + err = json.Unmarshal([]byte(managedResource.TargetState), &target) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal target state of managed resources: %w", err) + } nodes = append(nodes, appv1.ResourceNode{ ResourceRef: appv1.ResourceRef{ Version: target.GroupVersionKind().Version, @@ -714,6 +787,24 @@ func (ctrl *ApplicationController) Run(ctx context.Context, statusProcessors int go ctrl.appInformer.Run(ctx.Done()) go ctrl.projInformer.Run(ctx.Done()) + if ctrl.dynamicClusterDistributionEnabled { + // only start deployment informer if dynamic distribution is enabled + go ctrl.deploymentInformer.Informer().Run(ctx.Done()) + } + + clusters, err := ctrl.db.ListClusters(ctx) + if err != nil { + log.Warnf("Cannot init sharding. Error while querying clusters list from database: %v", err) + } else { + appItems, err := ctrl.getAppList(metav1.ListOptions{}) + + if err != nil { + log.Warnf("Cannot init sharding. Error while querying application list from database: %v", err) + } else { + ctrl.clusterSharding.Init(clusters, appItems) + } + } + errors.CheckError(ctrl.stateCache.Init()) if !cache.WaitForCacheSync(ctx.Done(), ctrl.appInformer.HasSynced, ctrl.projInformer.HasSynced) { @@ -767,8 +858,8 @@ func (ctrl *ApplicationController) requestAppRefresh(appName string, compareWith ctrl.appRefreshQueue.AddAfter(key, *after) ctrl.appOperationQueue.AddAfter(key, *after) } else { - ctrl.appRefreshQueue.Add(key) - ctrl.appOperationQueue.Add(key) + ctrl.appRefreshQueue.AddRateLimited(key) + ctrl.appOperationQueue.AddRateLimited(key) } } } @@ -827,17 +918,16 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b if app.Operation != nil { ctrl.processRequestedAppOperation(app) - } else if app.DeletionTimestamp != nil && app.CascadedDeletion() { - _, err = ctrl.finalizeApplicationDeletion(app, func(project string) ([]*appv1.Cluster, error) { + } else if app.DeletionTimestamp != nil { + if err = ctrl.finalizeApplicationDeletion(app, func(project string) ([]*appv1.Cluster, error) { return ctrl.db.GetProjectClusters(context.Background(), project) - }) - if err != nil { + }); err != nil { ctrl.setAppCondition(app, appv1.ApplicationCondition{ Type: appv1.ApplicationConditionDeletionError, Message: err.Error(), }) message := fmt.Sprintf("Unable to delete application resources: %v", err.Error()) - ctrl.auditLogger.LogAppEvent(app, argo.EventInfo{Reason: argo.EventReasonStatusRefreshed, Type: v1.EventTypeWarning}, message) + ctrl.auditLogger.LogAppEvent(app, argo.EventInfo{Reason: argo.EventReasonStatusRefreshed, Type: v1.EventTypeWarning}, message, "") } } return @@ -941,7 +1031,9 @@ func (ctrl *ApplicationController) removeProjectFinalizer(proj *appv1.AppProject // shouldBeDeleted returns whether a given resource obj should be deleted on cascade delete of application app func (ctrl *ApplicationController) shouldBeDeleted(app *appv1.Application, obj *unstructured.Unstructured) bool { - return !kube.IsCRD(obj) && !isSelfReferencedApp(app, kube.GetObjectRef(obj)) + return !kube.IsCRD(obj) && !isSelfReferencedApp(app, kube.GetObjectRef(obj)) && + !resourceutil.HasAnnotationOption(obj, synccommon.AnnotationSyncOptions, synccommon.SyncOptionDisableDeletion) && + !resourceutil.HasAnnotationOption(obj, helm.ResourcePolicyAnnotation, helm.ResourcePolicyKeep) } func (ctrl *ApplicationController) getPermittedAppLiveObjects(app *appv1.Application, proj *appv1.AppProject, projectClusters func(project string) ([]*appv1.Cluster, error)) (map[kube.ResourceKey]*unstructured.Unstructured, error) { @@ -964,57 +1056,63 @@ func (ctrl *ApplicationController) getPermittedAppLiveObjects(app *appv1.Applica return objsMap, nil } -func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Application, projectClusters func(project string) ([]*appv1.Cluster, error)) ([]*unstructured.Unstructured, error) { +func (ctrl *ApplicationController) isValidDestination(app *appv1.Application) (bool, *appv1.Cluster) { + // Validate the cluster using the Application destination's `name` field, if applicable, + // and set the Server field, if needed. + if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil { + log.Warnf("Unable to validate destination of the Application being deleted: %v", err) + return false, nil + } + + cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server) + if err != nil { + log.Warnf("Unable to locate cluster URL for Application being deleted: %v", err) + return false, nil + } + return true, cluster +} + +func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Application, projectClusters func(project string) ([]*appv1.Cluster, error)) error { logCtx := log.WithField("application", app.QualifiedName()) - logCtx.Infof("Deleting resources") // Get refreshed application info, since informer app copy might be stale app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(context.Background(), app.Name, metav1.GetOptions{}) if err != nil { if !apierr.IsNotFound(err) { logCtx.Errorf("Unable to get refreshed application info prior deleting resources: %v", err) } - return nil, nil + return nil } proj, err := ctrl.getAppProj(app) if err != nil { - return nil, err + return err } - // validDestination is true if the Application destination points to a cluster that is managed by Argo CD - // (and thus either a cluster secret exists for it, or it's local); validDestination is false otherwise. - validDestination := true - - // Validate the cluster using the Application destination's `name` field, if applicable, - // and set the Server field, if needed. - if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil { - log.Warnf("Unable to validate destination of the Application being deleted: %v", err) - validDestination = false - } - - objs := make([]*unstructured.Unstructured, 0) - var cluster *appv1.Cluster - - // Attempt to validate the destination via its URL - if validDestination { - if cluster, err = ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server); err != nil { - log.Warnf("Unable to locate cluster URL for Application being deleted: %v", err) - validDestination = false + isValid, cluster := ctrl.isValidDestination(app) + if !isValid { + app.UnSetCascadedDeletion() + app.UnSetPostDeleteFinalizer() + if err := ctrl.updateFinalizers(app); err != nil { + return err } + logCtx.Infof("Resource entries removed from undefined cluster") + return nil } + config := metrics.AddMetricsTransportWrapper(ctrl.metricsServer, app, cluster.RESTConfig()) - if validDestination { + if app.CascadedDeletion() { + logCtx.Infof("Deleting resources") // ApplicationDestination points to a valid cluster, so we may clean up the live objects - + objs := make([]*unstructured.Unstructured, 0) objsMap, err := ctrl.getPermittedAppLiveObjects(app, proj, projectClusters) if err != nil { - return nil, err + return err } for k := range objsMap { // Wait for objects pending deletion to complete before proceeding with next sync wave if objsMap[k].GetDeletionTimestamp() != nil { logCtx.Infof("%d objects remaining for deletion", len(objsMap)) - return objs, nil + return nil } if ctrl.shouldBeDeleted(app, objsMap[k]) { @@ -1022,8 +1120,6 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic } } - config := metrics.AddMetricsTransportWrapper(ctrl.metricsServer, app, cluster.RESTConfig()) - filteredObjs := FilterObjectsForDeletion(objs) propagationPolicy := metav1.DeletePropagationForeground @@ -1037,12 +1133,12 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic return ctrl.kubectl.DeleteResource(context.Background(), config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), metav1.DeleteOptions{PropagationPolicy: &propagationPolicy}) }) if err != nil { - return objs, err + return err } objsMap, err = ctrl.getPermittedAppLiveObjects(app, proj, projectClusters) if err != nil { - return nil, err + return err } for k, obj := range objsMap { @@ -1052,38 +1148,67 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic } if len(objsMap) > 0 { logCtx.Infof("%d objects remaining for deletion", len(objsMap)) - return objs, nil + return nil } + logCtx.Infof("Successfully deleted %d resources", len(objs)) + app.UnSetCascadedDeletion() + return ctrl.updateFinalizers(app) } - if err := ctrl.cache.SetAppManagedResources(app.Name, nil); err != nil { - return objs, err - } + if app.HasPostDeleteFinalizer() { + objsMap, err := ctrl.getPermittedAppLiveObjects(app, proj, projectClusters) + if err != nil { + return err + } - if err := ctrl.cache.SetAppResourcesTree(app.Name, nil); err != nil { - return objs, err + done, err := ctrl.executePostDeleteHooks(app, proj, objsMap, config, logCtx) + if err != nil { + return err + } + if !done { + return nil + } + app.UnSetPostDeleteFinalizer() + return ctrl.updateFinalizers(app) } - if err := ctrl.removeCascadeFinalizer(app); err != nil { - return objs, err + if app.HasPostDeleteFinalizer("cleanup") { + objsMap, err := ctrl.getPermittedAppLiveObjects(app, proj, projectClusters) + if err != nil { + return err + } + + done, err := ctrl.cleanupPostDeleteHooks(objsMap, config, logCtx) + if err != nil { + return err + } + if !done { + return nil + } + app.UnSetPostDeleteFinalizer("cleanup") + return ctrl.updateFinalizers(app) } - if validDestination { - logCtx.Infof("Successfully deleted %d resources", len(objs)) - } else { - logCtx.Infof("Resource entries removed from undefined cluster") + if !app.CascadedDeletion() && !app.HasPostDeleteFinalizer() { + if err := ctrl.cache.SetAppManagedResources(app.Name, nil); err != nil { + return err + } + + if err := ctrl.cache.SetAppResourcesTree(app.Name, nil); err != nil { + return err + } + ctrl.projectRefreshQueue.Add(fmt.Sprintf("%s/%s", ctrl.namespace, app.Spec.GetProject())) } - ctrl.projectRefreshQueue.Add(fmt.Sprintf("%s/%s", ctrl.namespace, app.Spec.GetProject())) - return objs, nil + return nil } -func (ctrl *ApplicationController) removeCascadeFinalizer(app *appv1.Application) error { +func (ctrl *ApplicationController) updateFinalizers(app *appv1.Application) error { _, err := ctrl.getAppProj(app) if err != nil { return fmt.Errorf("error getting project: %w", err) } - app.UnSetCascadedDeletion() + var patch []byte patch, _ = json.Marshal(map[string]interface{}{ "metadata": map[string]interface{}{ @@ -1235,76 +1360,106 @@ 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", logutils.NewLogrusLogger(logutils.NewWithCurrentConfig()), func() error { - if state.Phase == "" { - // expose any bugs where we neglect to set phase - panic("no phase was set") - } - if state.Phase.Completed() { - now := metav1.Now() - state.FinishedAt = &now - } - patch := map[string]interface{}{ - "status": map[string]interface{}{ - "operationState": state, - }, - } - if state.Phase.Completed() { - // If operation is completed, clear the operation field to indicate no operation is - // in progress. - patch["operation"] = nil - } - if reflect.DeepEqual(app.Status.OperationState, state) { - log.Infof("No operation updates necessary to '%s'. Skipping patch", app.QualifiedName()) - return nil - } - patchJSON, err := json.Marshal(patch) + logCtx := log.WithFields(log.Fields{"application": app.Name, "appNamespace": app.Namespace, "project": app.Spec.Project}) + + if state.Phase == "" { + // expose any bugs where we neglect to set phase + panic("no phase was set") + } + if state.Phase.Completed() { + now := metav1.Now() + state.FinishedAt = &now + } + patch := map[string]interface{}{ + "status": map[string]interface{}{ + "operationState": state, + }, + } + if state.Phase.Completed() { + // If operation is completed, clear the operation field to indicate no operation is + // in progress. + patch["operation"] = nil + } + if reflect.DeepEqual(app.Status.OperationState, state) { + logCtx.Infof("No operation updates necessary to '%s'. Skipping patch", app.QualifiedName()) + return + } + patchJSON, err := json.Marshal(patch) + if err != nil { + logCtx.Errorf("error marshaling json: %v", err) + return + } + if app.Status.OperationState != nil && app.Status.OperationState.FinishedAt != nil && state.FinishedAt == nil { + patchJSON, err = jsonpatch.MergeMergePatches(patchJSON, []byte(`{"status": {"operationState": {"finishedAt": null}}}`)) if err != nil { - return fmt.Errorf("error marshaling json: %w", err) - } - if app.Status.OperationState != nil && app.Status.OperationState.FinishedAt != nil && state.FinishedAt == nil { - patchJSON, err = jsonpatch.MergeMergePatches(patchJSON, []byte(`{"status": {"operationState": {"finishedAt": null}}}`)) - if err != nil { - return fmt.Errorf("error merging operation state patch: %w", err) - } + logCtx.Errorf("error merging operation state patch: %v", err) + return } + } - appClient := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace) - _, err = appClient.Patch(context.Background(), app.Name, types.MergePatchType, patchJSON, metav1.PatchOptions{}) + kube.RetryUntilSucceed(context.Background(), updateOperationStateTimeout, "Update application operation state", logutils.NewLogrusLogger(logutils.NewWithCurrentConfig()), func() error { + _, err := ctrl.PatchAppWithWriteBack(context.Background(), app.Name, app.Namespace, types.MergePatchType, patchJSON, metav1.PatchOptions{}) if err != nil { // Stop retrying updating deleted application if apierr.IsNotFound(err) { return nil } + // kube.RetryUntilSucceed logs failed attempts at "debug" level, but we want to know if this fails. Log a + // warning. + logCtx.Warnf("error patching application with operation state: %v", err) return fmt.Errorf("error patching application with operation state: %w", err) } - log.Infof("updated '%s' operation (phase: %s)", app.QualifiedName(), state.Phase) - if state.Phase.Completed() { - eventInfo := argo.EventInfo{Reason: argo.EventReasonOperationCompleted} - var messages []string - if state.Operation.Sync != nil && len(state.Operation.Sync.Resources) > 0 { - messages = []string{"Partial sync operation"} - } else { - messages = []string{"Sync operation"} - } - if state.SyncResult != nil { - messages = append(messages, "to", state.SyncResult.Revision) - } - if state.Phase.Successful() { - eventInfo.Type = v1.EventTypeNormal - messages = append(messages, "succeeded") - } else { - eventInfo.Type = v1.EventTypeWarning - messages = append(messages, "failed:", state.Message) - } - ctrl.auditLogger.LogAppEvent(app, eventInfo, strings.Join(messages, " ")) - ctrl.metricsServer.IncSync(app, state) - } return nil }) + + logCtx.Infof("updated '%s' operation (phase: %s)", app.QualifiedName(), state.Phase) + if state.Phase.Completed() { + eventInfo := argo.EventInfo{Reason: argo.EventReasonOperationCompleted} + var messages []string + if state.Operation.Sync != nil && len(state.Operation.Sync.Resources) > 0 { + messages = []string{"Partial sync operation"} + } else { + messages = []string{"Sync operation"} + } + if state.SyncResult != nil { + messages = append(messages, "to", state.SyncResult.Revision) + } + if state.Phase.Successful() { + eventInfo.Type = v1.EventTypeNormal + messages = append(messages, "succeeded") + } else { + eventInfo.Type = v1.EventTypeWarning + messages = append(messages, "failed:", state.Message) + } + ctrl.auditLogger.LogAppEvent(app, eventInfo, strings.Join(messages, " "), "") + ctrl.metricsServer.IncSync(app, state) + } +} + +// writeBackToInformer writes a just recently updated App back into the informer cache. +// This prevents the situation where the controller operates on a stale app and repeats work +func (ctrl *ApplicationController) writeBackToInformer(app *appv1.Application) { + logCtx := log.WithFields(log.Fields{"application": app.Name, "appNamespace": app.Namespace, "project": app.Spec.Project, "informer-writeBack": true}) + err := ctrl.appInformer.GetStore().Update(app) + if err != nil { + logCtx.Errorf("failed to update informer store: %v", err) + return + } +} + +// PatchAppWithWriteBack patches an application and writes it back to the informer cache +func (ctrl *ApplicationController) PatchAppWithWriteBack(ctx context.Context, name, ns string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *appv1.Application, err error) { + patchedApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ns).Patch(ctx, name, pt, data, opts, subresources...) + if err != nil { + return patchedApp, err + } + ctrl.writeBackToInformer(patchedApp) + return patchedApp, err } func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext bool) { + patchMs := time.Duration(0) // time spent in doing patch/update calls + setOpMs := time.Duration(0) // time spent in doing Operation patch calls in autosync appKey, shutdown := ctrl.appRefreshQueue.Get() if shutdown { processNext = false @@ -1338,18 +1493,22 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo return } app := origApp.DeepCopy() - logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()}) + logCtx := log.WithFields(log.Fields{ + "application": app.QualifiedName(), + "level": comparisonLevel, + "dest-server": origApp.Spec.Destination.Server, + "dest-name": origApp.Spec.Destination.Name, + "dest-namespace": origApp.Spec.Destination.Namespace, + }) startTime := time.Now() defer func() { reconcileDuration := time.Since(startTime) ctrl.metricsServer.IncReconcile(origApp, reconcileDuration) logCtx.WithFields(log.Fields{ - "time_ms": reconcileDuration.Milliseconds(), - "level": comparisonLevel, - "dest-server": origApp.Spec.Destination.Server, - "dest-name": origApp.Spec.Destination.Name, - "dest-namespace": origApp.Spec.Destination.Namespace, + "time_ms": reconcileDuration.Milliseconds(), + "patch_ms": patchMs.Milliseconds(), + "setop_ms": setOpMs.Milliseconds(), }).Info("Reconciliation completed") }() @@ -1367,7 +1526,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo } } - ctrl.persistAppStatus(origApp, &app.Status) + patchMs = ctrl.persistAppStatus(origApp, &app.Status) return } } @@ -1376,7 +1535,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo if hasErrors { app.Status.Sync.Status = appv1.SyncStatusCodeUnknown app.Status.Health.Status = health.HealthStatusUnknown - ctrl.persistAppStatus(origApp, &app.Status) + patchMs = ctrl.persistAppStatus(origApp, &app.Status) if err := ctrl.cache.SetAppResourcesTree(app.InstanceName(ctrl.namespace), &appv1.ApplicationTree{}); err != nil { log.Warnf("failed to set app resource tree: %v", err) @@ -1420,10 +1579,15 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo } now := metav1.Now() - compareResult := ctrl.appStateManager.CompareAppState(app, project, revisions, sources, + compareResult, err := ctrl.appStateManager.CompareAppState(app, project, revisions, sources, refreshType == appv1.RefreshTypeHard, comparisonLevel == CompareWithLatestForceResolve, localManifests, hasMultipleSources) + if goerrors.Is(err, CompareStateRepoError) { + logCtx.Warnf("Ignoring temporary failed attempt to compare app state against repo: %v", err) + return // short circuit if git error is encountered + } + for k, v := range compareResult.timings { logCtx = logCtx.WithField(k, v.Milliseconds()) } @@ -1438,7 +1602,8 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo } if project.Spec.SyncWindows.Matches(app).CanSync(false) { - syncErrCond := ctrl.autoSync(app, compareResult.syncStatus, compareResult.resources) + syncErrCond, opMS := ctrl.autoSync(app, compareResult.syncStatus, compareResult.resources) + setOpMs = opMS if syncErrCond != nil { app.Status.SetConditions( []appv1.ApplicationCondition{*syncErrCond}, @@ -1465,7 +1630,22 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo }) app.Status.SourceType = compareResult.appSourceType app.Status.SourceTypes = compareResult.appSourceTypes - ctrl.persistAppStatus(origApp, &app.Status) + app.Status.ControllerNamespace = ctrl.namespace + patchMs = ctrl.persistAppStatus(origApp, &app.Status) + if (compareResult.hasPostDeleteHooks != app.HasPostDeleteFinalizer() || compareResult.hasPostDeleteHooks != app.HasPostDeleteFinalizer("cleanup")) && + app.GetDeletionTimestamp() == nil { + if compareResult.hasPostDeleteHooks { + app.SetPostDeleteFinalizer() + app.SetPostDeleteFinalizer("cleanup") + } else { + app.UnSetPostDeleteFinalizer() + app.UnSetPostDeleteFinalizer("cleanup") + } + + if err := ctrl.updateFinalizers(app); err != nil { + logCtx.Errorf("Failed to update finalizers: %v", err) + } + } return } @@ -1473,15 +1653,23 @@ func resourceStatusKey(res appv1.ResourceStatus) string { return strings.Join([]string{res.Group, res.Kind, res.Namespace, res.Name}, "/") } +func currentSourceEqualsSyncedSource(app *appv1.Application) bool { + if app.Spec.HasMultipleSources() { + return app.Spec.Sources.Equals(app.Status.Sync.ComparedTo.Sources) + } + return app.Spec.Source.Equals(&app.Status.Sync.ComparedTo.Source) +} + // needRefreshAppStatus answers if application status needs to be refreshed. // Returns true if application never been compared, has changed or comparison result has expired. -// Additionally returns whether full refresh was requested or not. +// Additionally, it returns whether full refresh was requested or not. // If full refresh is requested then target and live state should be reconciled, else only live state tree should be updated. func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application, statusRefreshTimeout, statusHardRefreshTimeout time.Duration) (bool, appv1.RefreshType, CompareWith) { logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()}) var reason string compareWith := CompareWithLatest refreshType := appv1.RefreshTypeNormal + softExpired := app.Status.ReconciledAt == nil || app.Status.ReconciledAt.Add(statusRefreshTimeout).Before(time.Now().UTC()) hardExpired := (app.Status.ReconciledAt == nil || app.Status.ReconciledAt.Add(statusHardRefreshTimeout).Before(time.Now().UTC())) && statusHardRefreshTimeout.Seconds() != 0 @@ -1491,18 +1679,16 @@ func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application, refreshType = requestedType reason = fmt.Sprintf("%s refresh requested", refreshType) } else { - if app.Spec.HasMultipleSources() { - if (len(app.Spec.Sources) != len(app.Status.Sync.ComparedTo.Sources)) || !reflect.DeepEqual(app.Spec.Sources, app.Status.Sync.ComparedTo.Sources) { - reason = "atleast one of the spec.sources differs" - compareWith = CompareWithLatestForceResolve - } - } else if !app.Spec.Source.Equals(app.Status.Sync.ComparedTo.Source) { + if !currentSourceEqualsSyncedSource(app) { reason = "spec.source differs" compareWith = CompareWithLatestForceResolve + if app.Spec.HasMultipleSources() { + reason = "at least one of the spec.sources differs" + } } else if hardExpired || softExpired { // The commented line below mysteriously crashes if app.Status.ReconciledAt is nil // reason = fmt.Sprintf("comparison expired. reconciledAt: %v, expiry: %v", app.Status.ReconciledAt, statusRefreshTimeout) - //TODO: find existing Golang bug or create a new one + // TODO: find existing Golang bug or create a new one reconciledAtStr := "never" if app.Status.ReconciledAt != nil { reconciledAtStr = app.Status.ReconciledAt.String() @@ -1514,6 +1700,10 @@ func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application, } } else if !app.Spec.Destination.Equals(app.Status.Sync.ComparedTo.Destination) { reason = "spec.destination differs" + } else if app.HasChangedManagedNamespaceMetadata() { + reason = "spec.syncPolicy.managedNamespaceMetadata differs" + } else if !app.Spec.IgnoreDifferences.Equals(app.Status.Sync.ComparedTo.IgnoreDifferences) { + reason = "spec.ignoreDifferences differs" } else if requested, level := ctrl.isRefreshRequested(app.QualifiedName()); requested { compareWith = level reason = "controller refresh requested" @@ -1560,8 +1750,7 @@ func (ctrl *ApplicationController) normalizeApplication(orig, app *appv1.Applica if err != nil { logCtx.Errorf("error constructing app spec patch: %v", err) } else if modified { - appClient := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace) - _, err = appClient.Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{}) + _, err := ctrl.PatchAppWithWriteBack(context.Background(), app.Name, app.Namespace, types.MergePatchType, patch, metav1.PatchOptions{}) if err != nil { logCtx.Errorf("Error persisting normalized application spec: %v", err) } else { @@ -1571,15 +1760,15 @@ func (ctrl *ApplicationController) normalizeApplication(orig, app *appv1.Applica } // persistAppStatus persists updates to application status. If no changes were made, it is a no-op -func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, newStatus *appv1.ApplicationStatus) { +func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, newStatus *appv1.ApplicationStatus) (patchMs time.Duration) { logCtx := log.WithFields(log.Fields{"application": orig.QualifiedName()}) if orig.Status.Sync.Status != newStatus.Sync.Status { message := fmt.Sprintf("Updated sync status: %s -> %s", orig.Status.Sync.Status, newStatus.Sync.Status) - ctrl.auditLogger.LogAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message) + ctrl.auditLogger.LogAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message, "") } if orig.Status.Health.Status != newStatus.Health.Status { message := fmt.Sprintf("Updated health status: %s -> %s", orig.Status.Health.Status, newStatus.Health.Status) - ctrl.auditLogger.LogAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message) + ctrl.auditLogger.LogAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message, "") } var newAnnotations map[string]string if orig.GetAnnotations() != nil { @@ -1600,36 +1789,41 @@ func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, new logCtx.Infof("No status changes. Skipping patch") return } - appClient := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(orig.Namespace) - _, err = appClient.Patch(context.Background(), orig.Name, types.MergePatchType, patch, metav1.PatchOptions{}) + // calculate time for path call + start := time.Now() + defer func() { + patchMs = time.Since(start) + }() + _, err = ctrl.PatchAppWithWriteBack(context.Background(), orig.Name, orig.Namespace, types.MergePatchType, patch, metav1.PatchOptions{}) if err != nil { logCtx.Warnf("Error updating application: %v", err) } else { logCtx.Infof("Update successful") } + return patchMs } // autoSync will initiate a sync operation for an application configured with automated sync -func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *appv1.SyncStatus, resources []appv1.ResourceStatus) *appv1.ApplicationCondition { +func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *appv1.SyncStatus, resources []appv1.ResourceStatus) (*appv1.ApplicationCondition, time.Duration) { if app.Spec.SyncPolicy == nil || app.Spec.SyncPolicy.Automated == nil { - return nil + return nil, 0 } logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()}) if app.Operation != nil { logCtx.Infof("Skipping auto-sync: another operation is in progress") - return nil + return nil, 0 } if app.DeletionTimestamp != nil && !app.DeletionTimestamp.IsZero() { logCtx.Infof("Skipping auto-sync: deletion in progress") - return nil + return nil, 0 } // Only perform auto-sync if we detect OutOfSync status. This is to prevent us from attempting // a sync when application is already in a Synced or Unknown state if syncStatus.Status != appv1.SyncStatusCodeOutOfSync { logCtx.Infof("Skipping auto-sync: application status is %s", syncStatus.Status) - return nil + return nil, 0 } if !app.Spec.SyncPolicy.Automated.Prune { @@ -1642,7 +1836,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus * } if requirePruneOnly { logCtx.Infof("Skipping auto-sync: need to prune extra resources only but automated prune is disabled") - return nil + return nil, 0 } } @@ -1671,10 +1865,10 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus * if !attemptPhase.Successful() { logCtx.Warnf("Skipping auto-sync: failed previous sync attempt to %s", desiredCommitSHA) message := fmt.Sprintf("Failed sync attempt to %s: %s", desiredCommitSHA, app.Status.OperationState.Message) - return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: message} + return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: message}, 0 } logCtx.Infof("Skipping auto-sync: most recent sync already to %s", desiredCommitSHA) - return nil + return nil, 0 } else if alreadyAttempted && selfHeal { if shouldSelfHeal, retryAfter := ctrl.shouldSelfHeal(app); shouldSelfHeal { for _, resource := range resources { @@ -1689,7 +1883,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus * } else { logCtx.Infof("Skipping auto-sync: already attempted sync to %s with timeout %v (retrying in %v)", desiredCommitSHA, ctrl.selfHealTimeout, retryAfter) ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatest.Pointer(), &retryAfter) - return nil + return nil, 0 } } @@ -1704,22 +1898,34 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus * if bAllNeedPrune { 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} + return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: message}, 0 } } + appIf := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace) - _, err := argo.SetAppOperation(appIf, app.Name, &op) + start := time.Now() + updatedApp, err := argo.SetAppOperation(appIf, app.Name, &op) + setOpTime := time.Since(start) if err != nil { + if goerrors.Is(err, argo.ErrAnotherOperationInProgress) { + // skipping auto-sync because another operation is in progress and was not noticed due to stale data in informer + // it is safe to skip auto-sync because it is already running + logCtx.Warnf("Failed to initiate auto-sync to %s: %v", desiredCommitSHA, err) + return nil, 0 + } + logCtx.Errorf("Failed to initiate auto-sync to %s: %v", desiredCommitSHA, err) - return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: err.Error()} + return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: err.Error()}, setOpTime + } else { + ctrl.writeBackToInformer(updatedApp) } message := fmt.Sprintf("Initiated automated sync to '%s'", desiredCommitSHA) - ctrl.auditLogger.LogAppEvent(app, argo.EventInfo{Reason: argo.EventReasonOperationStarted, Type: v1.EventTypeNormal}, message) + ctrl.auditLogger.LogAppEvent(app, argo.EventInfo{Reason: argo.EventReasonOperationStarted, Type: v1.EventTypeNormal}, message, "") logCtx.Info(message) - return nil + return nil, setOpTime } -// alreadyAttemptedSync returns whether or not the most recent sync was performed against the +// alreadyAttemptedSync returns whether the most recent sync was performed against the // commitSHA and with the same app source config which are currently set in the app func alreadyAttemptedSync(app *appv1.Application, commitSHA string, commitSHAsMS []string, hasMultipleSources bool) (bool, synccommon.OperationPhase) { if app.Status.OperationState == nil || app.Status.OperationState.Operation.Sync == nil || app.Status.OperationState.SyncResult == nil { @@ -1772,26 +1978,43 @@ func (ctrl *ApplicationController) shouldSelfHeal(app *appv1.Application) (bool, return retryAfter <= 0, retryAfter } +// isAppNamespaceAllowed returns whether the application is allowed in the +// namespace it's residing in. +func (ctrl *ApplicationController) isAppNamespaceAllowed(app *appv1.Application) bool { + return app.Namespace == ctrl.namespace || glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false) +} + 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) - } // Only process given app if it exists in a watched namespace, or in the // control plane's namespace. - if app.Namespace != ctrl.namespace && !glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false) { + if !ctrl.isAppNamespaceAllowed(app) { return false } - return true + if annotations := app.GetAnnotations(); annotations != nil { + if skipVal, ok := annotations[common.AnnotationKeyAppSkipReconcile]; ok { + logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()}) + if skipReconcile, err := strconv.ParseBool(skipVal); err == nil { + if skipReconcile { + logCtx.Debugf("Skipping Application reconcile based on annotation %s", common.AnnotationKeyAppSkipReconcile) + return false + } + } else { + logCtx.Debugf("Unable to determine if Application should skip reconcile based on annotation %s: %v", common.AnnotationKeyAppSkipReconcile, err) + } + } + } + + cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server) + if err != nil { + return ctrl.clusterSharding.IsManagedCluster(nil) + } + return ctrl.clusterSharding.IsManagedCluster(cluster) } func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.SharedIndexInformer, applisters.ApplicationLister) { @@ -1816,7 +2039,7 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar } newItems := []appv1.Application{} for _, app := range appList.Items { - if ctrl.namespace == app.Namespace || glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false) { + if ctrl.isAppNamespaceAllowed(&app) { newItems = append(newItems, app) } } @@ -1833,20 +2056,24 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar cache.NamespaceIndex: func(obj interface{}) ([]string, error) { app, ok := obj.(*appv1.Application) if ok { - // This call to 'ValidateDestination' ensures that the .spec.destination field of all Applications - // returned by the informer/lister will have server field set (if not already set) based on the name. - // (or, if not found, an error app condition) - - // If the server field is not set, set it based on the cluster name; if the cluster name can't be found, - // log an error as an App Condition. - if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil { - ctrl.setAppCondition(app, appv1.ApplicationCondition{Type: appv1.ApplicationConditionInvalidSpecError, Message: err.Error()}) - } - - // If the application is not allowed to use the project, - // log an error. - if _, err := ctrl.getAppProj(app); err != nil { - ctrl.setAppCondition(app, ctrl.projectErrorToCondition(err, app)) + // We only generally work with applications that are in one + // the allowed namespaces. + if ctrl.isAppNamespaceAllowed(app) { + // If the application is not allowed to use the project, + // log an error. + if _, err := ctrl.getAppProj(app); err != nil { + ctrl.setAppCondition(app, ctrl.projectErrorToCondition(err, app)) + } else { + // This call to 'ValidateDestination' ensures that the .spec.destination field of all Applications + // returned by the informer/lister will have server field set (if not already set) based on the name. + // (or, if not found, an error app condition) + + // If the server field is not set, set it based on the cluster name; if the cluster name can't be found, + // log an error as an App Condition. + if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil { + ctrl.setAppCondition(app, appv1.ApplicationCondition{Type: appv1.ApplicationConditionInvalidSpecError, Message: err.Error()}) + } + } } } @@ -1858,7 +2085,11 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar return nil, nil } - proj, err := applisters.NewAppProjectLister(ctrl.projInformer.GetIndexer()).AppProjects(ctrl.namespace).Get(app.Spec.GetProject()) + if !ctrl.isAppNamespaceAllowed(app) { + return nil, nil + } + + proj, err := ctrl.getAppProj(app) if err != nil { return nil, nil } @@ -1870,7 +2101,7 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar }, ) lister := applisters.NewApplicationLister(informer.GetIndexer()) - informer.AddEventHandler( + _, err := informer.AddEventHandler( cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { if !ctrl.canProcessApp(obj) { @@ -1878,8 +2109,12 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar } key, err := cache.MetaNamespaceKeyFunc(obj) if err == nil { - ctrl.appRefreshQueue.Add(key) - ctrl.appOperationQueue.Add(key) + ctrl.appRefreshQueue.AddRateLimited(key) + ctrl.appOperationQueue.AddRateLimited(key) + } + newApp, newOK := obj.(*appv1.Application) + if err == nil && newOK { + ctrl.clusterSharding.AddApp(newApp) } }, UpdateFunc: func(old, new interface{}) { @@ -1891,15 +2126,27 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar if err != nil { return } + var compareWith *CompareWith + var delay *time.Duration + oldApp, oldOK := old.(*appv1.Application) newApp, newOK := new.(*appv1.Application) - if oldOK && newOK && automatedSyncEnabled(oldApp, newApp) { - log.WithField("application", newApp.QualifiedName()).Info("Enabled automated sync") - compareWith = CompareWithLatest.Pointer() + if oldOK && newOK { + if automatedSyncEnabled(oldApp, newApp) { + log.WithField("application", newApp.QualifiedName()).Info("Enabled automated sync") + compareWith = CompareWithLatest.Pointer() + } + if ctrl.statusRefreshJitter != 0 && oldApp.ResourceVersion == newApp.ResourceVersion { + // Handler is refreshing the apps, add a random jitter to spread the load and avoid spikes + jitter := time.Duration(float64(ctrl.statusRefreshJitter) * rand.Float64()) + delay = &jitter + } } - ctrl.requestAppRefresh(newApp.QualifiedName(), compareWith, nil) - ctrl.appOperationQueue.Add(key) + + ctrl.requestAppRefresh(newApp.QualifiedName(), compareWith, delay) + ctrl.appOperationQueue.AddRateLimited(key) + ctrl.clusterSharding.UpdateApp(newApp) }, DeleteFunc: func(obj interface{}) { if !ctrl.canProcessApp(obj) { @@ -1909,11 +2156,19 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar // key function. key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) if err == nil { + // for deletes, we immediately add to the refresh queue ctrl.appRefreshQueue.Add(key) } + delApp, delOK := obj.(*appv1.Application) + if err == nil && delOK { + ctrl.clusterSharding.DeleteApp(delApp) + } }, }, ) + if err != nil { + return nil, nil + } return informer, lister } @@ -1931,7 +2186,7 @@ func (ctrl *ApplicationController) projectErrorToCondition(err error, app *appv1 } func (ctrl *ApplicationController) RegisterClusterSecretUpdater(ctx context.Context) { - updater := NewClusterInfoUpdater(ctrl.stateCache, ctrl.db, ctrl.appLister.Applications(""), ctrl.cache, ctrl.clusterFilter, ctrl.getAppProj, ctrl.namespace) + updater := NewClusterInfoUpdater(ctrl.stateCache, ctrl.db, ctrl.appLister.Applications(""), ctrl.cache, ctrl.clusterSharding.IsManagedCluster, ctrl.getAppProj, ctrl.namespace) go updater.Run(ctx) } @@ -1982,3 +2237,27 @@ func (ctrl *ApplicationController) toAppKey(appName string) string { func (ctrl *ApplicationController) toAppQualifiedName(appName, appNamespace string) string { return fmt.Sprintf("%s/%s", appNamespace, appName) } + +func (ctrl *ApplicationController) getAppList(options metav1.ListOptions) (*appv1.ApplicationList, error) { + watchNamespace := ctrl.namespace + // If we have at least one additional namespace configured, we need to + // watch on them all. + if len(ctrl.applicationNamespaces) > 0 { + watchNamespace = "" + } + + appList, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(watchNamespace).List(context.TODO(), options) + if err != nil { + return nil, err + } + newItems := []appv1.Application{} + for _, app := range appList.Items { + if ctrl.isAppNamespaceAllowed(&app) { + newItems = append(newItems, app) + } + } + appList.Items = newItems + return appList, nil +} + +type ClusterFilterFunction func(c *appv1.Cluster, distributionFunction sharding.DistributionFunction) bool diff --git a/controller/appcontroller_test.go b/controller/appcontroller_test.go index fff4c7f37db5c..33a29bc5ca3f8 100644 --- a/controller/appcontroller_test.go +++ b/controller/appcontroller_test.go @@ -3,21 +3,26 @@ package controller import ( "context" "encoding/json" + "errors" "testing" "time" + "github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/client-go/rest" clustercache "github.com/argoproj/gitops-engine/pkg/cache" "github.com/argoproj/argo-cd/v2/common" statecache "github.com/argoproj/argo-cd/v2/controller/cache" + "github.com/argoproj/argo-cd/v2/controller/sharding" + dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" "github.com/argoproj/gitops-engine/pkg/cache/mocks" synccommon "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/argoproj/gitops-engine/pkg/utils/kube" - "github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest" - "github.com/ghodss/yaml" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" corev1 "k8s.io/api/core/v1" @@ -29,10 +34,10 @@ import ( "k8s.io/client-go/kubernetes/fake" kubetesting "k8s.io/client-go/testing" "k8s.io/client-go/tools/cache" + "sigs.k8s.io/yaml" mockstatecache "github.com/argoproj/argo-cd/v2/controller/cache/mocks" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" mockrepoclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks" @@ -43,20 +48,39 @@ import ( ) type namespacedResource struct { - argoappv1.ResourceNode + v1alpha1.ResourceNode AppName string } type fakeData struct { apps []runtime.Object manifestResponse *apiclient.ManifestResponse + manifestResponses []*apiclient.ManifestResponse managedLiveObjs map[kube.ResourceKey]*unstructured.Unstructured namespacedResources map[kube.ResourceKey]namespacedResource configMapData map[string]string metricsCacheExpiration time.Duration + applicationNamespaces []string +} + +type MockKubectl struct { + kube.Kubectl + + DeletedResources []kube.ResourceKey + CreatedResources []*unstructured.Unstructured +} + +func (m *MockKubectl) CreateResource(ctx context.Context, config *rest.Config, gvk schema.GroupVersionKind, name string, namespace string, obj *unstructured.Unstructured, createOptions metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) { + m.CreatedResources = append(m.CreatedResources, obj) + return m.Kubectl.CreateResource(ctx, config, gvk, name, namespace, obj, createOptions, subresources...) +} + +func (m *MockKubectl) DeleteResource(ctx context.Context, config *rest.Config, gvk schema.GroupVersionKind, name string, namespace string, deleteOptions metav1.DeleteOptions) error { + m.DeletedResources = append(m.DeletedResources, kube.NewResourceKey(gvk.Group, gvk.Kind, namespace, name)) + return m.Kubectl.DeleteResource(ctx, config, gvk, name, namespace, deleteOptions) } -func newFakeController(data *fakeData) *ApplicationController { +func newFakeController(data *fakeData, repoErr error) *ApplicationController { var clust corev1.Secret err := yaml.Unmarshal([]byte(fakeCluster), &clust) if err != nil { @@ -65,7 +89,23 @@ func newFakeController(data *fakeData) *ApplicationController { // Mock out call to GenerateManifest mockRepoClient := mockrepoclient.RepoServerServiceClient{} - mockRepoClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(data.manifestResponse, nil) + + if len(data.manifestResponses) > 0 { + for _, response := range data.manifestResponses { + if repoErr != nil { + mockRepoClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(response, repoErr).Once() + } else { + mockRepoClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(response, nil).Once() + } + } + } else { + if repoErr != nil { + mockRepoClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(data.manifestResponse, repoErr).Once() + } else { + mockRepoClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(data.manifestResponse, nil).Once() + } + } + mockRepoClientset := mockrepoclient.Clientset{RepoServerServiceClient: &mockRepoClient} secret := corev1.Secret{ @@ -90,7 +130,7 @@ func newFakeController(data *fakeData) *ApplicationController { } kubeClient := fake.NewSimpleClientset(&clust, &cm, &secret) settingsMgr := settings.NewSettingsManager(context.Background(), kubeClient, test.FakeArgoCDNamespace) - kubectl := &kubetest.MockKubectlCmd{} + kubectl := &MockKubectl{Kubectl: &kubetest.MockKubectlCmd{}} ctrl, err := NewApplicationController( test.FakeArgoCDNamespace, settingsMgr, @@ -104,15 +144,25 @@ func newFakeController(data *fakeData) *ApplicationController { kubectl, time.Minute, time.Hour, + time.Second, time.Minute, + time.Second*10, common.DefaultPortArgoCDMetrics, data.metricsCacheExpiration, []string{}, 0, true, nil, - []string{}, + data.applicationNamespaces, + nil, + + false, + false, ) + db := &dbmocks.ArgoDB{} + db.On("GetApplicationControllerReplicas").Return(1) + // Setting a default sharding algorithm for the tests where we cannot set it. + ctrl.clusterSharding = sharding.NewClusterSharding(db, 0, 1, common.DefaultShardingAlgorithm) if err != nil { panic(err) } @@ -131,7 +181,7 @@ func newFakeController(data *fakeData) *ApplicationController { mockStateCache.On("IsNamespaced", mock.Anything, mock.Anything).Return(true, nil) mockStateCache.On("GetManagedLiveObjs", mock.Anything, mock.Anything).Return(data.managedLiveObjs, nil) mockStateCache.On("GetVersionsInfo", mock.Anything).Return("v1.2.3", nil, nil) - response := make(map[kube.ResourceKey]argoappv1.ResourceNode) + response := make(map[kube.ResourceKey]v1alpha1.ResourceNode) for k, v := range data.namespacedResources { response[k] = v.ResourceNode } @@ -140,12 +190,12 @@ func newFakeController(data *fakeData) *ApplicationController { mockStateCache.On("GetClusterCache", mock.Anything).Return(&clusterCacheMock, nil) mockStateCache.On("IterateHierarchy", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { key := args[1].(kube.ResourceKey) - action := args[2].(func(child argoappv1.ResourceNode, appName string) bool) + action := args[2].(func(child v1alpha1.ResourceNode, appName string) bool) appName := "" if res, ok := data.namespacedResources[key]; ok { appName = res.AppName } - _ = action(argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: key.Kind, Group: key.Group, Namespace: key.Namespace, Name: key.Name}}, appName) + _ = action(v1alpha1.ResourceNode{ResourceRef: v1alpha1.ResourceRef{Kind: key.Kind, Group: key.Group, Namespace: key.Namespace, Name: key.Name}}, appName) }).Return(nil) return ctrl } @@ -167,7 +217,6 @@ metadata: namespace: ` + test.FakeArgoCDNamespace + ` type: Opaque ` - var fakeApp = ` apiVersion: argoproj.io/v1alpha1 kind: Application @@ -209,6 +258,64 @@ status: repoURL: https://github.com/argoproj/argocd-example-apps.git ` +var fakeMultiSourceApp = ` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + uid: "123" + name: my-app + namespace: ` + test.FakeArgoCDNamespace + ` +spec: + destination: + namespace: ` + test.FakeDestNamespace + ` + server: https://localhost:6443 + project: default + sources: + - path: some/path + helm: + valueFiles: + - $values_test/values.yaml + repoURL: https://github.com/argoproj/argocd-example-apps.git + - path: some/other/path + repoURL: https://github.com/argoproj/argocd-example-apps-fake.git + - ref: values_test + repoURL: https://github.com/argoproj/argocd-example-apps-fake-ref.git + syncPolicy: + automated: {} +status: + operationState: + finishedAt: 2018-09-21T23:50:29Z + message: successfully synced + operation: + sync: + revisions: + - HEAD + - HEAD + - HEAD + phase: Succeeded + startedAt: 2018-09-21T23:50:25Z + syncResult: + resources: + - kind: RoleBinding + message: |- + rolebinding.rbac.authorization.k8s.io/always-outofsync reconciled + rolebinding.rbac.authorization.k8s.io/always-outofsync configured + name: always-outofsync + namespace: default + status: Synced + revisions: + - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + - cccccccccccccccccccccccccccccccccccccccc + sources: + - path: some/path + repoURL: https://github.com/argoproj/argocd-example-apps.git + - path: some/other/path + repoURL: https://github.com/argoproj/argocd-example-apps-fake.git + - path: some/other/path + repoURL: https://github.com/argoproj/argocd-example-apps-fake-ref.git +` + var fakeAppWithDestName = ` apiVersion: argoproj.io/v1alpha1 kind: Application @@ -259,20 +366,56 @@ metadata: data: ` -func newFakeApp() *argoappv1.Application { +var fakePostDeleteHook = ` +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "name": "post-delete-hook", + "namespace": "default", + "labels": { + "app.kubernetes.io/instance": "my-app" + }, + "annotations": { + "argocd.argoproj.io/hook": "PostDelete", + "argocd.argoproj.io/hook-delete-policy": "HookSucceeded" + } + }, + "spec": { + "containers": [ + { + "name": "post-delete-hook", + "image": "busybox", + "restartPolicy": "Never", + "command": [ + "/bin/sh", + "-c", + "sleep 5 && echo hello from the post-delete-hook pod" + ] + } + ] + } +} +` + +func newFakeApp() *v1alpha1.Application { return createFakeApp(fakeApp) } -func newFakeAppWithDestMismatch() *argoappv1.Application { +func newFakeMultiSourceApp() *v1alpha1.Application { + return createFakeApp(fakeMultiSourceApp) +} + +func newFakeAppWithDestMismatch() *v1alpha1.Application { return createFakeApp(fakeAppWithDestMismatch) } -func newFakeAppWithDestName() *argoappv1.Application { +func newFakeAppWithDestName() *v1alpha1.Application { return createFakeApp(fakeAppWithDestName) } -func createFakeApp(testApp string) *argoappv1.Application { - var app argoappv1.Application +func createFakeApp(testApp string) *v1alpha1.Application { + var app v1alpha1.Application err := yaml.Unmarshal([]byte(testApp), &app) if err != nil { panic(err) @@ -289,14 +432,23 @@ func newFakeCM() map[string]interface{} { return cm } +func newFakePostDeleteHook() map[string]interface{} { + var cm map[string]interface{} + err := yaml.Unmarshal([]byte(fakePostDeleteHook), &cm) + if err != nil { + panic(err) + } + return cm +} + func TestAutoSync(t *testing.T) { app := newFakeApp() - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) - syncStatus := argoappv1.SyncStatus{ - Status: argoappv1.SyncStatusCodeOutOfSync, + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + syncStatus := v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: argoappv1.SyncStatusCodeOutOfSync}}) + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}) assert.Nil(t, cond) app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{}) assert.NoError(t, err) @@ -308,12 +460,12 @@ func TestAutoSync(t *testing.T) { 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, + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + syncStatus := v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{}) + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}) assert.NotNil(t, cond) } @@ -321,12 +473,12 @@ 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, + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + syncStatus := v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{}) + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}) assert.Nil(t, cond) } @@ -335,12 +487,12 @@ func TestSkipAutoSync(t *testing.T) { // Set current to 'aaaaa', desired to 'aaaa' and mark system OutOfSync t.Run("PreviouslySyncedToRevision", func(t *testing.T) { app := newFakeApp() - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) - syncStatus := argoappv1.SyncStatus{ - Status: argoappv1.SyncStatusCodeOutOfSync, + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + syncStatus := v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", } - cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{}) + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}) assert.Nil(t, cond) app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{}) assert.NoError(t, err) @@ -350,12 +502,12 @@ func TestSkipAutoSync(t *testing.T) { // Verify we skip when we are already Synced (even if revision is different) t.Run("AlreadyInSyncedState", func(t *testing.T) { app := newFakeApp() - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) - syncStatus := argoappv1.SyncStatus{ - Status: argoappv1.SyncStatusCodeSynced, + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + syncStatus := v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{}) + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}) assert.Nil(t, cond) app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{}) assert.NoError(t, err) @@ -366,12 +518,12 @@ func TestSkipAutoSync(t *testing.T) { t.Run("AutoSyncIsDisabled", func(t *testing.T) { app := newFakeApp() app.Spec.SyncPolicy = nil - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) - syncStatus := argoappv1.SyncStatus{ - Status: argoappv1.SyncStatusCodeOutOfSync, + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + syncStatus := v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{}) + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}) assert.Nil(t, cond) app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{}) assert.NoError(t, err) @@ -383,12 +535,12 @@ func TestSkipAutoSync(t *testing.T) { app := newFakeApp() now := metav1.Now() app.DeletionTimestamp = &now - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) - syncStatus := argoappv1.SyncStatus{ - Status: argoappv1.SyncStatusCodeOutOfSync, + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + syncStatus := v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{}) + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{}) assert.Nil(t, cond) app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{}) assert.NoError(t, err) @@ -399,22 +551,22 @@ func TestSkipAutoSync(t *testing.T) { // Set current to 'aaaaa', desired to 'bbbbb' and add 'bbbbb' to failure history t.Run("PreviousSyncAttemptFailed", func(t *testing.T) { app := newFakeApp() - app.Status.OperationState = &argoappv1.OperationState{ - Operation: argoappv1.Operation{ - Sync: &argoappv1.SyncOperation{}, + app.Status.OperationState = &v1alpha1.OperationState{ + Operation: v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{}, }, Phase: synccommon.OperationFailed, - SyncResult: &argoappv1.SyncOperationResult{ + SyncResult: &v1alpha1.SyncOperationResult{ Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", Source: *app.Spec.Source.DeepCopy(), }, } - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) - syncStatus := argoappv1.SyncStatus{ - Status: argoappv1.SyncStatusCodeOutOfSync, + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + syncStatus := v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: argoappv1.SyncStatusCodeOutOfSync}}) + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}) assert.NotNil(t, cond) app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{}) assert.NoError(t, err) @@ -423,13 +575,13 @@ func TestSkipAutoSync(t *testing.T) { t.Run("NeedsToPruneResourcesOnlyButAutomatedPruneDisabled", func(t *testing.T) { app := newFakeApp() - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) - syncStatus := argoappv1.SyncStatus{ - Status: argoappv1.SyncStatusCodeOutOfSync, + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + syncStatus := v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", } - cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{ - {Name: "guestbook", Kind: kube.DeploymentKind, Status: argoappv1.SyncStatusCodeOutOfSync, RequiresPruning: true}, + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{ + {Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync, RequiresPruning: true}, }) assert.Nil(t, cond) app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{}) @@ -441,32 +593,32 @@ func TestSkipAutoSync(t *testing.T) { // TestAutoSyncIndicateError verifies we skip auto-sync and return error condition if previous sync failed func TestAutoSyncIndicateError(t *testing.T) { app := newFakeApp() - app.Spec.Source.Helm = &argoappv1.ApplicationSourceHelm{ - Parameters: []argoappv1.HelmParameter{ + app.Spec.Source.Helm = &v1alpha1.ApplicationSourceHelm{ + Parameters: []v1alpha1.HelmParameter{ { Name: "a", Value: "1", }, }, } - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) - syncStatus := argoappv1.SyncStatus{ - Status: argoappv1.SyncStatusCodeOutOfSync, + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + syncStatus := v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", } - app.Status.OperationState = &argoappv1.OperationState{ - Operation: argoappv1.Operation{ - Sync: &argoappv1.SyncOperation{ + app.Status.OperationState = &v1alpha1.OperationState{ + Operation: v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{ Source: app.Spec.Source.DeepCopy(), }, }, Phase: synccommon.OperationFailed, - SyncResult: &argoappv1.SyncOperationResult{ + SyncResult: &v1alpha1.SyncOperationResult{ Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Source: *app.Spec.Source.DeepCopy(), }, } - cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: argoappv1.SyncStatusCodeOutOfSync}}) + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}) assert.NotNil(t, cond) app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{}) assert.NoError(t, err) @@ -476,25 +628,25 @@ func TestAutoSyncIndicateError(t *testing.T) { // TestAutoSyncParameterOverrides verifies we auto-sync if revision is same but parameter overrides are different func TestAutoSyncParameterOverrides(t *testing.T) { app := newFakeApp() - app.Spec.Source.Helm = &argoappv1.ApplicationSourceHelm{ - Parameters: []argoappv1.HelmParameter{ + app.Spec.Source.Helm = &v1alpha1.ApplicationSourceHelm{ + Parameters: []v1alpha1.HelmParameter{ { Name: "a", Value: "1", }, }, } - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) - syncStatus := argoappv1.SyncStatus{ - Status: argoappv1.SyncStatusCodeOutOfSync, + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + syncStatus := v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeOutOfSync, Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", } - app.Status.OperationState = &argoappv1.OperationState{ - Operation: argoappv1.Operation{ - Sync: &argoappv1.SyncOperation{ - Source: &argoappv1.ApplicationSource{ - Helm: &argoappv1.ApplicationSourceHelm{ - Parameters: []argoappv1.HelmParameter{ + app.Status.OperationState = &v1alpha1.OperationState{ + Operation: v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{ + Source: &v1alpha1.ApplicationSource{ + Helm: &v1alpha1.ApplicationSourceHelm{ + Parameters: []v1alpha1.HelmParameter{ { Name: "a", Value: "2", // this value changed @@ -505,11 +657,11 @@ func TestAutoSyncParameterOverrides(t *testing.T) { }, }, Phase: synccommon.OperationFailed, - SyncResult: &argoappv1.SyncOperationResult{ + SyncResult: &v1alpha1.SyncOperationResult{ Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", }, } - cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: argoappv1.SyncStatusCodeOutOfSync}}) + cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}) assert.Nil(t, cond) app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{}) assert.NoError(t, err) @@ -518,14 +670,14 @@ func TestAutoSyncParameterOverrides(t *testing.T) { // TestFinalizeAppDeletion verifies application deletion func TestFinalizeAppDeletion(t *testing.T) { - defaultProj := argoappv1.AppProject{ + defaultProj := v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: test.FakeArgoCDNamespace, }, - Spec: argoappv1.AppProjectSpec{ + Spec: v1alpha1.AppProjectSpec{ SourceRepos: []string{"*"}, - Destinations: []argoappv1.ApplicationDestination{ + Destinations: []v1alpha1.ApplicationDestination{ { Server: "*", Namespace: "*", @@ -537,12 +689,12 @@ func TestFinalizeAppDeletion(t *testing.T) { // Ensure app can be deleted cascading t.Run("CascadingDelete", func(t *testing.T) { app := newFakeApp() + app.SetCascadedDeletion(v1alpha1.ResourcesFinalizerName) app.Spec.Destination.Namespace = test.FakeArgoCDNamespace appObj := kube.MustToUnstructured(&app) ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}, managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ kube.GetResourceKey(appObj): appObj, - }}) - + }}, nil) patched := false fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) defaultReactor := fakeAppCs.ReactionChain[0] @@ -552,10 +704,10 @@ func TestFinalizeAppDeletion(t *testing.T) { }) fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { patched = true - return true, nil, nil + return true, &v1alpha1.Application{}, nil }) - _, err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*argoappv1.Cluster, error) { - return []*argoappv1.Cluster{}, nil + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + return []*v1alpha1.Cluster{}, nil }) assert.NoError(t, err) assert.True(t, patched) @@ -564,14 +716,14 @@ func TestFinalizeAppDeletion(t *testing.T) { // Ensure any stray resources irregularly labeled with instance label of app are not deleted upon deleting, // when app project restriction is in place t.Run("ProjectRestrictionEnforced", func(*testing.T) { - restrictedProj := argoappv1.AppProject{ + restrictedProj := v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{ Name: "restricted", Namespace: test.FakeArgoCDNamespace, }, - Spec: argoappv1.AppProjectSpec{ + Spec: v1alpha1.AppProjectSpec{ SourceRepos: []string{"*"}, - Destinations: []argoappv1.ApplicationDestination{ + Destinations: []v1alpha1.ApplicationDestination{ { Server: "*", Namespace: "my-app", @@ -580,6 +732,7 @@ func TestFinalizeAppDeletion(t *testing.T) { }, } app := newFakeApp() + app.SetCascadedDeletion(v1alpha1.ResourcesFinalizerName) app.Spec.Destination.Namespace = test.FakeArgoCDNamespace app.Spec.Project = "restricted" appObj := kube.MustToUnstructured(&app) @@ -591,7 +744,7 @@ func TestFinalizeAppDeletion(t *testing.T) { kube.GetResourceKey(appObj): appObj, kube.GetResourceKey(strayObj): strayObj, }, - }) + }, nil) patched := false fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) @@ -602,10 +755,10 @@ func TestFinalizeAppDeletion(t *testing.T) { }) fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { patched = true - return true, nil, nil + return true, &v1alpha1.Application{}, nil }) - objs, err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*argoappv1.Cluster, error) { - return []*argoappv1.Cluster{}, nil + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + return []*v1alpha1.Cluster{}, nil }) assert.NoError(t, err) assert.True(t, patched) @@ -615,18 +768,20 @@ func TestFinalizeAppDeletion(t *testing.T) { } // Managed objects must be empty assert.Empty(t, objsMap) + // Loop through all deleted objects, ensure that test-cm is none of them - for _, o := range objs { - assert.NotEqual(t, "test-cm", o.GetName()) + for _, o := range ctrl.kubectl.(*MockKubectl).DeletedResources { + assert.NotEqual(t, "test-cm", o.Name) } }) t.Run("DeleteWithDestinationClusterName", func(t *testing.T) { app := newFakeAppWithDestName() + app.SetCascadedDeletion(v1alpha1.ResourcesFinalizerName) appObj := kube.MustToUnstructured(&app) ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}, managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ kube.GetResourceKey(appObj): appObj, - }}) + }}, nil) patched := false fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) defaultReactor := fakeAppCs.ReactionChain[0] @@ -636,10 +791,10 @@ func TestFinalizeAppDeletion(t *testing.T) { }) fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { patched = true - return true, nil, nil + return true, &v1alpha1.Application{}, nil }) - _, err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*argoappv1.Cluster, error) { - return []*argoappv1.Cluster{}, nil + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + return []*v1alpha1.Cluster{}, nil }) assert.NoError(t, err) assert.True(t, patched) @@ -651,11 +806,11 @@ func TestFinalizeAppDeletion(t *testing.T) { appTemplate := newFakeAppWithDestName() - testShouldDelete := func(app *argoappv1.Application) { + testShouldDelete := func(app *v1alpha1.Application) { appObj := kube.MustToUnstructured(&app) ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}, managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ kube.GetResourceKey(appObj): appObj, - }}) + }}, nil) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) defaultReactor := fakeAppCs.ReactionChain[0] @@ -663,8 +818,8 @@ func TestFinalizeAppDeletion(t *testing.T) { fakeAppCs.AddReactor("get", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { return defaultReactor.React(action) }) - _, err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*argoappv1.Cluster, error) { - return []*argoappv1.Cluster{}, nil + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + return []*v1alpha1.Cluster{}, nil }) assert.NoError(t, err) } @@ -684,18 +839,121 @@ func TestFinalizeAppDeletion(t *testing.T) { }) + t.Run("PostDelete_HookIsCreated", func(t *testing.T) { + app := newFakeApp() + app.SetPostDeleteFinalizer() + app.Spec.Destination.Namespace = test.FakeArgoCDNamespace + ctrl := newFakeController(&fakeData{ + manifestResponses: []*apiclient.ManifestResponse{{ + Manifests: []string{fakePostDeleteHook}, + }}, + apps: []runtime.Object{app, &defaultProj}, + managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{}}, nil) + + patched := false + fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) + 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) + }) + fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { + patched = true + return true, &v1alpha1.Application{}, nil + }) + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + return []*v1alpha1.Cluster{}, nil + }) + assert.NoError(t, err) + // finalizer is not deleted + assert.False(t, patched) + // post-delete hook is created + require.Len(t, ctrl.kubectl.(*MockKubectl).CreatedResources, 1) + require.Equal(t, "post-delete-hook", ctrl.kubectl.(*MockKubectl).CreatedResources[0].GetName()) + }) + + t.Run("PostDelete_HookIsExecuted", func(t *testing.T) { + app := newFakeApp() + app.SetPostDeleteFinalizer() + app.Spec.Destination.Namespace = test.FakeArgoCDNamespace + liveHook := &unstructured.Unstructured{Object: newFakePostDeleteHook()} + require.NoError(t, unstructured.SetNestedField(liveHook.Object, "Succeeded", "status", "phase")) + ctrl := newFakeController(&fakeData{ + manifestResponses: []*apiclient.ManifestResponse{{ + Manifests: []string{fakePostDeleteHook}, + }}, + apps: []runtime.Object{app, &defaultProj}, + managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ + kube.GetResourceKey(liveHook): liveHook, + }}, nil) + + patched := false + fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) + 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) + }) + fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { + patched = true + return true, &v1alpha1.Application{}, nil + }) + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + return []*v1alpha1.Cluster{}, nil + }) + assert.NoError(t, err) + // finalizer is removed + assert.True(t, patched) + }) + + t.Run("PostDelete_HookIsDeleted", func(t *testing.T) { + app := newFakeApp() + app.SetPostDeleteFinalizer("cleanup") + app.Spec.Destination.Namespace = test.FakeArgoCDNamespace + liveHook := &unstructured.Unstructured{Object: newFakePostDeleteHook()} + require.NoError(t, unstructured.SetNestedField(liveHook.Object, "Succeeded", "status", "phase")) + ctrl := newFakeController(&fakeData{ + manifestResponses: []*apiclient.ManifestResponse{{ + Manifests: []string{fakePostDeleteHook}, + }}, + apps: []runtime.Object{app, &defaultProj}, + managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ + kube.GetResourceKey(liveHook): liveHook, + }}, nil) + + patched := false + fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) + 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) + }) + fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { + patched = true + return true, &v1alpha1.Application{}, nil + }) + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + return []*v1alpha1.Cluster{}, nil + }) + assert.NoError(t, err) + // post-delete hook is deleted + require.Len(t, ctrl.kubectl.(*MockKubectl).DeletedResources, 1) + require.Equal(t, "post-delete-hook", ctrl.kubectl.(*MockKubectl).DeletedResources[0].Name) + // finalizer is not removed + assert.False(t, patched) + }) } // TestNormalizeApplication verifies we normalize an application during reconciliation func TestNormalizeApplication(t *testing.T) { - defaultProj := argoappv1.AppProject{ + defaultProj := v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: test.FakeArgoCDNamespace, }, - Spec: argoappv1.AppProjectSpec{ + Spec: v1alpha1.AppProjectSpec{ SourceRepos: []string{"*"}, - Destinations: []argoappv1.ApplicationDestination{ + Destinations: []v1alpha1.ApplicationDestination{ { Server: "*", Namespace: "*", @@ -705,7 +963,7 @@ func TestNormalizeApplication(t *testing.T) { } app := newFakeApp() app.Spec.Project = "" - app.Spec.Source.Kustomize = &argoappv1.ApplicationSourceKustomize{NamePrefix: "foo-"} + app.Spec.Source.Kustomize = &v1alpha1.ApplicationSourceKustomize{NamePrefix: "foo-"} data := fakeData{ apps: []runtime.Object{app, &defaultProj}, manifestResponse: &apiclient.ManifestResponse{ @@ -719,9 +977,9 @@ func TestNormalizeApplication(t *testing.T) { { // Verify we normalize the app because project is missing - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) key, _ := cache.MetaNamespaceKeyFunc(app) - ctrl.appRefreshQueue.Add(key) + ctrl.appRefreshQueue.AddRateLimited(key) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) fakeAppCs.ReactionChain = nil normalized := false @@ -731,7 +989,7 @@ func TestNormalizeApplication(t *testing.T) { normalized = true } } - return true, nil, nil + return true, &v1alpha1.Application{}, nil }) ctrl.processAppRefreshQueueItem() assert.True(t, normalized) @@ -741,9 +999,9 @@ func TestNormalizeApplication(t *testing.T) { // Verify we don't unnecessarily normalize app when project is set app.Spec.Project = "default" data.apps[0] = app - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) key, _ := cache.MetaNamespaceKeyFunc(app) - ctrl.appRefreshQueue.Add(key) + ctrl.appRefreshQueue.AddRateLimited(key) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) fakeAppCs.ReactionChain = nil normalized := false @@ -753,7 +1011,7 @@ func TestNormalizeApplication(t *testing.T) { normalized = true } } - return true, nil, nil + return true, &v1alpha1.Application{}, nil }) ctrl.processAppRefreshQueueItem() assert.False(t, normalized) @@ -763,10 +1021,10 @@ func TestNormalizeApplication(t *testing.T) { func TestHandleAppUpdated(t *testing.T) { app := newFakeApp() app.Spec.Destination.Namespace = test.FakeArgoCDNamespace - app.Spec.Destination.Server = argoappv1.KubernetesInternalAPIServerAddr + app.Spec.Destination.Server = v1alpha1.KubernetesInternalAPIServerAddr proj := defaultProj.DeepCopy() proj.Spec.SourceNamespaces = []string{test.FakeArgoCDNamespace} - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, proj}}) + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, proj}}, nil) ctrl.handleObjectUpdated(map[string]bool{app.InstanceName(ctrl.namespace): true}, kube.GetObjectRef(kube.MustToUnstructured(app))) isRequested, level := ctrl.isRefreshRequested(app.QualifiedName()) @@ -783,17 +1041,17 @@ func TestHandleOrphanedResourceUpdated(t *testing.T) { app1 := newFakeApp() app1.Name = "app1" app1.Spec.Destination.Namespace = test.FakeArgoCDNamespace - app1.Spec.Destination.Server = argoappv1.KubernetesInternalAPIServerAddr + app1.Spec.Destination.Server = v1alpha1.KubernetesInternalAPIServerAddr app2 := newFakeApp() app2.Name = "app2" app2.Spec.Destination.Namespace = test.FakeArgoCDNamespace - app2.Spec.Destination.Server = argoappv1.KubernetesInternalAPIServerAddr + app2.Spec.Destination.Server = v1alpha1.KubernetesInternalAPIServerAddr proj := defaultProj.DeepCopy() - proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{} + proj.Spec.OrphanedResources = &v1alpha1.OrphanedResourcesMonitorSettings{} - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app1, app2, proj}}) + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app1, app2, proj}}, nil) ctrl.handleObjectUpdated(map[string]bool{}, corev1.ObjectReference{UID: "test", Kind: kube.DeploymentKind, Name: "test", Namespace: test.FakeArgoCDNamespace}) @@ -809,16 +1067,16 @@ func TestHandleOrphanedResourceUpdated(t *testing.T) { func TestGetResourceTree_HasOrphanedResources(t *testing.T) { app := newFakeApp() proj := defaultProj.DeepCopy() - proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{} + proj.Spec.OrphanedResources = &v1alpha1.OrphanedResourcesMonitorSettings{} - managedDeploy := argoappv1.ResourceNode{ - ResourceRef: argoappv1.ResourceRef{Group: "apps", Kind: "Deployment", Namespace: "default", Name: "nginx-deployment", Version: "v1"}, + managedDeploy := v1alpha1.ResourceNode{ + ResourceRef: v1alpha1.ResourceRef{Group: "apps", Kind: "Deployment", Namespace: "default", Name: "nginx-deployment", Version: "v1"}, } - orphanedDeploy1 := argoappv1.ResourceNode{ - ResourceRef: argoappv1.ResourceRef{Group: "apps", Kind: "Deployment", Namespace: "default", Name: "deploy1"}, + orphanedDeploy1 := v1alpha1.ResourceNode{ + ResourceRef: v1alpha1.ResourceRef{Group: "apps", Kind: "Deployment", Namespace: "default", Name: "deploy1"}, } - orphanedDeploy2 := argoappv1.ResourceNode{ - ResourceRef: argoappv1.ResourceRef{Group: "apps", Kind: "Deployment", Namespace: "default", Name: "deploy2"}, + orphanedDeploy2 := v1alpha1.ResourceNode{ + ResourceRef: v1alpha1.ResourceRef{Group: "apps", Kind: "Deployment", Namespace: "default", Name: "deploy2"}, } ctrl := newFakeController(&fakeData{ @@ -828,8 +1086,8 @@ func TestGetResourceTree_HasOrphanedResources(t *testing.T) { kube.NewResourceKey("apps", "Deployment", "default", "deploy1"): {ResourceNode: orphanedDeploy1}, kube.NewResourceKey("apps", "Deployment", "default", "deploy2"): {ResourceNode: orphanedDeploy2}, }, - }) - tree, err := ctrl.getResourceTree(app, []*argoappv1.ResourceDiff{{ + }, nil) + tree, err := ctrl.getResourceTree(app, []*v1alpha1.ResourceDiff{{ Namespace: "default", Name: "nginx-deployment", Kind: "Deployment", @@ -839,131 +1097,300 @@ func TestGetResourceTree_HasOrphanedResources(t *testing.T) { }}) assert.NoError(t, err) - assert.Equal(t, tree.Nodes, []argoappv1.ResourceNode{managedDeploy}) - assert.Equal(t, tree.OrphanedNodes, []argoappv1.ResourceNode{orphanedDeploy1, orphanedDeploy2}) + assert.Equal(t, tree.Nodes, []v1alpha1.ResourceNode{managedDeploy}) + assert.Equal(t, tree.OrphanedNodes, []v1alpha1.ResourceNode{orphanedDeploy1, orphanedDeploy2}) } func TestSetOperationStateOnDeletedApp(t *testing.T) { - ctrl := newFakeController(&fakeData{apps: []runtime.Object{}}) + ctrl := newFakeController(&fakeData{apps: []runtime.Object{}}, nil) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) fakeAppCs.ReactionChain = nil patched := false fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { patched = true - return true, nil, apierr.NewNotFound(schema.GroupResource{}, "my-app") + return true, &v1alpha1.Application{}, apierr.NewNotFound(schema.GroupResource{}, "my-app") }) - ctrl.setOperationState(newFakeApp(), &argoappv1.OperationState{Phase: synccommon.OperationSucceeded}) + ctrl.setOperationState(newFakeApp(), &v1alpha1.OperationState{Phase: synccommon.OperationSucceeded}) assert.True(t, patched) } -func TestNeedRefreshAppStatus(t *testing.T) { - ctrl := newFakeController(&fakeData{apps: []runtime.Object{}}) +type logHook struct { + entries []logrus.Entry +} - app := newFakeApp() - now := metav1.Now() - app.Status.ReconciledAt = &now - app.Status.Sync = argoappv1.SyncStatus{ - Status: argoappv1.SyncStatusCodeSynced, - ComparedTo: argoappv1.ComparedTo{ - Source: app.Spec.GetSource(), - Destination: app.Spec.Destination, +func (h *logHook) Levels() []logrus.Level { + return []logrus.Level{logrus.WarnLevel} +} + +func (h *logHook) Fire(entry *logrus.Entry) error { + h.entries = append(h.entries, *entry) + return nil +} + +func TestSetOperationStateLogRetries(t *testing.T) { + hook := logHook{} + logrus.AddHook(&hook) + t.Cleanup(func() { + logrus.StandardLogger().ReplaceHooks(logrus.LevelHooks{}) + }) + ctrl := newFakeController(&fakeData{apps: []runtime.Object{}}, nil) + fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) + fakeAppCs.ReactionChain = nil + patched := false + fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { + if !patched { + patched = true + return true, &v1alpha1.Application{}, errors.New("fake error") + } + return true, &v1alpha1.Application{}, nil + }) + ctrl.setOperationState(newFakeApp(), &v1alpha1.OperationState{Phase: synccommon.OperationSucceeded}) + assert.True(t, patched) + assert.Contains(t, hook.entries[0].Message, "fake error") +} + +func TestNeedRefreshAppStatus(t *testing.T) { + testCases := []struct { + name string + app *v1alpha1.Application + }{ + { + name: "single-source app", + app: newFakeApp(), + }, + { + name: "multi-source app", + app: newFakeMultiSourceApp(), }, } - // no need to refresh just reconciled application - needRefresh, _, _ := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) - assert.False(t, needRefresh) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + app := tc.app + now := metav1.Now() + app.Status.ReconciledAt = &now + + app.Status.Sync = v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, + ComparedTo: v1alpha1.ComparedTo{ + Destination: app.Spec.Destination, + IgnoreDifferences: app.Spec.IgnoreDifferences, + }, + } - // refresh app using the 'deepest' requested comparison level - ctrl.requestAppRefresh(app.Name, CompareWithRecent.Pointer(), nil) - ctrl.requestAppRefresh(app.Name, ComparisonWithNothing.Pointer(), nil) + if app.Spec.HasMultipleSources() { + app.Status.Sync.ComparedTo.Sources = app.Spec.Sources + } else { + app.Status.Sync.ComparedTo.Source = app.Spec.GetSource() + } - needRefresh, refreshType, compareWith := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) - assert.True(t, needRefresh) - assert.Equal(t, argoappv1.RefreshTypeNormal, refreshType) - assert.Equal(t, CompareWithRecent, compareWith) + ctrl := newFakeController(&fakeData{apps: []runtime.Object{}}, nil) - // refresh application which status is not reconciled using latest commit - app.Status.Sync = argoappv1.SyncStatus{Status: argoappv1.SyncStatusCodeUnknown} + t.Run("no need to refresh just reconciled application", func(t *testing.T) { + needRefresh, _, _ := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.False(t, needRefresh) + }) - needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) - assert.True(t, needRefresh) - assert.Equal(t, argoappv1.RefreshTypeNormal, refreshType) - assert.Equal(t, CompareWithLatestForceResolve, compareWith) + t.Run("requested refresh is respected", func(t *testing.T) { + needRefresh, _, _ := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.False(t, needRefresh) - { - // refresh app using the 'latest' level if comparison expired - app := app.DeepCopy() - ctrl.requestAppRefresh(app.Name, CompareWithRecent.Pointer(), nil) - reconciledAt := metav1.NewTime(time.Now().UTC().Add(-1 * time.Hour)) - app.Status.ReconciledAt = &reconciledAt - needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Minute, 2*time.Hour) - assert.True(t, needRefresh) - assert.Equal(t, argoappv1.RefreshTypeNormal, refreshType) - assert.Equal(t, CompareWithLatestForceResolve, compareWith) - } + // use a one-off controller so other tests don't have a manual refresh request + ctrl := newFakeController(&fakeData{apps: []runtime.Object{}}, nil) - { - // refresh app using the 'latest' level if comparison expired for hard refresh - app := app.DeepCopy() - app.Status.Sync = argoappv1.SyncStatus{ - Status: argoappv1.SyncStatusCodeSynced, - ComparedTo: argoappv1.ComparedTo{ - Source: app.Spec.GetSource(), - Destination: app.Spec.Destination, - }, - } - ctrl.requestAppRefresh(app.Name, CompareWithRecent.Pointer(), nil) - reconciledAt := metav1.NewTime(time.Now().UTC().Add(-1 * time.Hour)) - app.Status.ReconciledAt = &reconciledAt - needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 2*time.Hour, 1*time.Minute) - assert.True(t, needRefresh) - assert.Equal(t, argoappv1.RefreshTypeHard, refreshType) - assert.Equal(t, CompareWithLatest, compareWith) + // refresh app using the 'deepest' requested comparison level + ctrl.requestAppRefresh(app.Name, CompareWithRecent.Pointer(), nil) + ctrl.requestAppRefresh(app.Name, ComparisonWithNothing.Pointer(), nil) + + needRefresh, refreshType, compareWith := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.True(t, needRefresh) + assert.Equal(t, v1alpha1.RefreshTypeNormal, refreshType) + assert.Equal(t, CompareWithRecent, compareWith) + }) + + t.Run("refresh application which status is not reconciled using latest commit", func(t *testing.T) { + app := app.DeepCopy() + needRefresh, _, _ := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.False(t, needRefresh) + app.Status.Sync = v1alpha1.SyncStatus{Status: v1alpha1.SyncStatusCodeUnknown} + + needRefresh, refreshType, compareWith := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.True(t, needRefresh) + assert.Equal(t, v1alpha1.RefreshTypeNormal, refreshType) + assert.Equal(t, CompareWithLatestForceResolve, compareWith) + }) + + t.Run("refresh app using the 'latest' level if comparison expired", func(t *testing.T) { + app := app.DeepCopy() + + // use a one-off controller so other tests don't have a manual refresh request + ctrl := newFakeController(&fakeData{apps: []runtime.Object{}}, nil) + + needRefresh, _, _ := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.False(t, needRefresh) + + ctrl.requestAppRefresh(app.Name, CompareWithRecent.Pointer(), nil) + reconciledAt := metav1.NewTime(time.Now().UTC().Add(-1 * time.Hour)) + app.Status.ReconciledAt = &reconciledAt + needRefresh, refreshType, compareWith := ctrl.needRefreshAppStatus(app, 1*time.Minute, 2*time.Hour) + assert.True(t, needRefresh) + assert.Equal(t, v1alpha1.RefreshTypeNormal, refreshType) + assert.Equal(t, CompareWithLatest, compareWith) + }) + + t.Run("refresh app using the 'latest' level if comparison expired for hard refresh", func(t *testing.T) { + app := app.DeepCopy() + app.Status.Sync = v1alpha1.SyncStatus{ + Status: v1alpha1.SyncStatusCodeSynced, + ComparedTo: v1alpha1.ComparedTo{ + Destination: app.Spec.Destination, + IgnoreDifferences: app.Spec.IgnoreDifferences, + }, + } + if app.Spec.HasMultipleSources() { + app.Status.Sync.ComparedTo.Sources = app.Spec.Sources + } else { + app.Status.Sync.ComparedTo.Source = app.Spec.GetSource() + } + + // use a one-off controller so other tests don't have a manual refresh request + ctrl := newFakeController(&fakeData{apps: []runtime.Object{}}, nil) + + needRefresh, _, _ := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.False(t, needRefresh) + ctrl.requestAppRefresh(app.Name, CompareWithRecent.Pointer(), nil) + reconciledAt := metav1.NewTime(time.Now().UTC().Add(-1 * time.Hour)) + app.Status.ReconciledAt = &reconciledAt + needRefresh, refreshType, compareWith := ctrl.needRefreshAppStatus(app, 2*time.Hour, 1*time.Minute) + assert.True(t, needRefresh) + assert.Equal(t, v1alpha1.RefreshTypeHard, refreshType) + assert.Equal(t, CompareWithLatest, compareWith) + }) + + t.Run("execute hard refresh if app has refresh annotation", func(t *testing.T) { + app := app.DeepCopy() + needRefresh, _, _ := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.False(t, needRefresh) + reconciledAt := metav1.NewTime(time.Now().UTC().Add(-1 * time.Hour)) + app.Status.ReconciledAt = &reconciledAt + app.Annotations = map[string]string{ + v1alpha1.AnnotationKeyRefresh: string(v1alpha1.RefreshTypeHard), + } + needRefresh, refreshType, compareWith := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.True(t, needRefresh) + assert.Equal(t, v1alpha1.RefreshTypeHard, refreshType) + assert.Equal(t, CompareWithLatestForceResolve, compareWith) + }) + + t.Run("ensure that CompareWithLatest level is used if application source has changed", func(t *testing.T) { + app := app.DeepCopy() + needRefresh, _, _ := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.False(t, needRefresh) + // sample app source change + if app.Spec.HasMultipleSources() { + app.Spec.Sources[0].Helm = &v1alpha1.ApplicationSourceHelm{ + Parameters: []v1alpha1.HelmParameter{{ + Name: "foo", + Value: "bar", + }}, + } + } else { + app.Spec.Source.Helm = &v1alpha1.ApplicationSourceHelm{ + Parameters: []v1alpha1.HelmParameter{{ + Name: "foo", + Value: "bar", + }}, + } + } + + needRefresh, refreshType, compareWith := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.True(t, needRefresh) + assert.Equal(t, v1alpha1.RefreshTypeNormal, refreshType) + assert.Equal(t, CompareWithLatestForceResolve, compareWith) + }) + + t.Run("ensure that CompareWithLatest level is used if ignored differences change", func(t *testing.T) { + app := app.DeepCopy() + needRefresh, _, _ := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.False(t, needRefresh) + + app.Spec.IgnoreDifferences = []v1alpha1.ResourceIgnoreDifferences{ + { + Group: "apps", + Kind: "Deployment", + JSONPointers: []string{ + "/spec/template/spec/containers/0/image", + }, + }, + } + + needRefresh, refreshType, compareWith := ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) + assert.True(t, needRefresh) + assert.Equal(t, v1alpha1.RefreshTypeNormal, refreshType) + assert.Equal(t, CompareWithLatest, compareWith) + }) + }) } +} - { - app := app.DeepCopy() - // execute hard refresh if app has refresh annotation - reconciledAt := metav1.NewTime(time.Now().UTC().Add(-1 * time.Hour)) - app.Status.ReconciledAt = &reconciledAt - app.Annotations = map[string]string{ - v1alpha1.AnnotationKeyRefresh: string(argoappv1.RefreshTypeHard), - } - needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) - assert.True(t, needRefresh) - assert.Equal(t, argoappv1.RefreshTypeHard, refreshType) - assert.Equal(t, CompareWithLatestForceResolve, compareWith) +func TestUpdatedManagedNamespaceMetadata(t *testing.T) { + ctrl := newFakeController(&fakeData{apps: []runtime.Object{}}, nil) + app := newFakeApp() + app.Spec.SyncPolicy.ManagedNamespaceMetadata = &v1alpha1.ManagedNamespaceMetadata{ + Labels: map[string]string{ + "foo": "bar", + }, + Annotations: map[string]string{ + "foo": "bar", + }, } + app.Status.Sync.ComparedTo.Source = app.Spec.GetSource() + app.Status.Sync.ComparedTo.Destination = app.Spec.Destination - { - app := app.DeepCopy() - // ensure that CompareWithLatest level is used if application source has changed - ctrl.requestAppRefresh(app.Name, ComparisonWithNothing.Pointer(), nil) - // sample app source change - app.Spec.Source.Helm = &argoappv1.ApplicationSourceHelm{ - Parameters: []argoappv1.HelmParameter{{ - Name: "foo", - Value: "bar", - }}, - } + // Ensure that hard/soft refresh isn't triggered due to reconciledAt being expired + reconciledAt := metav1.NewTime(time.Now().UTC().Add(15 * time.Minute)) + app.Status.ReconciledAt = &reconciledAt + needRefresh, refreshType, compareWith := ctrl.needRefreshAppStatus(app, 30*time.Minute, 2*time.Hour) - needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Hour, 2*time.Hour) - assert.True(t, needRefresh) - assert.Equal(t, argoappv1.RefreshTypeNormal, refreshType) - assert.Equal(t, CompareWithLatestForceResolve, compareWith) + assert.True(t, needRefresh) + assert.Equal(t, v1alpha1.RefreshTypeNormal, refreshType) + assert.Equal(t, CompareWithLatest, compareWith) +} + +func TestUnchangedManagedNamespaceMetadata(t *testing.T) { + ctrl := newFakeController(&fakeData{apps: []runtime.Object{}}, nil) + app := newFakeApp() + app.Spec.SyncPolicy.ManagedNamespaceMetadata = &v1alpha1.ManagedNamespaceMetadata{ + Labels: map[string]string{ + "foo": "bar", + }, + Annotations: map[string]string{ + "foo": "bar", + }, } + app.Status.Sync.ComparedTo.Source = app.Spec.GetSource() + app.Status.Sync.ComparedTo.Destination = app.Spec.Destination + app.Status.OperationState.SyncResult.ManagedNamespaceMetadata = app.Spec.SyncPolicy.ManagedNamespaceMetadata + + // Ensure that hard/soft refresh isn't triggered due to reconciledAt being expired + reconciledAt := metav1.NewTime(time.Now().UTC().Add(15 * time.Minute)) + app.Status.ReconciledAt = &reconciledAt + needRefresh, refreshType, compareWith := ctrl.needRefreshAppStatus(app, 30*time.Minute, 2*time.Hour) + + assert.False(t, needRefresh) + assert.Equal(t, v1alpha1.RefreshTypeNormal, refreshType) + assert.Equal(t, CompareWithLatest, compareWith) } func TestRefreshAppConditions(t *testing.T) { - defaultProj := argoappv1.AppProject{ + defaultProj := v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: test.FakeArgoCDNamespace, }, - Spec: argoappv1.AppProjectSpec{ + Spec: v1alpha1.AppProjectSpec{ SourceRepos: []string{"*"}, - Destinations: []argoappv1.ApplicationDestination{ + Destinations: []v1alpha1.ApplicationDestination{ { Server: "*", Namespace: "*", @@ -974,7 +1401,7 @@ func TestRefreshAppConditions(t *testing.T) { t.Run("NoErrorConditions", func(t *testing.T) { app := newFakeApp() - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}}) + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}}, nil) _, hasErrors := ctrl.refreshAppConditions(app) assert.False(t, hasErrors) @@ -983,27 +1410,27 @@ func TestRefreshAppConditions(t *testing.T) { t.Run("PreserveExistingWarningCondition", func(t *testing.T) { app := newFakeApp() - app.Status.SetConditions([]argoappv1.ApplicationCondition{{Type: argoappv1.ApplicationConditionExcludedResourceWarning}}, nil) + app.Status.SetConditions([]v1alpha1.ApplicationCondition{{Type: v1alpha1.ApplicationConditionExcludedResourceWarning}}, nil) - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}}) + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}}, nil) _, hasErrors := ctrl.refreshAppConditions(app) assert.False(t, hasErrors) assert.Len(t, app.Status.Conditions, 1) - assert.Equal(t, argoappv1.ApplicationConditionExcludedResourceWarning, app.Status.Conditions[0].Type) + assert.Equal(t, v1alpha1.ApplicationConditionExcludedResourceWarning, app.Status.Conditions[0].Type) }) t.Run("ReplacesSpecErrorCondition", func(t *testing.T) { app := newFakeApp() app.Spec.Project = "wrong project" - app.Status.SetConditions([]argoappv1.ApplicationCondition{{Type: argoappv1.ApplicationConditionInvalidSpecError, Message: "old message"}}, nil) + app.Status.SetConditions([]v1alpha1.ApplicationCondition{{Type: v1alpha1.ApplicationConditionInvalidSpecError, Message: "old message"}}, nil) - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}}) + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}}, nil) _, 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, v1alpha1.ApplicationConditionInvalidSpecError, app.Status.Conditions[0].Type) assert.Equal(t, "Application referencing project wrong project which does not exist", app.Status.Conditions[0].Message) }) } @@ -1011,8 +1438,8 @@ func TestRefreshAppConditions(t *testing.T) { func TestUpdateReconciledAt(t *testing.T) { app := newFakeApp() reconciledAt := metav1.NewTime(time.Now().Add(-1 * time.Second)) - app.Status = argoappv1.ApplicationStatus{ReconciledAt: &reconciledAt} - app.Status.Sync = argoappv1.SyncStatus{ComparedTo: argoappv1.ComparedTo{Source: app.Spec.GetSource(), Destination: app.Spec.Destination}} + app.Status = v1alpha1.ApplicationStatus{ReconciledAt: &reconciledAt} + app.Status.Sync = v1alpha1.SyncStatus{ComparedTo: v1alpha1.ComparedTo{Source: app.Spec.GetSource(), Destination: app.Spec.Destination, IgnoreDifferences: app.Spec.IgnoreDifferences}} ctrl := newFakeController(&fakeData{ apps: []runtime.Object{app, &defaultProj}, manifestResponse: &apiclient.ManifestResponse{ @@ -1022,7 +1449,7 @@ func TestUpdateReconciledAt(t *testing.T) { Revision: "abc123", }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), - }) + }, nil) key, _ := cache.MetaNamespaceKeyFunc(app) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) fakeAppCs.ReactionChain = nil @@ -1031,13 +1458,13 @@ func TestUpdateReconciledAt(t *testing.T) { if patchAction, ok := action.(kubetesting.PatchAction); ok { assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) } - return true, nil, nil + return true, &v1alpha1.Application{}, nil }) t.Run("UpdatedOnFullReconciliation", func(t *testing.T) { receivedPatch = map[string]interface{}{} ctrl.requestAppRefresh(app.Name, CompareWithLatest.Pointer(), nil) - ctrl.appRefreshQueue.Add(key) + ctrl.appRefreshQueue.AddRateLimited(key) ctrl.processAppRefreshQueueItem() @@ -1052,7 +1479,7 @@ func TestUpdateReconciledAt(t *testing.T) { t.Run("NotUpdatedOnPartialReconciliation", func(t *testing.T) { receivedPatch = map[string]interface{}{} - ctrl.appRefreshQueue.Add(key) + ctrl.appRefreshQueue.AddRateLimited(key) ctrl.requestAppRefresh(app.Name, CompareWithRecent.Pointer(), nil) ctrl.processAppRefreshQueueItem() @@ -1080,9 +1507,9 @@ func TestProjectErrorToCondition(t *testing.T) { Revision: "abc123", }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), - }) + }, nil) key, _ := cache.MetaNamespaceKeyFunc(app) - ctrl.appRefreshQueue.Add(key) + ctrl.appRefreshQueue.AddRateLimited(key) ctrl.requestAppRefresh(app.Name, CompareWithRecent.Pointer(), nil) ctrl.processAppRefreshQueueItem() @@ -1090,22 +1517,22 @@ func TestProjectErrorToCondition(t *testing.T) { obj, ok, err := ctrl.appInformer.GetIndexer().GetByKey(key) assert.True(t, ok) assert.NoError(t, err) - updatedApp := obj.(*argoappv1.Application) - assert.Equal(t, argoappv1.ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[0].Type) + updatedApp := obj.(*v1alpha1.Application) + assert.Equal(t, v1alpha1.ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[0].Type) assert.Equal(t, "Application referencing project wrong project which does not exist", updatedApp.Status.Conditions[0].Message) - assert.Equal(t, argoappv1.ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[0].Type) + assert.Equal(t, v1alpha1.ApplicationConditionInvalidSpecError, updatedApp.Status.Conditions[0].Type) } func TestFinalizeProjectDeletion_HasApplications(t *testing.T) { app := newFakeApp() - proj := &argoappv1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: test.FakeArgoCDNamespace}} - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, proj}}) + proj := &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: test.FakeArgoCDNamespace}} + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, proj}}, nil) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) patched := false fakeAppCs.PrependReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { patched = true - return true, nil, nil + return true, &v1alpha1.Application{}, nil }) err := ctrl.finalizeProjectDeletion(proj) @@ -1114,8 +1541,8 @@ func TestFinalizeProjectDeletion_HasApplications(t *testing.T) { } func TestFinalizeProjectDeletion_DoesNotHaveApplications(t *testing.T) { - proj := &argoappv1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: test.FakeArgoCDNamespace}} - ctrl := newFakeController(&fakeData{apps: []runtime.Object{&defaultProj}}) + proj := &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: test.FakeArgoCDNamespace}} + ctrl := newFakeController(&fakeData{apps: []runtime.Object{&defaultProj}}, nil) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) receivedPatch := map[string]interface{}{} @@ -1123,7 +1550,7 @@ func TestFinalizeProjectDeletion_DoesNotHaveApplications(t *testing.T) { if patchAction, ok := action.(kubetesting.PatchAction); ok { assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) } - return true, nil, nil + return true, &v1alpha1.AppProject{}, nil }) err := ctrl.finalizeProjectDeletion(proj) @@ -1138,17 +1565,17 @@ func TestFinalizeProjectDeletion_DoesNotHaveApplications(t *testing.T) { func TestProcessRequestedAppOperation_FailedNoRetries(t *testing.T) { app := newFakeApp() app.Spec.Project = "default" - app.Operation = &argoappv1.Operation{ - Sync: &argoappv1.SyncOperation{}, + app.Operation = &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{}, } - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) receivedPatch := map[string]interface{}{} 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 + return true, &v1alpha1.Application{}, nil }) ctrl.processRequestedAppOperation(app) @@ -1160,13 +1587,13 @@ func TestProcessRequestedAppOperation_FailedNoRetries(t *testing.T) { func TestProcessRequestedAppOperation_InvalidDestination(t *testing.T) { app := newFakeAppWithDestMismatch() app.Spec.Project = "test-project" - app.Operation = &argoappv1.Operation{ - Sync: &argoappv1.SyncOperation{}, + app.Operation = &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{}, } proj := defaultProj proj.Name = "test-project" proj.Spec.SourceNamespaces = []string{test.FakeArgoCDNamespace} - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &proj}}) + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &proj}}, nil) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) receivedPatch := map[string]interface{}{} func() { @@ -1176,7 +1603,7 @@ func TestProcessRequestedAppOperation_InvalidDestination(t *testing.T) { if patchAction, ok := action.(kubetesting.PatchAction); ok { assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) } - return true, nil, nil + return true, &v1alpha1.Application{}, nil }) }() @@ -1191,18 +1618,18 @@ func TestProcessRequestedAppOperation_InvalidDestination(t *testing.T) { func TestProcessRequestedAppOperation_FailedHasRetries(t *testing.T) { app := newFakeApp() app.Spec.Project = "invalid-project" - app.Operation = &argoappv1.Operation{ - Sync: &argoappv1.SyncOperation{}, - Retry: argoappv1.RetryStrategy{Limit: 1}, + app.Operation = &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{}, + Retry: v1alpha1.RetryStrategy{Limit: 1}, } - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) receivedPatch := map[string]interface{}{} 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 + return true, &v1alpha1.Application{}, nil }) ctrl.processRequestedAppOperation(app) @@ -1217,12 +1644,12 @@ func TestProcessRequestedAppOperation_FailedHasRetries(t *testing.T) { func TestProcessRequestedAppOperation_RunningPreviouslyFailed(t *testing.T) { app := newFakeApp() - app.Operation = &argoappv1.Operation{ - Sync: &argoappv1.SyncOperation{}, - Retry: argoappv1.RetryStrategy{Limit: 1}, + app.Operation = &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{}, + Retry: v1alpha1.RetryStrategy{Limit: 1}, } app.Status.OperationState.Phase = synccommon.OperationRunning - app.Status.OperationState.SyncResult.Resources = []*argoappv1.ResourceResult{{ + app.Status.OperationState.SyncResult.Resources = []*v1alpha1.ResourceResult{{ Name: "guestbook", Kind: "Deployment", Group: "apps", @@ -1238,14 +1665,14 @@ func TestProcessRequestedAppOperation_RunningPreviouslyFailed(t *testing.T) { Revision: "abc123", }, } - ctrl := newFakeController(data) + ctrl := newFakeController(data, nil) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) receivedPatch := map[string]interface{}{} 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 + return true, &v1alpha1.Application{}, nil }) ctrl.processRequestedAppOperation(app) @@ -1256,9 +1683,9 @@ func TestProcessRequestedAppOperation_RunningPreviouslyFailed(t *testing.T) { func TestProcessRequestedAppOperation_HasRetriesTerminated(t *testing.T) { app := newFakeApp() - app.Operation = &argoappv1.Operation{ - Sync: &argoappv1.SyncOperation{}, - Retry: argoappv1.RetryStrategy{Limit: 10}, + app.Operation = &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{}, + Retry: v1alpha1.RetryStrategy{Limit: 10}, } app.Status.OperationState.Phase = synccommon.OperationTerminating @@ -1271,14 +1698,14 @@ func TestProcessRequestedAppOperation_HasRetriesTerminated(t *testing.T) { Revision: "abc123", }, } - ctrl := newFakeController(data) + ctrl := newFakeController(data, nil) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) receivedPatch := map[string]interface{}{} 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 + return true, &v1alpha1.Application{}, nil }) ctrl.processRequestedAppOperation(app) @@ -1298,7 +1725,7 @@ func TestGetAppHosts(t *testing.T) { Revision: "abc123", }, } - ctrl := newFakeController(data) + ctrl := newFakeController(data, nil) mockStateCache := &mockstatecache.LiveStateCache{} mockStateCache.On("IterateResources", mock.Anything, mock.MatchedBy(func(callback func(res *clustercache.Resource, info *statecache.ResourceInfo)) bool { // node resource @@ -1328,19 +1755,19 @@ func TestGetAppHosts(t *testing.T) { })).Return(nil) ctrl.stateCache = mockStateCache - hosts, err := ctrl.getAppHosts(app, []argoappv1.ResourceNode{{ - ResourceRef: argoappv1.ResourceRef{Name: "pod1", Namespace: "default", Kind: kube.PodKind}, - Info: []argoappv1.InfoItem{{ + hosts, err := ctrl.getAppHosts(app, []v1alpha1.ResourceNode{{ + ResourceRef: v1alpha1.ResourceRef{Name: "pod1", Namespace: "default", Kind: kube.PodKind}, + Info: []v1alpha1.InfoItem{{ Name: "Host", Value: "Minikube", }}, }}) assert.NoError(t, err) - assert.Equal(t, []argoappv1.HostInfo{{ + assert.Equal(t, []v1alpha1.HostInfo{{ Name: "minikube", SystemInfo: corev1.NodeSystemInfo{OSImage: "debian"}, - ResourcesInfo: []argoappv1.HostResourceInfo{{ + ResourcesInfo: []v1alpha1.HostResourceInfo{{ ResourceName: corev1.ResourceCPU, Capacity: 5000, RequestedByApp: 1000, RequestedByNeighbors: 2000}, }}}, hosts) } @@ -1348,15 +1775,15 @@ func TestGetAppHosts(t *testing.T) { func TestMetricsExpiration(t *testing.T) { app := newFakeApp() // Check expiration is disabled by default - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) assert.False(t, ctrl.metricsServer.HasExpiration()) // Check expiration is enabled if set - ctrl = newFakeController(&fakeData{apps: []runtime.Object{app}, metricsCacheExpiration: 10 * time.Second}) + ctrl = newFakeController(&fakeData{apps: []runtime.Object{app}, metricsCacheExpiration: 10 * time.Second}, nil) assert.True(t, ctrl.metricsServer.HasExpiration()) } func TestToAppKey(t *testing.T) { - ctrl := newFakeController(&fakeData{}) + ctrl := newFakeController(&fakeData{}, nil) tests := []struct { name string input string @@ -1373,3 +1800,113 @@ func TestToAppKey(t *testing.T) { }) } } + +func Test_canProcessApp(t *testing.T) { + app := newFakeApp() + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + ctrl.applicationNamespaces = []string{"good"} + t.Run("without cluster filter, good namespace", func(t *testing.T) { + app.Namespace = "good" + canProcess := ctrl.canProcessApp(app) + assert.True(t, canProcess) + }) + t.Run("without cluster filter, bad namespace", func(t *testing.T) { + app.Namespace = "bad" + canProcess := ctrl.canProcessApp(app) + assert.False(t, canProcess) + }) + t.Run("with cluster filter, good namespace", func(t *testing.T) { + app.Namespace = "good" + canProcess := ctrl.canProcessApp(app) + assert.True(t, canProcess) + }) + t.Run("with cluster filter, bad namespace", func(t *testing.T) { + app.Namespace = "bad" + canProcess := ctrl.canProcessApp(app) + assert.False(t, canProcess) + }) +} + +func Test_canProcessAppSkipReconcileAnnotation(t *testing.T) { + appSkipReconcileInvalid := newFakeApp() + appSkipReconcileInvalid.Annotations = map[string]string{common.AnnotationKeyAppSkipReconcile: "invalid-value"} + appSkipReconcileFalse := newFakeApp() + appSkipReconcileFalse.Annotations = map[string]string{common.AnnotationKeyAppSkipReconcile: "false"} + appSkipReconcileTrue := newFakeApp() + appSkipReconcileTrue.Annotations = map[string]string{common.AnnotationKeyAppSkipReconcile: "true"} + ctrl := newFakeController(&fakeData{}, nil) + tests := []struct { + name string + input interface{} + expected bool + }{ + {"No skip reconcile annotation", newFakeApp(), true}, + {"Contains skip reconcile annotation ", appSkipReconcileInvalid, true}, + {"Contains skip reconcile annotation value false", appSkipReconcileFalse, true}, + {"Contains skip reconcile annotation value true", appSkipReconcileTrue, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, ctrl.canProcessApp(tt.input)) + }) + } +} + +func Test_syncDeleteOption(t *testing.T) { + app := newFakeApp() + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) + cm := newFakeCM() + t.Run("without delete option object is deleted", func(t *testing.T) { + cmObj := kube.MustToUnstructured(&cm) + delete := ctrl.shouldBeDeleted(app, cmObj) + assert.True(t, delete) + }) + t.Run("with delete set to false object is retained", func(t *testing.T) { + cmObj := kube.MustToUnstructured(&cm) + cmObj.SetAnnotations(map[string]string{"argocd.argoproj.io/sync-options": "Delete=false"}) + delete := ctrl.shouldBeDeleted(app, cmObj) + assert.False(t, delete) + }) + t.Run("with delete set to false object is retained", func(t *testing.T) { + cmObj := kube.MustToUnstructured(&cm) + cmObj.SetAnnotations(map[string]string{"helm.sh/resource-policy": "keep"}) + delete := ctrl.shouldBeDeleted(app, cmObj) + assert.False(t, delete) + }) +} + +func TestAddControllerNamespace(t *testing.T) { + t.Run("set controllerNamespace when the app is in the controller namespace", func(t *testing.T) { + app := newFakeApp() + ctrl := newFakeController(&fakeData{ + apps: []runtime.Object{app, &defaultProj}, + manifestResponse: &apiclient.ManifestResponse{}, + }, nil) + + ctrl.processAppRefreshQueueItem() + + updatedApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).Get(context.Background(), app.Name, metav1.GetOptions{}) + assert.NoError(t, err) + assert.Equal(t, test.FakeArgoCDNamespace, updatedApp.Status.ControllerNamespace) + }) + t.Run("set controllerNamespace when the app is in another namespace than the controller", func(t *testing.T) { + appNamespace := "app-namespace" + + app := newFakeApp() + app.ObjectMeta.Namespace = appNamespace + proj := defaultProj + proj.Spec.SourceNamespaces = []string{appNamespace} + ctrl := newFakeController(&fakeData{ + apps: []runtime.Object{app, &proj}, + manifestResponse: &apiclient.ManifestResponse{}, + applicationNamespaces: []string{appNamespace}, + }, nil) + + ctrl.processAppRefreshQueueItem() + + updatedApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(appNamespace).Get(context.Background(), app.Name, metav1.GetOptions{}) + assert.NoError(t, err) + assert.Equal(t, test.FakeArgoCDNamespace, updatedApp.Status.ControllerNamespace) + }) +} diff --git a/controller/cache/cache.go b/controller/cache/cache.go index 23cde35a5bd19..826079d62cda3 100644 --- a/controller/cache/cache.go +++ b/controller/cache/cache.go @@ -25,9 +25,12 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/rest" "k8s.io/client-go/tools/cache" "github.com/argoproj/argo-cd/v2/controller/metrics" + "github.com/argoproj/argo-cd/v2/controller/sharding" + "github.com/argoproj/argo-cd/v2/pkg/apis/application" appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/db" @@ -44,18 +47,21 @@ const ( // EnvClusterCacheWatchResyncDuration is the env variable that holds cluster cache watch re-sync duration EnvClusterCacheWatchResyncDuration = "ARGOCD_CLUSTER_CACHE_WATCH_RESYNC_DURATION" - // EnvClusterRetryTimeoutDuration is the env variable that holds cluster retry duration when sync error happens + // EnvClusterSyncRetryTimeoutDuration is the env variable that holds cluster retry duration when sync error happens EnvClusterSyncRetryTimeoutDuration = "ARGOCD_CLUSTER_SYNC_RETRY_TIMEOUT_DURATION" // EnvClusterCacheListPageSize is the env variable to control size of the list page size when making K8s queries EnvClusterCacheListPageSize = "ARGOCD_CLUSTER_CACHE_LIST_PAGE_SIZE" + // EnvClusterCacheListPageBufferSize is the env variable to control the number of pages to buffer when making a K8s query to list resources + EnvClusterCacheListPageBufferSize = "ARGOCD_CLUSTER_CACHE_LIST_PAGE_BUFFER_SIZE" + // EnvClusterCacheListSemaphore is the env variable to control size of the list semaphore // This 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. EnvClusterCacheListSemaphore = "ARGOCD_CLUSTER_CACHE_LIST_SEMAPHORE" - // EnvClusterCacheRetryLimit is the env variable to control the retry limit for listing resources during cluster cache sync + // EnvClusterCacheAttemptLimit is the env variable to control the retry limit for listing resources during cluster cache sync EnvClusterCacheAttemptLimit = "ARGOCD_CLUSTER_CACHE_ATTEMPT_LIMIT" // EnvClusterCacheRetryUseBackoff is the env variable to control whether to use a backoff strategy with the retry during cluster cache sync @@ -82,6 +88,9 @@ var ( // 500 is equal to kubectl's size clusterCacheListPageSize int64 = 500 + // clusterCacheListPageBufferSize is the number of pages to buffer when performing K8s list requests + clusterCacheListPageBufferSize int32 = 1 + // clusterCacheRetryLimit sets a retry limit for failed requests during cluster cache sync // If set to 1, retries are disabled. clusterCacheAttemptLimit int32 = 1 @@ -95,8 +104,9 @@ func init() { clusterCacheWatchResyncDuration = env.ParseDurationFromEnv(EnvClusterCacheWatchResyncDuration, clusterCacheWatchResyncDuration, 0, math.MaxInt64) clusterSyncRetryTimeoutDuration = env.ParseDurationFromEnv(EnvClusterSyncRetryTimeoutDuration, clusterSyncRetryTimeoutDuration, 0, math.MaxInt64) clusterCacheListPageSize = env.ParseInt64FromEnv(EnvClusterCacheListPageSize, clusterCacheListPageSize, 0, math.MaxInt64) + clusterCacheListPageBufferSize = int32(env.ParseNumFromEnv(EnvClusterCacheListPageBufferSize, int(clusterCacheListPageBufferSize), 1, math.MaxInt32)) clusterCacheListSemaphoreSize = env.ParseInt64FromEnv(EnvClusterCacheListSemaphore, clusterCacheListSemaphoreSize, 0, math.MaxInt64) - clusterCacheAttemptLimit = int32(env.ParseInt64FromEnv(EnvClusterCacheAttemptLimit, 1, 1, math.MaxInt32)) + clusterCacheAttemptLimit = int32(env.ParseNumFromEnv(EnvClusterCacheAttemptLimit, int(clusterCacheAttemptLimit), 1, math.MaxInt32)) clusterCacheRetryUseBackoff = env.ParseBoolFromEnv(EnvClusterCacheRetryUseBackoff, false) } @@ -148,6 +158,8 @@ type ResourceInfo struct { PodInfo *PodInfo // NodeInfo is available for nodes only NodeInfo *NodeInfo + + manifestHash string } func NewLiveStateCache( @@ -157,7 +169,7 @@ func NewLiveStateCache( kubectl kube.Kubectl, metricsServer *metrics.MetricsServer, onObjectUpdated ObjectUpdatedHandler, - clusterFilter func(cluster *appv1.Cluster) bool, + clusterSharding sharding.ClusterShardingCache, resourceTracking argo.ResourceTracking) LiveStateCache { return &liveStateCache{ @@ -168,7 +180,7 @@ func NewLiveStateCache( kubectl: kubectl, settingsMgr: settingsMgr, metricsServer: metricsServer, - clusterFilter: clusterFilter, + clusterSharding: clusterSharding, resourceTracking: resourceTracking, } } @@ -177,6 +189,11 @@ type cacheSettings struct { clusterSettings clustercache.Settings appInstanceLabelKey string trackingMethod appv1.TrackingMethod + // resourceOverrides provides a list of ignored differences to ignore watched resource updates + resourceOverrides map[string]appv1.ResourceOverride + + // ignoreResourceUpdates is a flag to enable resource-ignore rules. + ignoreResourceUpdatesEnabled bool } type liveStateCache struct { @@ -186,7 +203,7 @@ type liveStateCache struct { kubectl kube.Kubectl settingsMgr *settings.SettingsManager metricsServer *metrics.MetricsServer - clusterFilter func(cluster *appv1.Cluster) bool + clusterSharding sharding.ClusterShardingCache resourceTracking argo.ResourceTracking clusters map[string]clustercache.ClusterCache @@ -199,6 +216,14 @@ func (c *liveStateCache) loadCacheSettings() (*cacheSettings, error) { if err != nil { return nil, err } + resourceUpdatesOverrides, err := c.settingsMgr.GetIgnoreResourceUpdatesOverrides() + if err != nil { + return nil, err + } + ignoreResourceUpdatesEnabled, err := c.settingsMgr.GetIsIgnoreResourceUpdatesEnabled() + if err != nil { + return nil, err + } resourcesFilter, err := c.settingsMgr.GetResourcesFilter() if err != nil { return nil, err @@ -211,7 +236,8 @@ func (c *liveStateCache) loadCacheSettings() (*cacheSettings, error) { ResourceHealthOverride: lua.ResourceHealthOverrides(resourceOverrides), ResourcesFilter: resourcesFilter, } - return &cacheSettings{clusterSettings, appInstanceLabelKey, argo.GetTrackingMethod(c.settingsMgr)}, nil + + return &cacheSettings{clusterSettings, appInstanceLabelKey, argo.GetTrackingMethod(c.settingsMgr), resourceUpdatesOverrides, ignoreResourceUpdatesEnabled}, nil } func asResourceNode(r *clustercache.Resource) appv1.ResourceNode { @@ -308,6 +334,27 @@ func skipAppRequeuing(key kube.ResourceKey) bool { return ignoredRefreshResources[key.Group+"/"+key.Kind] } +func skipResourceUpdate(oldInfo, newInfo *ResourceInfo) bool { + if oldInfo == nil || newInfo == nil { + return false + } + isSameHealthStatus := (oldInfo.Health == nil && newInfo.Health == nil) || oldInfo.Health != nil && newInfo.Health != nil && oldInfo.Health.Status == newInfo.Health.Status + isSameManifest := oldInfo.manifestHash != "" && newInfo.manifestHash != "" && oldInfo.manifestHash == newInfo.manifestHash + return isSameHealthStatus && isSameManifest +} + +// shouldHashManifest validates if the API resource needs to be hashed. +// If there's an app name from resource tracking, or if this is itself an app, we should generate a hash. +// Otherwise, the hashing should be skipped to save CPU time. +func shouldHashManifest(appName string, gvk schema.GroupVersionKind) bool { + // Only hash if the resource belongs to an app. + // Best - Only hash for resources that are part of an app or their dependencies + // (current) - Only hash for resources that are part of an app + all apps that might be from an ApplicationSet + // Orphan - If orphan is enabled, hash should be made on all resource of that namespace and a config to disable it + // Worst - Hash all resources watched by Argo + return appName != "" || (gvk.Group == application.Group && gvk.Kind == application.ApplicationKind) +} + // isRetryableError is a helper method to see whether an error // returned from the dynamic client is potentially retryable. func isRetryableError(err error) bool { @@ -325,9 +372,14 @@ func isRetryableError(err error) bool { isResourceQuotaConflictErr(err) || isTransientNetworkErr(err) || isExceededQuotaErr(err) || + isHTTP2GoawayErr(err) || errors.Is(err, syscall.ECONNRESET) } +func isHTTP2GoawayErr(err error) bool { + return strings.Contains(err.Error(), "http2: server sent GOAWAY and closed the connection") +} + func isExceededQuotaErr(err error) bool { return kerrors.IsForbidden(err) && strings.Contains(err.Error(), "exceeded quota") } @@ -385,6 +437,10 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e return nil, fmt.Errorf("error getting cluster: %w", err) } + if c.clusterSharding == nil { + return nil, fmt.Errorf("unable to handle cluster %s: cluster sharding is not configured", cluster.Server) + } + if !c.canHandleCluster(cluster) { return nil, fmt.Errorf("controller is configured to ignore cluster %s", cluster.Server) } @@ -394,9 +450,29 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e return nil, fmt.Errorf("error getting custom label: %w", err) } + respectRBAC, err := c.settingsMgr.RespectRBAC() + if err != nil { + return nil, fmt.Errorf("error getting value for %v: %w", settings.RespectRBAC, err) + } + + clusterCacheConfig := cluster.RESTConfig() + // Controller dynamically fetches all resource types available on the cluster + // using a discovery API that may contain deprecated APIs. + // This causes log flooding when managing a large number of clusters. + // https://github.com/argoproj/argo-cd/issues/11973 + // However, we can safely suppress deprecation warnings + // because we do not rely on resources with a particular API group or version. + // https://kubernetes.io/blog/2020/09/03/warnings/#customize-client-handling + // + // Completely suppress warning logs only for log levels that are less than Debug. + if log.GetLevel() < log.DebugLevel { + clusterCacheConfig.WarningHandler = rest.NoWarnings{} + } + clusterCacheOpts := []clustercache.UpdateSettingsFunc{ clustercache.SetListSemaphore(semaphore.NewWeighted(clusterCacheListSemaphoreSize)), clustercache.SetListPageSize(clusterCacheListPageSize), + clustercache.SetListPageBufferSize(clusterCacheListPageBufferSize), clustercache.SetWatchResyncTimeout(clusterCacheWatchResyncDuration), clustercache.SetClusterSyncRetryTimeout(clusterSyncRetryTimeoutDuration), clustercache.SetResyncTimeout(clusterCacheResyncDuration), @@ -409,23 +485,35 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e c.lock.RLock() cacheSettings := c.cacheSettings c.lock.RUnlock() + res.Health, _ = health.GetResourceHealth(un, cacheSettings.clusterSettings.ResourceHealthOverride) appName := c.resourceTracking.GetAppName(un, cacheSettings.appInstanceLabelKey, cacheSettings.trackingMethod) if isRoot && appName != "" { res.AppName = appName } + gvk := un.GroupVersionKind() + if cacheSettings.ignoreResourceUpdatesEnabled && shouldHashManifest(appName, gvk) { + hash, err := generateManifestHash(un, nil, cacheSettings.resourceOverrides) + if err != nil { + log.Errorf("Failed to generate manifest hash: %v", err) + } else { + res.manifestHash = hash + } + } + // edge case. we do not label CRDs, so they miss the tracking label we inject. But we still // want the full resource to be available in our cache (to diff), so we store all CRDs return res, res.AppName != "" || gvk.Kind == kube.CustomResourceDefinitionKind }), clustercache.SetLogr(logutils.NewLogrusLogger(log.WithField("server", cluster.Server))), clustercache.SetRetryOptions(clusterCacheAttemptLimit, clusterCacheRetryUseBackoff, isRetryableError), + clustercache.SetRespectRBAC(respectRBAC), } - clusterCache = clustercache.NewClusterCache(cluster.RESTConfig(), clusterCacheOpts...) + clusterCache = clustercache.NewClusterCache(clusterCacheConfig, clusterCacheOpts...) _ = clusterCache.OnResourceUpdated(func(newRes *clustercache.Resource, oldRes *clustercache.Resource, namespaceResources map[kube.ResourceKey]*clustercache.Resource) { toNotify := make(map[string]bool) @@ -435,6 +523,30 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e } else { ref = oldRes.Ref } + + c.lock.RLock() + cacheSettings := c.cacheSettings + c.lock.RUnlock() + + if cacheSettings.ignoreResourceUpdatesEnabled && oldRes != nil && newRes != nil && skipResourceUpdate(resInfo(oldRes), resInfo(newRes)) { + // Additional check for debug level so we don't need to evaluate the + // format string in case of non-debug scenarios + if log.GetLevel() >= log.DebugLevel { + namespace := ref.Namespace + if ref.Namespace == "" { + namespace = "(cluster-scoped)" + } + log.WithFields(log.Fields{ + "server": cluster.Server, + "namespace": namespace, + "name": ref.Name, + "api-version": ref.APIVersion, + "kind": ref.Kind, + }).Debug("Ignoring change of object because none of the watched resource fields have changed") + } + return + } + for _, r := range []*clustercache.Resource{newRes, oldRes} { if r == nil { continue @@ -473,10 +585,11 @@ func (c *liveStateCache) getSyncedCluster(server string) (clustercache.ClusterCa func (c *liveStateCache) invalidate(cacheSettings cacheSettings) { log.Info("invalidating live state cache") c.lock.Lock() - defer c.lock.Unlock() - c.cacheSettings = cacheSettings - for _, clust := range c.clusters { + clusters := c.clusters + c.lock.Unlock() + + for _, clust := range clusters { clust.Invalidate(clustercache.SetSettings(cacheSettings.clusterSettings)) } log.Info("live state cache invalidated") @@ -531,7 +644,7 @@ func (c *liveStateCache) GetNamespaceTopLevelResources(server string, namespace func (c *liveStateCache) GetManagedLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error) { clusterInfo, err := c.getSyncedCluster(a.Spec.Destination.Server) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get cluster info for %q: %w", a.Spec.Destination.Server, err) } return clusterInfo.GetManagedLiveObjs(targetObjs, func(r *clustercache.Resource) bool { return resInfo(r).AppName == a.InstanceName(c.settingsMgr.GetNamespace()) @@ -541,7 +654,7 @@ func (c *liveStateCache) GetManagedLiveObjs(a *appv1.Application, targetObjs []* func (c *liveStateCache) GetVersionsInfo(serverURL string) (string, []kube.APIResourceInfo, error) { clusterInfo, err := c.getSyncedCluster(serverURL) if err != nil { - return "", nil, err + return "", nil, fmt.Errorf("failed to get cluster info for %q: %w", serverURL, err) } return clusterInfo.GetServerVersion(), clusterInfo.GetAPIResources(), nil } @@ -619,22 +732,24 @@ func (c *liveStateCache) Run(ctx context.Context) error { } func (c *liveStateCache) canHandleCluster(cluster *appv1.Cluster) bool { - if c.clusterFilter == nil { - return true - } - return c.clusterFilter(cluster) + return c.clusterSharding.IsManagedCluster(cluster) } func (c *liveStateCache) handleAddEvent(cluster *appv1.Cluster) { + c.clusterSharding.Add(cluster) if !c.canHandleCluster(cluster) { log.Infof("Ignoring cluster %s", cluster.Server) return } - c.lock.Lock() _, ok := c.clusters[cluster.Server] c.lock.Unlock() if !ok { + log.Debugf("Checking if cache %v / cluster %v has appInformer %v", c, cluster, c.appInformer) + if c.appInformer == nil { + log.Warn("Cannot get a cluster appInformer. Cache may not be started this time") + return + } if c.isClusterHasApps(c.appInformer.GetStore().List(), cluster) { go func() { // warm up cache for cluster with apps @@ -645,6 +760,7 @@ func (c *liveStateCache) handleAddEvent(cluster *appv1.Cluster) { } func (c *liveStateCache) handleModEvent(oldCluster *appv1.Cluster, newCluster *appv1.Cluster) { + c.clusterSharding.Update(oldCluster, newCluster) c.lock.Lock() cluster, ok := c.clusters[newCluster.Server] c.lock.Unlock() @@ -686,12 +802,15 @@ func (c *liveStateCache) handleModEvent(oldCluster *appv1.Cluster, newCluster *a } func (c *liveStateCache) handleDeleteEvent(clusterServer string) { - c.lock.Lock() - defer c.lock.Unlock() + c.lock.RLock() + c.clusterSharding.Delete(clusterServer) cluster, ok := c.clusters[clusterServer] + c.lock.RUnlock() if ok { cluster.Invalidate() + c.lock.Lock() delete(c.clusters, clusterServer) + c.lock.Unlock() } } diff --git a/controller/cache/cache_test.go b/controller/cache/cache_test.go index 9d1fad82b0279..53a03ca81995e 100644 --- a/controller/cache/cache_test.go +++ b/controller/cache/cache_test.go @@ -1,22 +1,32 @@ package cache import ( + "context" "errors" "net" "net/url" + "sync" "testing" + "time" "github.com/stretchr/testify/assert" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" apierr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "github.com/argoproj/gitops-engine/pkg/cache" "github.com/argoproj/gitops-engine/pkg/cache/mocks" + "github.com/argoproj/gitops-engine/pkg/health" "github.com/stretchr/testify/mock" + "k8s.io/client-go/kubernetes/fake" + "github.com/argoproj/argo-cd/v2/common" + "github.com/argoproj/argo-cd/v2/controller/metrics" + "github.com/argoproj/argo-cd/v2/controller/sharding" appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" + argosettings "github.com/argoproj/argo-cd/v2/util/settings" ) type netError string @@ -29,11 +39,13 @@ func TestHandleModEvent_HasChanges(t *testing.T) { clusterCache := &mocks.ClusterCache{} clusterCache.On("Invalidate", mock.Anything, mock.Anything).Return(nil).Once() clusterCache.On("EnsureSynced").Return(nil).Once() - + db := &dbmocks.ArgoDB{} + db.On("GetApplicationControllerReplicas").Return(1) clustersCache := liveStateCache{ clusters: map[string]cache.ClusterCache{ "https://mycluster": clusterCache, }, + clusterSharding: sharding.NewClusterSharding(db, 0, 1, common.DefaultShardingAlgorithm), } clustersCache.handleModEvent(&appv1.Cluster{ @@ -50,14 +62,22 @@ 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() - + db := &dbmocks.ArgoDB{} + db.On("GetApplicationControllerReplicas").Return(1) clustersCache := liveStateCache{ - clusters: map[string]cache.ClusterCache{ - "https://mycluster": clusterCache, - }, - clusterFilter: func(cluster *appv1.Cluster) bool { - return false + db: nil, + appInformer: nil, + onObjectUpdated: func(managedByApp map[string]bool, ref v1.ObjectReference) { }, + kubectl: nil, + settingsMgr: &argosettings.SettingsManager{}, + metricsServer: &metrics.MetricsServer{}, + // returns a shard that never process any cluster + clusterSharding: sharding.NewClusterSharding(db, 0, 1, common.DefaultShardingAlgorithm), + resourceTracking: nil, + clusters: map[string]cache.ClusterCache{"https://mycluster": clusterCache}, + cacheSettings: cacheSettings{}, + lock: sync.RWMutex{}, } clustersCache.handleModEvent(&appv1.Cluster{ @@ -69,18 +89,20 @@ func TestHandleModEvent_ClusterExcluded(t *testing.T) { Namespaces: []string{"default"}, }) - assert.Len(t, clustersCache.clusters, 0) + assert.Len(t, clustersCache.clusters, 1) } func TestHandleModEvent_NoChanges(t *testing.T) { clusterCache := &mocks.ClusterCache{} clusterCache.On("Invalidate", mock.Anything).Panic("should not invalidate") clusterCache.On("EnsureSynced").Return(nil).Panic("should not re-sync") - + db := &dbmocks.ArgoDB{} + db.On("GetApplicationControllerReplicas").Return(1) clustersCache := liveStateCache{ clusters: map[string]cache.ClusterCache{ "https://mycluster": clusterCache, }, + clusterSharding: sharding.NewClusterSharding(db, 0, 1, common.DefaultShardingAlgorithm), } clustersCache.handleModEvent(&appv1.Cluster{ @@ -93,11 +115,11 @@ func TestHandleModEvent_NoChanges(t *testing.T) { } func TestHandleAddEvent_ClusterExcluded(t *testing.T) { + db := &dbmocks.ArgoDB{} + db.On("GetApplicationControllerReplicas").Return(1) clustersCache := liveStateCache{ - clusters: map[string]cache.ClusterCache{}, - clusterFilter: func(cluster *appv1.Cluster) bool { - return false - }, + clusters: map[string]cache.ClusterCache{}, + clusterSharding: sharding.NewClusterSharding(db, 0, 2, common.DefaultShardingAlgorithm), } clustersCache.handleAddEvent(&appv1.Cluster{ Server: "https://mycluster", @@ -107,6 +129,100 @@ func TestHandleAddEvent_ClusterExcluded(t *testing.T) { assert.Len(t, clustersCache.clusters, 0) } +func TestHandleDeleteEvent_CacheDeadlock(t *testing.T) { + testCluster := &appv1.Cluster{ + Server: "https://mycluster", + Config: appv1.ClusterConfig{Username: "bar"}, + } + db := &dbmocks.ArgoDB{} + db.On("GetApplicationControllerReplicas").Return(1) + fakeClient := fake.NewSimpleClientset() + settingsMgr := argosettings.NewSettingsManager(context.TODO(), fakeClient, "argocd") + liveStateCacheLock := sync.RWMutex{} + gitopsEngineClusterCache := &mocks.ClusterCache{} + clustersCache := liveStateCache{ + clusters: map[string]cache.ClusterCache{ + testCluster.Server: gitopsEngineClusterCache, + }, + clusterSharding: sharding.NewClusterSharding(db, 0, 1, common.DefaultShardingAlgorithm), + settingsMgr: settingsMgr, + // Set the lock here so we can reference it later + // nolint We need to overwrite here to have access to the lock + lock: liveStateCacheLock, + } + channel := make(chan string) + // Mocked lock held by the gitops-engine cluster cache + gitopsEngineClusterCacheLock := sync.Mutex{} + // Ensure completion of both EnsureSynced and Invalidate + ensureSyncedCompleted := sync.Mutex{} + invalidateCompleted := sync.Mutex{} + // Locks to force trigger condition during test + // Condition order: + // EnsuredSynced -> Locks gitops-engine + // handleDeleteEvent -> Locks liveStateCache + // EnsureSynced via sync, newResource, populateResourceInfoHandler -> attempts to Lock liveStateCache + // handleDeleteEvent via cluster.Invalidate -> attempts to Lock gitops-engine + handleDeleteWasCalled := sync.Mutex{} + engineHoldsEngineLock := sync.Mutex{} + ensureSyncedCompleted.Lock() + invalidateCompleted.Lock() + handleDeleteWasCalled.Lock() + engineHoldsEngineLock.Lock() + + gitopsEngineClusterCache.On("EnsureSynced").Run(func(args mock.Arguments) { + gitopsEngineClusterCacheLock.Lock() + t.Log("EnsureSynced: Engine has engine lock") + engineHoldsEngineLock.Unlock() + defer gitopsEngineClusterCacheLock.Unlock() + // Wait until handleDeleteEvent holds the liveStateCache lock + handleDeleteWasCalled.Lock() + // Try and obtain the liveStateCache lock + clustersCache.lock.Lock() + t.Log("EnsureSynced: Engine has LiveStateCache lock") + clustersCache.lock.Unlock() + ensureSyncedCompleted.Unlock() + }).Return(nil).Once() + + gitopsEngineClusterCache.On("Invalidate").Run(func(args mock.Arguments) { + // Allow EnsureSynced to continue now that we're in the deadlock condition + handleDeleteWasCalled.Unlock() + // Wait until gitops engine holds the gitops lock + // This prevents timing issues if we reach this point before EnsureSynced has obtained the lock + engineHoldsEngineLock.Lock() + t.Log("Invalidate: Engine has engine lock") + engineHoldsEngineLock.Unlock() + // Lock engine lock + gitopsEngineClusterCacheLock.Lock() + t.Log("Invalidate: Invalidate has engine lock") + gitopsEngineClusterCacheLock.Unlock() + invalidateCompleted.Unlock() + }).Return() + go func() { + // Start the gitops-engine lock holds + go func() { + err := gitopsEngineClusterCache.EnsureSynced() + if err != nil { + assert.Fail(t, err.Error()) + } + }() + // Run in background + go clustersCache.handleDeleteEvent(testCluster.Server) + // Allow execution to continue on clusters cache call to trigger lock + ensureSyncedCompleted.Lock() + invalidateCompleted.Lock() + t.Log("Competing functions were able to obtain locks") + invalidateCompleted.Unlock() + ensureSyncedCompleted.Unlock() + channel <- "PASSED" + }() + select { + case str := <-channel: + assert.Equal(t, "PASSED", str, str) + case <-time.After(5 * time.Second): + assert.Fail(t, "Ended up in deadlock") + } +} + func TestIsRetryableError(t *testing.T) { var ( tlsHandshakeTimeoutErr net.Error = netError("net/http: TLS handshake timeout") @@ -202,3 +318,126 @@ func Test_asResourceNode_owner_refs(t *testing.T) { } assert.Equal(t, expected, resNode) } + +func TestSkipResourceUpdate(t *testing.T) { + var ( + hash1_x string = "x" + hash2_y string = "y" + hash3_x string = "x" + ) + info := &ResourceInfo{ + manifestHash: hash1_x, + Health: &health.HealthStatus{ + Status: health.HealthStatusHealthy, + Message: "default", + }, + } + t.Run("Nil", func(t *testing.T) { + assert.False(t, skipResourceUpdate(nil, nil)) + }) + t.Run("From Nil", func(t *testing.T) { + assert.False(t, skipResourceUpdate(nil, info)) + }) + t.Run("To Nil", func(t *testing.T) { + assert.False(t, skipResourceUpdate(info, nil)) + }) + t.Run("No hash", func(t *testing.T) { + assert.False(t, skipResourceUpdate(&ResourceInfo{}, &ResourceInfo{})) + }) + t.Run("Same hash", func(t *testing.T) { + assert.True(t, skipResourceUpdate(&ResourceInfo{ + manifestHash: hash1_x, + }, &ResourceInfo{ + manifestHash: hash1_x, + })) + }) + t.Run("Same hash value", func(t *testing.T) { + assert.True(t, skipResourceUpdate(&ResourceInfo{ + manifestHash: hash1_x, + }, &ResourceInfo{ + manifestHash: hash3_x, + })) + }) + t.Run("Different hash value", func(t *testing.T) { + assert.False(t, skipResourceUpdate(&ResourceInfo{ + manifestHash: hash1_x, + }, &ResourceInfo{ + manifestHash: hash2_y, + })) + }) + t.Run("Same hash, empty health", func(t *testing.T) { + assert.True(t, skipResourceUpdate(&ResourceInfo{ + manifestHash: hash1_x, + Health: &health.HealthStatus{}, + }, &ResourceInfo{ + manifestHash: hash3_x, + Health: &health.HealthStatus{}, + })) + }) + t.Run("Same hash, old health", func(t *testing.T) { + assert.False(t, skipResourceUpdate(&ResourceInfo{ + manifestHash: hash1_x, + Health: &health.HealthStatus{ + Status: health.HealthStatusHealthy}, + }, &ResourceInfo{ + manifestHash: hash3_x, + Health: nil, + })) + }) + t.Run("Same hash, new health", func(t *testing.T) { + assert.False(t, skipResourceUpdate(&ResourceInfo{ + manifestHash: hash1_x, + Health: &health.HealthStatus{}, + }, &ResourceInfo{ + manifestHash: hash3_x, + Health: &health.HealthStatus{ + Status: health.HealthStatusHealthy, + }, + })) + }) + t.Run("Same hash, same health", func(t *testing.T) { + assert.True(t, skipResourceUpdate(&ResourceInfo{ + manifestHash: hash1_x, + Health: &health.HealthStatus{ + Status: health.HealthStatusHealthy, + Message: "same", + }, + }, &ResourceInfo{ + manifestHash: hash3_x, + Health: &health.HealthStatus{ + Status: health.HealthStatusHealthy, + Message: "same", + }, + })) + }) + t.Run("Same hash, different health status", func(t *testing.T) { + assert.False(t, skipResourceUpdate(&ResourceInfo{ + manifestHash: hash1_x, + Health: &health.HealthStatus{ + Status: health.HealthStatusHealthy, + Message: "same", + }, + }, &ResourceInfo{ + manifestHash: hash3_x, + Health: &health.HealthStatus{ + Status: health.HealthStatusDegraded, + Message: "same", + }, + })) + }) + t.Run("Same hash, different health message", func(t *testing.T) { + assert.True(t, skipResourceUpdate(&ResourceInfo{ + manifestHash: hash1_x, + Health: &health.HealthStatus{ + Status: health.HealthStatusHealthy, + Message: "same", + }, + }, &ResourceInfo{ + manifestHash: hash3_x, + Health: &health.HealthStatus{ + Status: health.HealthStatusHealthy, + Message: "different", + }, + })) + }) +} diff --git a/controller/cache/info.go b/controller/cache/info.go index 3cc7980ad8e12..53512de6b713a 100644 --- a/controller/cache/info.go +++ b/controller/cache/info.go @@ -3,12 +3,14 @@ package cache import ( "errors" "fmt" + "strconv" "strings" "k8s.io/apimachinery/pkg/runtime/schema" "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/argoproj/gitops-engine/pkg/utils/text" + "github.com/cespare/xxhash/v2" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -16,6 +18,7 @@ import ( "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/util/argo/normalizers" "github.com/argoproj/argo-cd/v2/util/resource" ) @@ -34,6 +37,16 @@ func populateNodeInfo(un *unstructured.Unstructured, res *ResourceInfo, customLa } } } + + 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) + } + } + switch gvk.Group { case "": switch gvk.Kind { @@ -55,15 +68,6 @@ func populateNodeInfo(un *unstructured.Unstructured, res *ResourceInfo, customLa populateIstioVirtualServiceInfo(un, res) } } - - 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) - } - } } func getIngress(un *unstructured.Unstructured) []v1.LoadBalancerIngress { @@ -90,7 +94,13 @@ func populateServiceInfo(un *unstructured.Unstructured, res *ResourceInfo) { if serviceType, ok, err := unstructured.NestedString(un.Object, "spec", "type"); ok && err == nil && serviceType == string(v1.ServiceTypeLoadBalancer) { ingress = getIngress(un) } - res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetLabels: targetLabels, Ingress: ingress} + + var urls []string + if res.NetworkingInfo != nil { + urls = res.NetworkingInfo.ExternalURLs + } + + res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetLabels: targetLabels, Ingress: ingress, ExternalURLs: urls} } func getServiceName(backend map[string]interface{}, gvk schema.GroupVersionKind) (string, error) { @@ -260,7 +270,12 @@ func populateIstioVirtualServiceInfo(un *unstructured.Unstructured, res *Resourc targets = append(targets, target) } - res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetRefs: targets} + var urls []string + if res.NetworkingInfo != nil { + urls = res.NetworkingInfo.ExternalURLs + } + + res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetRefs: targets, ExternalURLs: urls} } func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) { @@ -371,7 +386,13 @@ func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) { if restarts > 0 { res.Info = append(res.Info, v1alpha1.InfoItem{Name: "Restart Count", Value: fmt.Sprintf("%d", restarts)}) } - res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{Labels: un.GetLabels()} + + var urls []string + if res.NetworkingInfo != nil { + urls = res.NetworkingInfo.ExternalURLs + } + + res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{Labels: un.GetLabels(), ExternalURLs: urls} } func populateHostNodeInfo(un *unstructured.Unstructured, res *ResourceInfo) { @@ -386,3 +407,27 @@ func populateHostNodeInfo(un *unstructured.Unstructured, res *ResourceInfo) { SystemInfo: node.Status.NodeInfo, } } + +func generateManifestHash(un *unstructured.Unstructured, ignores []v1alpha1.ResourceIgnoreDifferences, overrides map[string]v1alpha1.ResourceOverride) (string, error) { + normalizer, err := normalizers.NewIgnoreNormalizer(ignores, overrides) + if err != nil { + return "", fmt.Errorf("error creating normalizer: %w", err) + } + + resource := un.DeepCopy() + err = normalizer.Normalize(resource) + if err != nil { + return "", fmt.Errorf("error normalizing resource: %w", err) + } + + data, err := resource.MarshalJSON() + if err != nil { + return "", fmt.Errorf("error marshaling resource: %w", err) + } + hash := hash(data) + return hash, nil +} + +func hash(data []byte) string { + return strconv.FormatUint(xxhash.Sum64(data), 16) +} diff --git a/controller/cache/info_test.go b/controller/cache/info_test.go index 6c9977876bae0..7b48040009284 100644 --- a/controller/cache/info_test.go +++ b/controller/cache/info_test.go @@ -9,11 +9,11 @@ import ( "github.com/argoproj/gitops-engine/pkg/utils/kube" "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" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) @@ -52,7 +52,7 @@ var ( resourceVersion: "123" uid: "4" annotations: - link.argocd.argoproj.io/external-link: http://my-grafana.com/pre-generated-link + link.argocd.argoproj.io/external-link: http://my-grafana.example.com/pre-generated-link spec: selector: app: guestbook @@ -74,7 +74,7 @@ var ( serviceName: not-found-service servicePort: 443 rules: - - host: helm-guestbook.com + - host: helm-guestbook.example.com http: paths: - backend: @@ -86,7 +86,7 @@ var ( servicePort: https path: / tls: - - host: helm-guestbook.com + - host: helm-guestbook.example.com secretName: my-tls-secret status: loadBalancer: @@ -101,13 +101,13 @@ var ( namespace: default uid: "4" annotations: - link.argocd.argoproj.io/external-link: http://my-grafana.com/ingress-link + link.argocd.argoproj.io/external-link: http://my-grafana.example.com/ingress-link spec: backend: serviceName: not-found-service servicePort: 443 rules: - - host: helm-guestbook.com + - host: helm-guestbook.example.com http: paths: - backend: @@ -119,7 +119,7 @@ var ( servicePort: https path: / tls: - - host: helm-guestbook.com + - host: helm-guestbook.example.com secretName: my-tls-secret status: loadBalancer: @@ -138,7 +138,7 @@ var ( serviceName: not-found-service servicePort: 443 rules: - - host: helm-guestbook.com + - host: helm-guestbook.example.com http: paths: - backend: @@ -150,7 +150,7 @@ var ( servicePort: https path: /* tls: - - host: helm-guestbook.com + - host: helm-guestbook.example.com secretName: my-tls-secret status: loadBalancer: @@ -169,7 +169,7 @@ var ( serviceName: not-found-service servicePort: 443 rules: - - host: helm-guestbook.com + - host: helm-guestbook.example.com http: paths: - backend: @@ -199,7 +199,7 @@ var ( port: number: 443 rules: - - host: helm-guestbook.com + - host: helm-guestbook.example.com http: paths: - backend: @@ -215,7 +215,7 @@ var ( name: https path: / tls: - - host: helm-guestbook.com + - host: helm-guestbook.example.com secretName: my-tls-secret status: loadBalancer: @@ -327,7 +327,7 @@ func TestGetLinkAnnotatedServiceInfo(t *testing.T) { assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ TargetLabels: map[string]string{"app": "guestbook"}, Ingress: []v1.LoadBalancerIngress{{Hostname: "localhost"}}, - ExternalURLs: []string{"http://my-grafana.com/pre-generated-link"}, + ExternalURLs: []string{"http://my-grafana.example.com/pre-generated-link"}, }, info.NetworkingInfo) } @@ -381,7 +381,7 @@ func TestGetIngressInfo(t *testing.T) { Kind: kube.ServiceKind, Name: "helm-guestbook", }}, - ExternalURLs: []string{"https://helm-guestbook.com/"}, + ExternalURLs: []string{"https://helm-guestbook.example.com/"}, }, info.NetworkingInfo) } } @@ -406,7 +406,7 @@ func TestGetLinkAnnotatedIngressInfo(t *testing.T) { Kind: kube.ServiceKind, Name: "helm-guestbook", }}, - ExternalURLs: []string{"https://helm-guestbook.com/", "http://my-grafana.com/ingress-link"}, + ExternalURLs: []string{"http://my-grafana.example.com/ingress-link", "https://helm-guestbook.example.com/"}, }, info.NetworkingInfo) } @@ -430,7 +430,7 @@ func TestGetIngressInfoWildCardPath(t *testing.T) { Kind: kube.ServiceKind, Name: "helm-guestbook", }}, - ExternalURLs: []string{"https://helm-guestbook.com/"}, + ExternalURLs: []string{"https://helm-guestbook.example.com/"}, }, info.NetworkingInfo) } @@ -454,7 +454,7 @@ func TestGetIngressInfoWithoutTls(t *testing.T) { Kind: kube.ServiceKind, Name: "helm-guestbook", }}, - ExternalURLs: []string{"http://helm-guestbook.com/"}, + ExternalURLs: []string{"http://helm-guestbook.example.com/"}, }, info.NetworkingInfo) } @@ -563,7 +563,7 @@ func TestExternalUrlWithMultipleSubPaths(t *testing.T) { namespace: default spec: rules: - - host: helm-guestbook.com + - host: helm-guestbook.example.com http: paths: - backend: @@ -587,7 +587,7 @@ func TestExternalUrlWithMultipleSubPaths(t *testing.T) { info := &ResourceInfo{} populateNodeInfo(ingress, info, []string{}) - expectedExternalUrls := []string{"https://helm-guestbook.com/my/sub/path/", "https://helm-guestbook.com/my/sub/path/2", "https://helm-guestbook.com"} + expectedExternalUrls := []string{"https://helm-guestbook.example.com/my/sub/path/", "https://helm-guestbook.example.com/my/sub/path/2", "https://helm-guestbook.example.com"} actualURLs := info.NetworkingInfo.ExternalURLs sort.Strings(expectedExternalUrls) sort.Strings(actualURLs) @@ -694,3 +694,62 @@ func TestCustomLabel(t *testing.T) { assert.Equal(t, "other-label", info.Info[1].Name) assert.Equal(t, "value2", info.Info[1].Value) } + +func TestManifestHash(t *testing.T) { + manifest := strToUnstructured(` + apiVersion: v1 + kind: Pod + metadata: + name: helm-guestbook-pod + namespace: default + ownerReferences: + - apiVersion: extensions/v1beta1 + kind: ReplicaSet + name: helm-guestbook-rs + resourceVersion: "123" + labels: + app: guestbook + spec: + nodeName: minikube + containers: + - image: bar + resources: + requests: + memory: 128Mi +`) + + ignores := []v1alpha1.ResourceIgnoreDifferences{ + { + Group: "*", + Kind: "*", + JSONPointers: []string{"/metadata/resourceVersion"}, + }, + } + + data, _ := strToUnstructured(` + apiVersion: v1 + kind: Pod + metadata: + name: helm-guestbook-pod + namespace: default + ownerReferences: + - apiVersion: extensions/v1beta1 + kind: ReplicaSet + name: helm-guestbook-rs + labels: + app: guestbook + spec: + nodeName: minikube + containers: + - image: bar + resources: + requests: + memory: 128Mi +`).MarshalJSON() + + expected := hash(data) + + hash, err := generateManifestHash(manifest, ignores, nil) + assert.Equal(t, expected, hash) + assert.Nil(t, err) +} diff --git a/controller/clusterinfoupdater.go b/controller/clusterinfoupdater.go index 04ec12dec5040..d87cdad6be85d 100644 --- a/controller/clusterinfoupdater.go +++ b/controller/clusterinfoupdater.go @@ -2,8 +2,11 @@ package controller import ( "context" - "time" "fmt" + "github.com/argoproj/argo-cd/v2/common" + "time" + + "github.com/argoproj/argo-cd/v2/util/env" "github.com/argoproj/gitops-engine/pkg/cache" "github.com/argoproj/gitops-engine/pkg/utils/kube" log "github.com/sirupsen/logrus" @@ -19,7 +22,13 @@ import ( ) const ( - secretUpdateInterval = 10 * time.Second + defaultSecretUpdateInterval = 10 * time.Second + + EnvClusterInfoTimeout = "ARGO_CD_UPDATE_CLUSTER_INFO_TIMEOUT" +) + +var ( + clusterInfoTimeout = env.ParseDurationFromEnv(EnvClusterInfoTimeout, defaultSecretUpdateInterval, defaultSecretUpdateInterval, 1*time.Minute) ) type clusterInfoUpdater struct { @@ -30,6 +39,7 @@ type clusterInfoUpdater struct { clusterFilter func(cluster *appv1.Cluster) bool projGetter func(app *appv1.Application) (*appv1.AppProject, error) namespace string + lastUpdated time.Time } func NewClusterInfoUpdater( @@ -41,17 +51,17 @@ func NewClusterInfoUpdater( projGetter func(app *appv1.Application) (*appv1.AppProject, error), namespace string) *clusterInfoUpdater { - return &clusterInfoUpdater{infoSource, db, appLister, cache, clusterFilter, projGetter, namespace} + return &clusterInfoUpdater{infoSource, db, appLister, cache, clusterFilter, projGetter, namespace, time.Time{}} } func (c *clusterInfoUpdater) Run(ctx context.Context) { c.updateClusters() - ticker := time.NewTicker(secretUpdateInterval) + ticker := time.NewTicker(clusterInfoTimeout) for { select { case <-ctx.Done(): ticker.Stop() - break + return case <-ticker.C: c.updateClusters() } @@ -59,13 +69,23 @@ func (c *clusterInfoUpdater) Run(ctx context.Context) { } func (c *clusterInfoUpdater) updateClusters() { + if time.Since(c.lastUpdated) < clusterInfoTimeout { + return + } + + ctx, cancel := context.WithTimeout(context.Background(), clusterInfoTimeout) + defer func() { + cancel() + c.lastUpdated = time.Now() + }() + infoByServer := make(map[string]*cache.ClusterInfo) clustersInfo := c.infoSource.GetClustersInfo() for i := range clustersInfo { info := clustersInfo[i] infoByServer[info.Server] = &info } - clusters, err := c.db.ListClusters(context.Background()) + clusters, err := c.db.ListClusters(ctx) if err != nil { log.Warnf("Failed to save clusters info: %v", err) return @@ -82,19 +102,28 @@ func (c *clusterInfoUpdater) updateClusters() { } _ = 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) + clusterInfo := infoByServer[cluster.Server] + if err := c.updateClusterInfo(ctx, cluster, clusterInfo); err != nil { + log.Warnf("Failed to save cluster info: %v", err) + } else if err := updateClusterLabels(ctx, clusterInfo, cluster, c.db.UpdateCluster); err != nil { + log.Warnf("Failed to update cluster labels: %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 { +func (c *clusterInfoUpdater) updateClusterInfo(ctx context.Context, cluster appv1.Cluster, info *cache.ClusterInfo) error { apps, err := c.appLister.List(labels.Everything()) if err != nil { return fmt.Errorf("error while fetching the apps list: %w", err) } + + updated := c.getUpdatedClusterInfo(ctx, apps, cluster, info, metav1.Now()) + return c.cache.SetClusterInfo(cluster.Server, &updated) +} + +func (c *clusterInfoUpdater) getUpdatedClusterInfo(ctx context.Context, apps []*appv1.Application, cluster appv1.Cluster, info *cache.ClusterInfo, now metav1.Time) appv1.ClusterInfo { var appCount int64 for _, a := range apps { if c.projGetter != nil { @@ -103,14 +132,13 @@ func (c *clusterInfoUpdater) updateClusterInfo(cluster appv1.Cluster, info *cach continue } } - if err := argo.ValidateDestination(context.Background(), &a.Spec.Destination, c.db); err != nil { + if err := argo.ValidateDestination(ctx, &a.Spec.Destination, c.db); err != nil { continue } if a.Spec.Destination.Server == cluster.Server { appCount += 1 } } - now := metav1.Now() clusterInfo := appv1.ClusterInfo{ ConnectionState: appv1.ConnectionState{ModifiedAt: &now}, ApplicationsCount: appCount, @@ -137,5 +165,15 @@ func (c *clusterInfoUpdater) updateClusterInfo(cluster appv1.Cluster, info *cach } } - return c.cache.SetClusterInfo(cluster.Server, &clusterInfo) + return clusterInfo +} + +func updateClusterLabels(ctx context.Context, clusterInfo *cache.ClusterInfo, cluster appv1.Cluster, updateCluster func(context.Context, *appv1.Cluster) (*appv1.Cluster, error)) error { + if clusterInfo != nil && cluster.Labels[common.LabelKeyAutoLabelClusterInfo] == "true" && cluster.Labels[common.LabelKeyClusterKubernetesVersion] != clusterInfo.K8SVersion { + cluster.Labels[common.LabelKeyClusterKubernetesVersion] = clusterInfo.K8SVersion + _, err := updateCluster(ctx, &cluster) + return err + } + + return nil } diff --git a/controller/clusterinfoupdater_test.go b/controller/clusterinfoupdater_test.go index 60f074d2cfd37..d11d4412bf30c 100644 --- a/controller/clusterinfoupdater_test.go +++ b/controller/clusterinfoupdater_test.go @@ -2,6 +2,7 @@ package controller import ( "context" + "errors" "fmt" "testing" "time" @@ -88,7 +89,7 @@ func TestClusterSecretUpdater(t *testing.T) { lister := applisters.NewApplicationLister(appInformer.GetIndexer()).Applications(fakeNamespace) updater := NewClusterInfoUpdater(nil, argoDB, lister, appCache, nil, nil, fakeNamespace) - err = updater.updateClusterInfo(*cluster, info) + err = updater.updateClusterInfo(context.Background(), *cluster, info) assert.NoError(t, err, "Invoking updateClusterInfo failed.") var clusterInfo v1alpha1.ClusterInfo @@ -98,3 +99,92 @@ func TestClusterSecretUpdater(t *testing.T) { assert.Equal(t, test.ExpectedStatus, clusterInfo.ConnectionState.Status) } } + +func TestUpdateClusterLabels(t *testing.T) { + shouldNotBeInvoked := func(ctx context.Context, cluster *v1alpha1.Cluster) (*v1alpha1.Cluster, error) { + shouldNotHappen := errors.New("if an error happens here, something's wrong") + assert.NoError(t, shouldNotHappen) + return nil, shouldNotHappen + } + tests := []struct { + name string + clusterInfo *clustercache.ClusterInfo + cluster v1alpha1.Cluster + updateCluster func(context.Context, *v1alpha1.Cluster) (*v1alpha1.Cluster, error) + wantErr assert.ErrorAssertionFunc + }{ + { + "enableClusterInfoLabels = false", + &clustercache.ClusterInfo{ + Server: "kubernetes.svc.local", + K8SVersion: "1.28", + }, + v1alpha1.Cluster{ + Server: "kubernetes.svc.local", + Labels: nil, + }, + shouldNotBeInvoked, + assert.NoError, + }, + { + "clusterInfo = nil", + nil, + v1alpha1.Cluster{ + Server: "kubernetes.svc.local", + Labels: map[string]string{"argocd.argoproj.io/auto-label-cluster-info": "true"}, + }, + shouldNotBeInvoked, + assert.NoError, + }, + { + "clusterInfo.k8sversion == cluster k8s label", + &clustercache.ClusterInfo{ + Server: "kubernetes.svc.local", + K8SVersion: "1.28", + }, + v1alpha1.Cluster{ + Server: "kubernetes.svc.local", + Labels: map[string]string{"argocd.argoproj.io/kubernetes-version": "1.28", "argocd.argoproj.io/auto-label-cluster-info": "true"}, + }, + shouldNotBeInvoked, + assert.NoError, + }, + { + "clusterInfo.k8sversion != cluster k8s label, no error", + &clustercache.ClusterInfo{ + Server: "kubernetes.svc.local", + K8SVersion: "1.28", + }, + v1alpha1.Cluster{ + Server: "kubernetes.svc.local", + Labels: map[string]string{"argocd.argoproj.io/kubernetes-version": "1.27", "argocd.argoproj.io/auto-label-cluster-info": "true"}, + }, + func(ctx context.Context, cluster *v1alpha1.Cluster) (*v1alpha1.Cluster, error) { + assert.Equal(t, cluster.Labels["argocd.argoproj.io/kubernetes-version"], "1.28") + return nil, nil + }, + assert.NoError, + }, + { + "clusterInfo.k8sversion != cluster k8s label, some error", + &clustercache.ClusterInfo{ + Server: "kubernetes.svc.local", + K8SVersion: "1.28", + }, + v1alpha1.Cluster{ + Server: "kubernetes.svc.local", + Labels: map[string]string{"argocd.argoproj.io/kubernetes-version": "1.27", "argocd.argoproj.io/auto-label-cluster-info": "true"}, + }, + func(ctx context.Context, cluster *v1alpha1.Cluster) (*v1alpha1.Cluster, error) { + assert.Equal(t, cluster.Labels["argocd.argoproj.io/kubernetes-version"], "1.28") + return nil, errors.New("some error happened while saving") + }, + assert.Error, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.wantErr(t, updateClusterLabels(context.Background(), tt.clusterInfo, tt.cluster, tt.updateCluster), fmt.Sprintf("updateClusterLabels(%v, %v, %v)", context.Background(), tt.clusterInfo, tt.cluster)) + }) + } +} diff --git a/controller/health.go b/controller/health.go index 34a1ea07a0132..b1acac8ac5b9b 100644 --- a/controller/health.go +++ b/controller/health.go @@ -1,10 +1,13 @@ package controller import ( + "fmt" + "github.com/argoproj/gitops-engine/pkg/health" hookutil "github.com/argoproj/gitops-engine/pkg/sync/hook" "github.com/argoproj/gitops-engine/pkg/sync/ignore" kubeutil "github.com/argoproj/gitops-engine/pkg/utils/kube" + log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/runtime/schema" "github.com/argoproj/argo-cd/v2/pkg/apis/application" @@ -15,6 +18,7 @@ import ( // setApplicationHealth updates the health statuses of all resources performed in the comparison func setApplicationHealth(resources []managedResource, statuses []appv1.ResourceStatus, resourceOverrides map[string]appv1.ResourceOverride, app *appv1.Application, persistResourceHealth bool) (*appv1.HealthStatus, error) { var savedErr error + var errCount uint appHealth := appv1.HealthStatus{Status: health.HealthStatusHealthy} for i, res := range resources { if res.Target != nil && hookutil.Skip(res.Target) { @@ -38,7 +42,10 @@ func setApplicationHealth(resources []managedResource, statuses []appv1.Resource } healthStatus, err = health.GetResourceHealth(res.Live, healthOverrides) if err != nil && savedErr == nil { - savedErr = err + errCount++ + savedErr = fmt.Errorf("failed to get resource health for %q with name %q in namespace %q: %w", res.Live.GetKind(), res.Live.GetName(), res.Live.GetNamespace(), err) + // also log so we don't lose the message + log.WithField("application", app.QualifiedName()).Warn(savedErr) } } @@ -72,5 +79,8 @@ func setApplicationHealth(resources []managedResource, statuses []appv1.Resource } else { app.Status.ResourceHealthSource = appv1.ResourceHealthLocationAppTree } + if savedErr != nil && errCount > 1 { + savedErr = fmt.Errorf("see applicaton-controller logs for %d other errors; most recent error was: %w", errCount-1, savedErr) + } return &appHealth, savedErr } diff --git a/controller/health_test.go b/controller/health_test.go index ed5cfad25b02e..caa53b446f733 100644 --- a/controller/health_test.go +++ b/controller/health_test.go @@ -7,11 +7,11 @@ import ( "github.com/argoproj/gitops-engine/pkg/health" synccommon "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/argoproj/gitops-engine/pkg/utils/kube" - "github.com/ghodss/yaml" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/pkg/apis/application" appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -124,7 +124,7 @@ func newAppLiveObj(status health.HealthStatusCode) *unstructured.Unstructured { }, TypeMeta: metav1.TypeMeta{ APIVersion: "argoproj.io/v1alpha1", - Kind: "Application", + Kind: application.ApplicationKind, }, Status: appv1.ApplicationStatus{ Health: appv1.HealthStatus{ diff --git a/controller/hook.go b/controller/hook.go new file mode 100644 index 0000000000000..0c019ac6a1e08 --- /dev/null +++ b/controller/hook.go @@ -0,0 +1,158 @@ +package controller + +import ( + "context" + + "github.com/argoproj/gitops-engine/pkg/health" + "github.com/argoproj/gitops-engine/pkg/sync/common" + "github.com/argoproj/gitops-engine/pkg/sync/hook" + "github.com/argoproj/gitops-engine/pkg/utils/kube" + log "github.com/sirupsen/logrus" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/client-go/rest" + + "github.com/argoproj/argo-cd/v2/util/lua" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +var ( + postDeleteHook = "PostDelete" + postDeleteHooks = map[string]string{ + "argocd.argoproj.io/hook": postDeleteHook, + "helm.sh/hook": "post-delete", + } +) + +func isHook(obj *unstructured.Unstructured) bool { + return hook.IsHook(obj) || isPostDeleteHook(obj) +} + +func isPostDeleteHook(obj *unstructured.Unstructured) bool { + if obj == nil || obj.GetAnnotations() == nil { + return false + } + for k, v := range postDeleteHooks { + if val, ok := obj.GetAnnotations()[k]; ok && val == v { + return true + } + } + return false +} + +func (ctrl *ApplicationController) executePostDeleteHooks(app *v1alpha1.Application, proj *v1alpha1.AppProject, liveObjs map[kube.ResourceKey]*unstructured.Unstructured, config *rest.Config, logCtx *log.Entry) (bool, error) { + appLabelKey, err := ctrl.settingsMgr.GetAppInstanceLabelKey() + if err != nil { + return false, err + } + var revisions []string + for _, src := range app.Spec.GetSources() { + revisions = append(revisions, src.TargetRevision) + } + + targets, _, err := ctrl.appStateManager.GetRepoObjs(app, app.Spec.GetSources(), appLabelKey, revisions, false, false, false, proj) + if err != nil { + return false, err + } + runningHooks := map[kube.ResourceKey]*unstructured.Unstructured{} + for key, obj := range liveObjs { + if isPostDeleteHook(obj) { + runningHooks[key] = obj + } + } + + expectedHook := map[kube.ResourceKey]*unstructured.Unstructured{} + for _, obj := range targets { + if obj.GetNamespace() == "" { + obj.SetNamespace(app.Spec.Destination.Namespace) + } + if !isPostDeleteHook(obj) { + continue + } + if runningHook := runningHooks[kube.GetResourceKey(obj)]; runningHook == nil { + expectedHook[kube.GetResourceKey(obj)] = obj + } + } + createdCnt := 0 + for _, obj := range expectedHook { + _, err = ctrl.kubectl.CreateResource(context.Background(), config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), obj, v1.CreateOptions{}) + if err != nil { + return false, err + } + createdCnt++ + } + if createdCnt > 0 { + logCtx.Infof("Created %d post-delete hooks", createdCnt) + return false, nil + } + resourceOverrides, err := ctrl.settingsMgr.GetResourceOverrides() + if err != nil { + return false, err + } + healthOverrides := lua.ResourceHealthOverrides(resourceOverrides) + + progressingHooksCnt := 0 + for _, obj := range runningHooks { + hookHealth, err := health.GetResourceHealth(obj, healthOverrides) + if err != nil { + return false, err + } + if hookHealth.Status == health.HealthStatusProgressing { + progressingHooksCnt++ + } + } + if progressingHooksCnt > 0 { + logCtx.Infof("Waiting for %d post-delete hooks to complete", progressingHooksCnt) + return false, nil + } + + return true, nil +} + +func (ctrl *ApplicationController) cleanupPostDeleteHooks(liveObjs map[kube.ResourceKey]*unstructured.Unstructured, config *rest.Config, logCtx *log.Entry) (bool, error) { + resourceOverrides, err := ctrl.settingsMgr.GetResourceOverrides() + if err != nil { + return false, err + } + healthOverrides := lua.ResourceHealthOverrides(resourceOverrides) + + pendingDeletionCount := 0 + aggregatedHealth := health.HealthStatusHealthy + var hooks []*unstructured.Unstructured + for _, obj := range liveObjs { + if !isPostDeleteHook(obj) { + continue + } + hookHealth, err := health.GetResourceHealth(obj, healthOverrides) + if err != nil { + return false, err + } + if health.IsWorse(aggregatedHealth, hookHealth.Status) { + aggregatedHealth = hookHealth.Status + } + hooks = append(hooks, obj) + } + + for _, obj := range hooks { + for _, policy := range hook.DeletePolicies(obj) { + if policy == common.HookDeletePolicyHookFailed && aggregatedHealth == health.HealthStatusDegraded || policy == common.HookDeletePolicyHookSucceeded && aggregatedHealth == health.HealthStatusHealthy { + pendingDeletionCount++ + if obj.GetDeletionTimestamp() != nil { + continue + } + logCtx.Infof("Deleting post-delete hook %s/%s", obj.GetNamespace(), obj.GetName()) + err = ctrl.kubectl.DeleteResource(context.Background(), config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), v1.DeleteOptions{}) + if err != nil { + return false, err + } + } + } + + } + if pendingDeletionCount > 0 { + logCtx.Infof("Waiting for %d post-delete hooks to be deleted", pendingDeletionCount) + return false, nil + } + return true, nil +} diff --git a/controller/metrics/metrics.go b/controller/metrics/metrics.go index 3cd9837ff7036..94405b51eac75 100644 --- a/controller/metrics/metrics.go +++ b/controller/metrics/metrics.go @@ -17,11 +17,14 @@ import ( log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/labels" + "github.com/argoproj/argo-cd/v2/common" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" applister "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1" "github.com/argoproj/argo-cd/v2/util/git" "github.com/argoproj/argo-cd/v2/util/healthz" "github.com/argoproj/argo-cd/v2/util/profile" + + ctrl_metrics "sigs.k8s.io/controller-runtime/pkg/metrics" ) type MetricsServer struct { @@ -56,7 +59,7 @@ var ( descAppInfo = prometheus.NewDesc( "argocd_app_info", "Information about application.", - append(descAppDefaultLabels, "repo", "dest_server", "dest_namespace", "sync_status", "health_status", "operation"), + append(descAppDefaultLabels, "autosync_enabled", "repo", "dest_server", "dest_namespace", "sync_status", "health_status", "operation"), nil, ) // DEPRECATED @@ -159,12 +162,12 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFil mux := http.NewServeMux() registry := NewAppRegistry(appLister, appFilter, appLabels) - registry.MustRegister(depth, adds, latency, workDuration, unfinished, longestRunningProcessor, retries) + mux.Handle(MetricsPath, promhttp.HandlerFor(prometheus.Gatherers{ // contains app controller specific metrics registry, - // contains process, golang and controller workqueues metrics - prometheus.DefaultGatherer, + // contains workqueue metrics, process and golang metrics + ctrl_metrics.Registry, }, promhttp.HandlerOpts{})) profile.RegisterProfiler(mux) healthz.ServeHealthCheck(mux, healthCheck) @@ -260,12 +263,12 @@ func (m *MetricsServer) IncKubernetesRequest(app *argoappv1.Application, server, } func (m *MetricsServer) IncRedisRequest(failed bool) { - m.redisRequestCounter.WithLabelValues(m.hostname, "argocd-application-controller", strconv.FormatBool(failed)).Inc() + m.redisRequestCounter.WithLabelValues(m.hostname, common.ApplicationController, strconv.FormatBool(failed)).Inc() } // ObserveRedisRequestDuration observes redis request duration func (m *MetricsServer) ObserveRedisRequestDuration(duration time.Duration) { - m.redisRequestHistogram.WithLabelValues(m.hostname, "argocd-application-controller").Observe(duration.Seconds()) + m.redisRequestHistogram.WithLabelValues(m.hostname, common.ApplicationController).Observe(duration.Seconds()) } // IncReconcile increments the reconcile counter for an application @@ -381,7 +384,9 @@ func (c *appCollector) collectApps(ch chan<- prometheus.Metric, app *argoappv1.A healthStatus = health.HealthStatusUnknown } - addGauge(descAppInfo, 1, git.NormalizeGitURL(app.Spec.GetSource().RepoURL), app.Spec.Destination.Server, app.Spec.Destination.Namespace, string(syncStatus), string(healthStatus), operation) + autoSyncEnabled := app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.Automated != nil + + addGauge(descAppInfo, 1, strconv.FormatBool(autoSyncEnabled), git.NormalizeGitURL(app.Spec.GetSource().RepoURL), app.Spec.Destination.Server, app.Spec.Destination.Namespace, string(syncStatus), string(healthStatus), operation) if len(c.appLabels) > 0 { labelValues := []string{} diff --git a/controller/metrics/metrics_test.go b/controller/metrics/metrics_test.go index 36a1574489719..23628c38347a5 100644 --- a/controller/metrics/metrics_test.go +++ b/controller/metrics/metrics_test.go @@ -2,26 +2,29 @@ package metrics import ( "context" + "fmt" "log" "net/http" "net/http/httptest" - "os" "strings" "testing" "time" gitopsCache "github.com/argoproj/gitops-engine/pkg/cache" "github.com/argoproj/gitops-engine/pkg/sync/common" - "github.com/ghodss/yaml" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + "sigs.k8s.io/yaml" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake" appinformer "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions" applister "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1" + + "sigs.k8s.io/controller-runtime/pkg/controller" ) const fakeApp = ` @@ -67,6 +70,10 @@ spec: source: path: some/path repoURL: https://github.com/argoproj/argocd-example-apps.git + syncPolicy: + automated: + selfHeal: false + prune: true status: sync: status: Synced @@ -98,6 +105,10 @@ spec: source: path: some/path repoURL: https://github.com/argoproj/argocd-example-apps.git + syncPolicy: + automated: + selfHeal: true + prune: false status: sync: status: OutOfSync @@ -133,6 +144,12 @@ var appFilter = func(obj interface{}) bool { return true } +func init() { + // Create a fake controller so we initialize the internal controller metrics. + // https://github.com/kubernetes-sigs/controller-runtime/blob/4000e996a202917ad7d40f02ed8a2079a9ce25e9/pkg/internal/controller/metrics/metrics.go + _, _ = controller.New("test-controller", nil, controller.Options{}) +} + func newFakeApp(fakeAppYAML string) *argoappv1.Application { var app argoappv1.Application err := yaml.Unmarshal([]byte(fakeAppYAML), &app) @@ -207,7 +224,7 @@ func runTest(t *testing.T, cfg TestMetricServerConfig) { metricsServ.registry.MustRegister(collector) } - req, err := http.NewRequest("GET", "/metrics", nil) + req, err := http.NewRequest(http.MethodGet, "/metrics", nil) assert.NoError(t, err) rr := httptest.NewRecorder() metricsServ.Handler.ServeHTTP(rr, req) @@ -228,9 +245,9 @@ func TestMetrics(t *testing.T) { responseContains: ` # HELP argocd_app_info Information about application. # TYPE argocd_app_info gauge -argocd_app_info{dest_namespace="dummy-namespace",dest_server="https://localhost:6443",health_status="Degraded",name="my-app-3",namespace="argocd",operation="delete",project="important-project",repo="https://github.com/argoproj/argocd-example-apps",sync_status="OutOfSync"} 1 -argocd_app_info{dest_namespace="dummy-namespace",dest_server="https://localhost:6443",health_status="Healthy",name="my-app",namespace="argocd",operation="",project="important-project",repo="https://github.com/argoproj/argocd-example-apps",sync_status="Synced"} 1 -argocd_app_info{dest_namespace="dummy-namespace",dest_server="https://localhost:6443",health_status="Healthy",name="my-app-2",namespace="argocd",operation="sync",project="important-project",repo="https://github.com/argoproj/argocd-example-apps",sync_status="Synced"} 1 +argocd_app_info{autosync_enabled="true",dest_namespace="dummy-namespace",dest_server="https://localhost:6443",health_status="Degraded",name="my-app-3",namespace="argocd",operation="delete",project="important-project",repo="https://github.com/argoproj/argocd-example-apps",sync_status="OutOfSync"} 1 +argocd_app_info{autosync_enabled="false",dest_namespace="dummy-namespace",dest_server="https://localhost:6443",health_status="Healthy",name="my-app",namespace="argocd",operation="",project="important-project",repo="https://github.com/argoproj/argocd-example-apps",sync_status="Synced"} 1 +argocd_app_info{autosync_enabled="true",dest_namespace="dummy-namespace",dest_server="https://localhost:6443",health_status="Healthy",name="my-app-2",namespace="argocd",operation="sync",project="important-project",repo="https://github.com/argoproj/argocd-example-apps",sync_status="Synced"} 1 `, }, { @@ -238,7 +255,7 @@ argocd_app_info{dest_namespace="dummy-namespace",dest_server="https://localhost: responseContains: ` # HELP argocd_app_info Information about application. # TYPE argocd_app_info gauge -argocd_app_info{dest_namespace="dummy-namespace",dest_server="https://localhost:6443",health_status="Healthy",name="my-app",namespace="argocd",operation="",project="default",repo="https://github.com/argoproj/argocd-example-apps",sync_status="Synced"} 1 +argocd_app_info{autosync_enabled="false",dest_namespace="dummy-namespace",dest_server="https://localhost:6443",health_status="Healthy",name="my-app",namespace="argocd",operation="",project="default",repo="https://github.com/argoproj/argocd-example-apps",sync_status="Synced"} 1 `, }, } @@ -292,8 +309,7 @@ argocd_app_labels{label_non_existing="",name="my-app-3",namespace="argocd",proje } func TestLegacyMetrics(t *testing.T) { - os.Setenv(EnvVarLegacyControllerMetrics, "true") - defer os.Unsetenv(EnvVarLegacyControllerMetrics) + t.Setenv(EnvVarLegacyControllerMetrics, "true") expectedResponse := ` # HELP argocd_app_created_time Creation time in unix timestamp for an application. @@ -337,7 +353,7 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationSucceeded}) metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationSucceeded}) - req, err := http.NewRequest("GET", "/metrics", nil) + req, err := http.NewRequest(http.MethodGet, "/metrics", nil) assert.NoError(t, err) rr := httptest.NewRecorder() metricsServ.Handler.ServeHTTP(rr, req) @@ -354,7 +370,7 @@ func assertMetricsPrinted(t *testing.T, expectedLines, body string) { if line == "" { continue } - assert.Contains(t, body, line, "expected metrics mismatch") + assert.Contains(t, body, line, fmt.Sprintf("expected metrics mismatch for line: %s", line)) } } @@ -391,7 +407,7 @@ argocd_app_reconcile_count{dest_server="https://localhost:6443",namespace="argoc fakeApp := newFakeApp(fakeApp) metricsServ.IncReconcile(fakeApp, 5*time.Second) - req, err := http.NewRequest("GET", "/metrics", nil) + req, err := http.NewRequest(http.MethodGet, "/metrics", nil) assert.NoError(t, err) rr := httptest.NewRecorder() metricsServ.Handler.ServeHTTP(rr, req) @@ -415,7 +431,7 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespace="argocd",phase="Succeeded",project="important-project"} 2 ` - req, err := http.NewRequest("GET", "/metrics", nil) + req, err := http.NewRequest(http.MethodGet, "/metrics", nil) assert.NoError(t, err) rr := httptest.NewRecorder() metricsServ.Handler.ServeHTTP(rr, req) @@ -426,7 +442,7 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa err = metricsServ.SetExpiration(time.Second) assert.NoError(t, err) time.Sleep(2 * time.Second) - req, err = http.NewRequest("GET", "/metrics", nil) + req, err = http.NewRequest(http.MethodGet, "/metrics", nil) assert.NoError(t, err) rr = httptest.NewRecorder() metricsServ.Handler.ServeHTTP(rr, req) @@ -437,3 +453,70 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa err = metricsServ.SetExpiration(time.Second) assert.Error(t, err) } + +func TestWorkqueueMetrics(t *testing.T) { + cancel, appLister := newFakeLister() + defer cancel() + metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}) + assert.NoError(t, err) + + expectedMetrics := ` +# TYPE workqueue_adds_total counter +workqueue_adds_total{name="test"} + +# TYPE workqueue_depth gauge +workqueue_depth{name="test"} + +# TYPE workqueue_longest_running_processor_seconds gauge +workqueue_longest_running_processor_seconds{name="test"} + +# TYPE workqueue_queue_duration_seconds histogram + +# TYPE workqueue_unfinished_work_seconds gauge +workqueue_unfinished_work_seconds{name="test"} + +# TYPE workqueue_work_duration_seconds histogram +` + workqueue.NewNamed("test") + + req, err := http.NewRequest(http.MethodGet, "/metrics", nil) + assert.NoError(t, err) + rr := httptest.NewRecorder() + metricsServ.Handler.ServeHTTP(rr, req) + assert.Equal(t, rr.Code, http.StatusOK) + body := rr.Body.String() + log.Println(body) + assertMetricsPrinted(t, expectedMetrics, body) +} + +func TestGoMetrics(t *testing.T) { + cancel, appLister := newFakeLister() + defer cancel() + metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}) + assert.NoError(t, err) + + expectedMetrics := ` +# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds_sum +go_gc_duration_seconds_count +# TYPE go_goroutines gauge +go_goroutines +# TYPE go_info gauge +go_info +# TYPE go_memstats_alloc_bytes gauge +go_memstats_alloc_bytes +# TYPE go_memstats_sys_bytes gauge +go_memstats_sys_bytes +# TYPE go_threads gauge +go_threads +` + + req, err := http.NewRequest(http.MethodGet, "/metrics", nil) + assert.NoError(t, err) + rr := httptest.NewRecorder() + metricsServ.Handler.ServeHTTP(rr, req) + assert.Equal(t, rr.Code, http.StatusOK) + body := rr.Body.String() + log.Println(body) + assertMetricsPrinted(t, expectedMetrics, body) +} diff --git a/controller/metrics/workqueue.go b/controller/metrics/workqueue.go deleted file mode 100644 index 2ef10685ee47d..0000000000000 --- a/controller/metrics/workqueue.go +++ /dev/null @@ -1,101 +0,0 @@ -package metrics - -import ( - "github.com/prometheus/client_golang/prometheus" - "k8s.io/client-go/util/workqueue" -) - -const ( - WorkQueueSubsystem = "workqueue" - DepthKey = "depth" - AddsKey = "adds_total" - QueueLatencyKey = "queue_duration_seconds" - WorkDurationKey = "work_duration_seconds" - UnfinishedWorkKey = "unfinished_work_seconds" - LongestRunningProcessorKey = "longest_running_processor_seconds" - RetriesKey = "retries_total" -) - -var ( - depth = prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Subsystem: WorkQueueSubsystem, - Name: DepthKey, - Help: "Current depth of workqueue", - }, []string{"name"}) - - adds = prometheus.NewCounterVec(prometheus.CounterOpts{ - Subsystem: WorkQueueSubsystem, - Name: AddsKey, - Help: "Total number of adds handled by workqueue", - }, []string{"name"}) - - latency = prometheus.NewHistogramVec(prometheus.HistogramOpts{ - Subsystem: WorkQueueSubsystem, - Name: QueueLatencyKey, - Help: "How long in seconds an item stays in workqueue before being requested", - Buckets: []float64{1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1, 5, 10, 15, 30, 60, 120, 180}, - }, []string{"name"}) - - workDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{ - Subsystem: WorkQueueSubsystem, - Name: WorkDurationKey, - Help: "How long in seconds processing an item from workqueue takes.", - Buckets: []float64{1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1, 5, 10, 15, 30, 60, 120, 180}, - }, []string{"name"}) - - unfinished = prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Subsystem: WorkQueueSubsystem, - Name: UnfinishedWorkKey, - Help: "How many seconds of work has been done that " + - "is in progress and hasn't been observed by work_duration. Large " + - "values indicate stuck threads. One can deduce the number of stuck " + - "threads by observing the rate at which this increases.", - }, []string{"name"}) - - longestRunningProcessor = prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Subsystem: WorkQueueSubsystem, - Name: LongestRunningProcessorKey, - Help: "How many seconds has the longest running " + - "processor for workqueue been running.", - }, []string{"name"}) - - retries = prometheus.NewCounterVec(prometheus.CounterOpts{ - Subsystem: WorkQueueSubsystem, - Name: RetriesKey, - Help: "Total number of retries handled by workqueue", - }, []string{"name"}) -) - -func init() { - workqueue.SetProvider(workqueueMetricsProvider{}) -} - -type workqueueMetricsProvider struct{} - -func (workqueueMetricsProvider) NewDepthMetric(name string) workqueue.GaugeMetric { - return depth.WithLabelValues(name) -} - -func (workqueueMetricsProvider) NewAddsMetric(name string) workqueue.CounterMetric { - return adds.WithLabelValues(name) -} - -func (workqueueMetricsProvider) NewLatencyMetric(name string) workqueue.HistogramMetric { - return latency.WithLabelValues(name) -} - -func (workqueueMetricsProvider) NewWorkDurationMetric(name string) workqueue.HistogramMetric { - return workDuration.WithLabelValues(name) -} - -func (workqueueMetricsProvider) NewUnfinishedWorkSecondsMetric(name string) workqueue.SettableGaugeMetric { - return unfinished.WithLabelValues(name) -} - -func (workqueueMetricsProvider) NewLongestRunningProcessorSecondsMetric(name string) workqueue.SettableGaugeMetric { - return longestRunningProcessor.WithLabelValues(name) -} - -func (workqueueMetricsProvider) NewRetriesMetric(name string) workqueue.CounterMetric { - return retries.WithLabelValues(name) -} diff --git a/controller/sharding/cache.go b/controller/sharding/cache.go new file mode 100644 index 0000000000000..2f3ffcbcb95c6 --- /dev/null +++ b/controller/sharding/cache.go @@ -0,0 +1,264 @@ +package sharding + +import ( + "sync" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/util/db" + log "github.com/sirupsen/logrus" +) + +type ClusterShardingCache interface { + Init(clusters *v1alpha1.ClusterList, apps *v1alpha1.ApplicationList) + Add(c *v1alpha1.Cluster) + Delete(clusterServer string) + Update(oldCluster *v1alpha1.Cluster, newCluster *v1alpha1.Cluster) + AddApp(a *v1alpha1.Application) + DeleteApp(a *v1alpha1.Application) + UpdateApp(a *v1alpha1.Application) + IsManagedCluster(c *v1alpha1.Cluster) bool + GetDistribution() map[string]int + GetAppDistribution() map[string]int +} + +type ClusterSharding struct { + Shard int + Replicas int + Shards map[string]int + Clusters map[string]*v1alpha1.Cluster + Apps map[string]*v1alpha1.Application + lock sync.RWMutex + getClusterShard DistributionFunction +} + +func NewClusterSharding(_ db.ArgoDB, shard, replicas int, shardingAlgorithm string) ClusterShardingCache { + log.Debugf("Processing clusters from shard %d: Using filter function: %s", shard, shardingAlgorithm) + clusterSharding := &ClusterSharding{ + Shard: shard, + Replicas: replicas, + Shards: make(map[string]int), + Clusters: make(map[string]*v1alpha1.Cluster), + Apps: make(map[string]*v1alpha1.Application), + } + distributionFunction := NoShardingDistributionFunction() + if replicas > 1 { + log.Debugf("Processing clusters from shard %d: Using filter function: %s", shard, shardingAlgorithm) + distributionFunction = GetDistributionFunction(clusterSharding.getClusterAccessor(), clusterSharding.getAppAccessor(), shardingAlgorithm, replicas) + } else { + log.Info("Processing all cluster shards") + } + clusterSharding.getClusterShard = distributionFunction + return clusterSharding +} + +// IsManagedCluster returns wheter or not the cluster should be processed by a given shard. +func (s *ClusterSharding) IsManagedCluster(c *v1alpha1.Cluster) bool { + s.lock.RLock() + defer s.lock.RUnlock() + if c == nil { // nil cluster (in-cluster) is always managed by current clusterShard + return true + } + clusterShard := 0 + if shard, ok := s.Shards[c.Server]; ok { + clusterShard = shard + } else { + log.Warnf("The cluster %s has no assigned shard.", c.Server) + } + log.Debugf("Checking if cluster %s with clusterShard %d should be processed by shard %d", c.Server, clusterShard, s.Shard) + return clusterShard == s.Shard +} + +func (sharding *ClusterSharding) Init(clusters *v1alpha1.ClusterList, apps *v1alpha1.ApplicationList) { + sharding.lock.Lock() + defer sharding.lock.Unlock() + newClusters := make(map[string]*v1alpha1.Cluster, len(clusters.Items)) + for _, c := range clusters.Items { + cluster := c + newClusters[c.Server] = &cluster + } + sharding.Clusters = newClusters + + newApps := make(map[string]*v1alpha1.Application, len(apps.Items)) + for i := range apps.Items { + app := apps.Items[i] + newApps[app.Name] = &app + } + sharding.Apps = newApps + sharding.updateDistribution() +} + +func (sharding *ClusterSharding) Add(c *v1alpha1.Cluster) { + sharding.lock.Lock() + defer sharding.lock.Unlock() + + old, ok := sharding.Clusters[c.Server] + sharding.Clusters[c.Server] = c + if !ok || hasShardingUpdates(old, c) { + sharding.updateDistribution() + } else { + log.Debugf("Skipping sharding distribution update. Cluster already added") + } +} + +func (sharding *ClusterSharding) Delete(clusterServer string) { + sharding.lock.Lock() + defer sharding.lock.Unlock() + if _, ok := sharding.Clusters[clusterServer]; ok { + delete(sharding.Clusters, clusterServer) + delete(sharding.Shards, clusterServer) + sharding.updateDistribution() + } +} + +func (sharding *ClusterSharding) Update(oldCluster *v1alpha1.Cluster, newCluster *v1alpha1.Cluster) { + sharding.lock.Lock() + defer sharding.lock.Unlock() + + if _, ok := sharding.Clusters[oldCluster.Server]; ok && oldCluster.Server != newCluster.Server { + delete(sharding.Clusters, oldCluster.Server) + delete(sharding.Shards, oldCluster.Server) + } + sharding.Clusters[newCluster.Server] = newCluster + if hasShardingUpdates(oldCluster, newCluster) { + sharding.updateDistribution() + } else { + log.Debugf("Skipping sharding distribution update. No relevant changes") + } +} + +func (sharding *ClusterSharding) GetDistribution() map[string]int { + sharding.lock.RLock() + defer sharding.lock.RUnlock() + shards := sharding.Shards + + distribution := make(map[string]int, len(shards)) + for k, v := range shards { + distribution[k] = v + } + return distribution +} + +func (sharding *ClusterSharding) updateDistribution() { + for k, c := range sharding.Clusters { + shard := 0 + if c.Shard != nil { + requestedShard := int(*c.Shard) + if requestedShard < sharding.Replicas { + shard = requestedShard + } else { + log.Warnf("Specified cluster shard (%d) for cluster: %s is greater than the number of available shard (%d). Using shard 0.", requestedShard, c.Server, sharding.Replicas) + } + } else { + shard = sharding.getClusterShard(c) + } + + existingShard, ok := sharding.Shards[k] + if ok && existingShard != shard { + log.Infof("Cluster %s has changed shard from %d to %d", k, existingShard, shard) + } else if !ok { + log.Infof("Cluster %s has been assigned to shard %d", k, shard) + } else { + log.Debugf("Cluster %s has not changed shard", k) + } + sharding.Shards[k] = shard + } +} + +// hasShardingUpdates returns true if the sharding distribution has explicitly changed +func hasShardingUpdates(old, new *v1alpha1.Cluster) bool { + if old == nil || new == nil { + return false + } + + // returns true if the cluster id has changed because some sharding algorithms depend on it. + if old.ID != new.ID { + return true + } + + if old.Server != new.Server { + return true + } + + // return false if the shard field has not been modified + if old.Shard == nil && new.Shard == nil { + return false + } + return old.Shard == nil || new.Shard == nil || int64(*old.Shard) != int64(*new.Shard) +} + +// A read lock should be acquired before calling getClusterAccessor. +func (d *ClusterSharding) getClusterAccessor() clusterAccessor { + return func() []*v1alpha1.Cluster { + // no need to lock, as this is only called from the updateDistribution function + clusters := make([]*v1alpha1.Cluster, 0, len(d.Clusters)) + for _, c := range d.Clusters { + clusters = append(clusters, c) + } + return clusters + } +} + +// A read lock should be acquired before calling getAppAccessor. +func (d *ClusterSharding) getAppAccessor() appAccessor { + return func() []*v1alpha1.Application { + apps := make([]*v1alpha1.Application, 0, len(d.Apps)) + for _, a := range d.Apps { + apps = append(apps, a) + } + return apps + } +} + +func (sharding *ClusterSharding) AddApp(a *v1alpha1.Application) { + sharding.lock.Lock() + defer sharding.lock.Unlock() + + _, ok := sharding.Apps[a.Name] + sharding.Apps[a.Name] = a + if !ok { + sharding.updateDistribution() + } else { + log.Debugf("Skipping sharding distribution update. App already added") + } +} + +func (sharding *ClusterSharding) DeleteApp(a *v1alpha1.Application) { + sharding.lock.Lock() + defer sharding.lock.Unlock() + if _, ok := sharding.Apps[a.Name]; ok { + delete(sharding.Apps, a.Name) + sharding.updateDistribution() + } +} + +func (sharding *ClusterSharding) UpdateApp(a *v1alpha1.Application) { + sharding.lock.Lock() + defer sharding.lock.Unlock() + + _, ok := sharding.Apps[a.Name] + sharding.Apps[a.Name] = a + if !ok { + sharding.updateDistribution() + } else { + log.Debugf("Skipping sharding distribution update. No relevant changes") + } +} + +// GetAppDistribution should be not be called from a DestributionFunction because +// it could cause a deadlock when updateDistribution is called. +func (sharding *ClusterSharding) GetAppDistribution() map[string]int { + sharding.lock.RLock() + clusters := sharding.Clusters + apps := sharding.Apps + sharding.lock.RUnlock() + + appDistribution := make(map[string]int, len(clusters)) + + for _, a := range apps { + if _, ok := appDistribution[a.Spec.Destination.Server]; !ok { + appDistribution[a.Spec.Destination.Server] = 0 + } + appDistribution[a.Spec.Destination.Server]++ + } + return appDistribution +} diff --git a/controller/sharding/cache_test.go b/controller/sharding/cache_test.go new file mode 100644 index 0000000000000..f7798c31e3608 --- /dev/null +++ b/controller/sharding/cache_test.go @@ -0,0 +1,511 @@ +package sharding + +import ( + "testing" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" + "github.com/stretchr/testify/assert" +) + +func setupTestSharding(shard int, replicas int) *ClusterSharding { + shardingAlgorithm := "legacy" // we are using the legacy algorithm as it is deterministic based on the cluster id which is easier to test + db := &dbmocks.ArgoDB{} + return NewClusterSharding(db, shard, replicas, shardingAlgorithm).(*ClusterSharding) +} + +func TestNewClusterSharding(t *testing.T) { + shard := 1 + replicas := 2 + sharding := setupTestSharding(shard, replicas) + + assert.NotNil(t, sharding) + assert.Equal(t, shard, sharding.Shard) + assert.Equal(t, replicas, sharding.Replicas) + assert.NotNil(t, sharding.Shards) + assert.NotNil(t, sharding.Clusters) +} + +func TestClusterSharding_Add(t *testing.T) { + shard := 1 + replicas := 2 + sharding := setupTestSharding(shard, replicas) + + clusterA := &v1alpha1.Cluster{ + ID: "2", + Server: "https://127.0.0.1:6443", + } + + sharding.Add(clusterA) + + clusterB := v1alpha1.Cluster{ + ID: "1", + Server: "https://kubernetes.default.svc", + } + + sharding.Add(&clusterB) + + distribution := sharding.GetDistribution() + + assert.Contains(t, sharding.Clusters, clusterA.Server) + assert.Contains(t, sharding.Clusters, clusterB.Server) + + clusterDistribution, ok := distribution[clusterA.Server] + assert.True(t, ok) + assert.Equal(t, 1, clusterDistribution) + + myClusterDistribution, ok := distribution[clusterB.Server] + assert.True(t, ok) + assert.Equal(t, 0, myClusterDistribution) + + assert.Equal(t, 2, len(distribution)) +} + +func TestClusterSharding_AddRoundRobin_Redistributes(t *testing.T) { + shard := 1 + replicas := 2 + + db := &dbmocks.ArgoDB{} + + sharding := NewClusterSharding(db, shard, replicas, "round-robin").(*ClusterSharding) + + clusterA := &v1alpha1.Cluster{ + ID: "1", + Server: "https://127.0.0.1:6443", + } + sharding.Add(clusterA) + + clusterB := v1alpha1.Cluster{ + ID: "3", + Server: "https://kubernetes.default.svc", + } + sharding.Add(&clusterB) + + distributionBefore := sharding.GetDistribution() + + assert.Contains(t, sharding.Clusters, clusterA.Server) + assert.Contains(t, sharding.Clusters, clusterB.Server) + + clusterDistributionA, ok := distributionBefore[clusterA.Server] + assert.True(t, ok) + assert.Equal(t, 0, clusterDistributionA) + + clusterDistributionB, ok := distributionBefore[clusterB.Server] + assert.True(t, ok) + assert.Equal(t, 1, clusterDistributionB) + + assert.Equal(t, 2, len(distributionBefore)) + + clusterC := v1alpha1.Cluster{ + ID: "2", + Server: "https://1.1.1.1", + } + sharding.Add(&clusterC) + + distributionAfter := sharding.GetDistribution() + + assert.Contains(t, sharding.Clusters, clusterA.Server) + assert.Contains(t, sharding.Clusters, clusterB.Server) + assert.Contains(t, sharding.Clusters, clusterC.Server) + + clusterDistributionA, ok = distributionAfter[clusterA.Server] + assert.True(t, ok) + assert.Equal(t, 0, clusterDistributionA) + + clusterDistributionC, ok := distributionAfter[clusterC.Server] + assert.True(t, ok) + assert.Equal(t, 1, clusterDistributionC) // will be assigned to shard 1 because the .ID is smaller then the "B" cluster + + clusterDistributionB, ok = distributionAfter[clusterB.Server] + assert.True(t, ok) + assert.Equal(t, 0, clusterDistributionB) // will be reassigned to shard 0 because the .ID is bigger then the "C" cluster +} + +func TestClusterSharding_Delete(t *testing.T) { + shard := 1 + replicas := 2 + sharding := setupTestSharding(shard, replicas) + + sharding.Init( + &v1alpha1.ClusterList{ + Items: []v1alpha1.Cluster{ + { + ID: "2", + Server: "https://127.0.0.1:6443", + }, + { + ID: "1", + Server: "https://kubernetes.default.svc", + }, + }, + }, + &v1alpha1.ApplicationList{ + Items: []v1alpha1.Application{ + createApp("app2", "https://127.0.0.1:6443"), + createApp("app1", "https://kubernetes.default.svc"), + }, + }, + ) + + sharding.Delete("https://kubernetes.default.svc") + distribution := sharding.GetDistribution() + assert.Equal(t, 1, len(distribution)) +} + +func TestClusterSharding_Update(t *testing.T) { + shard := 1 + replicas := 2 + sharding := setupTestSharding(shard, replicas) + + sharding.Init( + &v1alpha1.ClusterList{ + Items: []v1alpha1.Cluster{ + { + ID: "2", + Server: "https://127.0.0.1:6443", + }, + { + ID: "1", + Server: "https://kubernetes.default.svc", + }, + }, + }, + &v1alpha1.ApplicationList{ + Items: []v1alpha1.Application{ + createApp("app2", "https://127.0.0.1:6443"), + createApp("app1", "https://kubernetes.default.svc"), + }, + }, + ) + + distributionBefore := sharding.GetDistribution() + assert.Equal(t, 2, len(distributionBefore)) + + distributionA, ok := distributionBefore["https://kubernetes.default.svc"] + assert.True(t, ok) + assert.Equal(t, 0, distributionA) + + sharding.Update(&v1alpha1.Cluster{ + ID: "1", + Server: "https://kubernetes.default.svc", + }, &v1alpha1.Cluster{ + ID: "4", + Server: "https://kubernetes.default.svc", + }) + + distributionAfter := sharding.GetDistribution() + assert.Equal(t, 2, len(distributionAfter)) + + distributionA, ok = distributionAfter["https://kubernetes.default.svc"] + assert.True(t, ok) + assert.Equal(t, 1, distributionA) +} + +func TestClusterSharding_UpdateServerName(t *testing.T) { + shard := 1 + replicas := 2 + sharding := setupTestSharding(shard, replicas) + + sharding.Init( + &v1alpha1.ClusterList{ + Items: []v1alpha1.Cluster{ + { + ID: "2", + Server: "https://127.0.0.1:6443", + }, + { + ID: "1", + Server: "https://kubernetes.default.svc", + }, + }, + }, + &v1alpha1.ApplicationList{ + Items: []v1alpha1.Application{ + createApp("app2", "https://127.0.0.1:6443"), + createApp("app1", "https://kubernetes.default.svc"), + }, + }, + ) + + distributionBefore := sharding.GetDistribution() + assert.Equal(t, 2, len(distributionBefore)) + + distributionA, ok := distributionBefore["https://kubernetes.default.svc"] + assert.True(t, ok) + assert.Equal(t, 0, distributionA) + + sharding.Update(&v1alpha1.Cluster{ + ID: "1", + Server: "https://kubernetes.default.svc", + }, &v1alpha1.Cluster{ + ID: "1", + Server: "https://server2", + }) + + distributionAfter := sharding.GetDistribution() + assert.Equal(t, 2, len(distributionAfter)) + + _, ok = distributionAfter["https://kubernetes.default.svc"] + assert.False(t, ok) // the old server name should not be present anymore + + _, ok = distributionAfter["https://server2"] + assert.True(t, ok) // the new server name should be present +} + +func TestClusterSharding_IsManagedCluster(t *testing.T) { + replicas := 2 + sharding0 := setupTestSharding(0, replicas) + + sharding0.Init( + &v1alpha1.ClusterList{ + Items: []v1alpha1.Cluster{ + { + ID: "1", + Server: "https://kubernetes.default.svc", + }, + { + ID: "2", + Server: "https://127.0.0.1:6443", + }, + }, + }, + &v1alpha1.ApplicationList{ + Items: []v1alpha1.Application{ + createApp("app2", "https://127.0.0.1:6443"), + createApp("app1", "https://kubernetes.default.svc"), + }, + }, + ) + + assert.True(t, sharding0.IsManagedCluster(&v1alpha1.Cluster{ + ID: "1", + Server: "https://kubernetes.default.svc", + })) + + assert.False(t, sharding0.IsManagedCluster(&v1alpha1.Cluster{ + ID: "2", + Server: "https://127.0.0.1:6443", + })) + + sharding1 := setupTestSharding(1, replicas) + + sharding1.Init( + &v1alpha1.ClusterList{ + Items: []v1alpha1.Cluster{ + { + ID: "2", + Server: "https://127.0.0.1:6443", + }, + { + ID: "1", + Server: "https://kubernetes.default.svc", + }, + }, + }, + &v1alpha1.ApplicationList{ + Items: []v1alpha1.Application{ + createApp("app2", "https://127.0.0.1:6443"), + createApp("app1", "https://kubernetes.default.svc"), + }, + }, + ) + + assert.False(t, sharding1.IsManagedCluster(&v1alpha1.Cluster{ + ID: "1", + Server: "https://kubernetes.default.svc", + })) + + assert.True(t, sharding1.IsManagedCluster(&v1alpha1.Cluster{ + ID: "2", + Server: "https://127.0.0.1:6443", + })) + +} + +func TestClusterSharding_ClusterShardOfResourceShouldNotBeChanged(t *testing.T) { + shard := 1 + replicas := 2 + sharding := setupTestSharding(shard, replicas) + + Int64Ptr := func(i int64) *int64 { + return &i + } + + clusterWithNil := &v1alpha1.Cluster{ + ID: "2", + Server: "https://127.0.0.1:6443", + Shard: nil, + } + + clusterWithValue := &v1alpha1.Cluster{ + ID: "1", + Server: "https://kubernetes.default.svc", + Shard: Int64Ptr(1), + } + + clusterWithToBigValue := &v1alpha1.Cluster{ + ID: "3", + Server: "https://1.1.1.1", + Shard: Int64Ptr(999), // shard value is explicitly bigger than the number of replicas + } + + sharding.Init( + &v1alpha1.ClusterList{ + Items: []v1alpha1.Cluster{ + *clusterWithNil, + *clusterWithValue, + *clusterWithToBigValue, + }, + }, + &v1alpha1.ApplicationList{ + Items: []v1alpha1.Application{ + createApp("app2", "https://127.0.0.1:6443"), + createApp("app1", "https://kubernetes.default.svc"), + }, + }, + ) + distribution := sharding.GetDistribution() + assert.Equal(t, 3, len(distribution)) + + assert.Nil(t, sharding.Clusters[clusterWithNil.Server].Shard) + + assert.NotNil(t, sharding.Clusters[clusterWithValue.Server].Shard) + assert.Equal(t, int64(1), *sharding.Clusters[clusterWithValue.Server].Shard) + assert.Equal(t, 1, distribution[clusterWithValue.Server]) + + assert.NotNil(t, sharding.Clusters[clusterWithToBigValue.Server].Shard) + assert.Equal(t, int64(999), *sharding.Clusters[clusterWithToBigValue.Server].Shard) + assert.Equal(t, 0, distribution[clusterWithToBigValue.Server]) // will be assigned to shard 0 because the value is bigger than the number of replicas +} + +func TestHasShardingUpdates(t *testing.T) { + Int64Ptr := func(i int64) *int64 { + return &i + } + + testCases := []struct { + name string + old *v1alpha1.Cluster + new *v1alpha1.Cluster + expected bool + }{ + { + name: "No updates", + old: &v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Shard: Int64Ptr(1), + }, + new: &v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Shard: Int64Ptr(1), + }, + expected: false, + }, + { + name: "Updates", + old: &v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Shard: Int64Ptr(1), + }, + new: &v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Shard: Int64Ptr(2), + }, + expected: true, + }, + { + name: "Old is nil", + old: nil, + new: &v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Shard: Int64Ptr(2), + }, + expected: false, + }, + { + name: "New is nil", + old: &v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Shard: Int64Ptr(2), + }, + new: nil, + expected: false, + }, + { + name: "Both are nil", + old: nil, + new: nil, + expected: false, + }, + { + name: "Both shards are nil", + old: &v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Shard: nil, + }, + new: &v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Shard: nil, + }, + expected: false, + }, + { + name: "Old shard is nil", + old: &v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Shard: nil, + }, + new: &v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Shard: Int64Ptr(2), + }, + expected: true, + }, + { + name: "New shard is nil", + old: &v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Shard: Int64Ptr(2), + }, + new: &v1alpha1.Cluster{ + Server: "https://kubernetes.default.svc", + Shard: nil, + }, + expected: true, + }, + { + name: "Cluster ID has changed", + old: &v1alpha1.Cluster{ + ID: "1", + Server: "https://kubernetes.default.svc", + Shard: Int64Ptr(2), + }, + new: &v1alpha1.Cluster{ + ID: "2", + Server: "https://kubernetes.default.svc", + Shard: Int64Ptr(2), + }, + expected: true, + }, + { + name: "Server has changed", + old: &v1alpha1.Cluster{ + ID: "1", + Server: "https://server1", + Shard: Int64Ptr(2), + }, + new: &v1alpha1.Cluster{ + ID: "1", + Server: "https://server2", + Shard: Int64Ptr(2), + }, + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.expected, hasShardingUpdates(tc.old, tc.new)) + }) + } +} diff --git a/controller/sharding/sharding.go b/controller/sharding/sharding.go index 1c0615196bd06..e4af7010931c6 100644 --- a/controller/sharding/sharding.go +++ b/controller/sharding/sharding.go @@ -1,53 +1,424 @@ package sharding import ( + "context" "fmt" "hash/fnv" + "math" "os" + "sort" "strconv" "strings" + "time" + "encoding/json" + + "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + + "github.com/argoproj/argo-cd/v2/util/db" + "github.com/argoproj/argo-cd/v2/util/env" + "github.com/argoproj/argo-cd/v2/util/errors" + "github.com/argoproj/argo-cd/v2/util/settings" + log "github.com/sirupsen/logrus" + kubeerrors "k8s.io/apimachinery/pkg/api/errors" ) +// Make it overridable for testing +var osHostnameFunction = os.Hostname + +// Make it overridable for testing +var heartbeatCurrentTime = metav1.Now + +var ( + HeartbeatDuration = env.ParseNumFromEnv(common.EnvControllerHeartbeatTime, 10, 10, 60) + HeartbeatTimeout = 3 * HeartbeatDuration +) + +const ShardControllerMappingKey = "shardControllerMapping" + +type DistributionFunction func(c *v1alpha1.Cluster) int +type ClusterFilterFunction func(c *v1alpha1.Cluster) bool +type clusterAccessor func() []*v1alpha1.Cluster +type appAccessor func() []*v1alpha1.Application + +// shardApplicationControllerMapping stores the mapping of Shard Number to Application Controller in ConfigMap. +// It also stores the heartbeat of last synced time of the application controller. +type shardApplicationControllerMapping struct { + ShardNumber int + ControllerName string + HeartbeatTime metav1.Time +} + +// GetClusterFilter returns a ClusterFilterFunction which is a function taking a cluster as a parameter +// and returns wheter or not the cluster should be processed by a given shard. It calls the distributionFunction +// to determine which shard will process the cluster, and if the given shard is equal to the calculated shard +// the function will return true. +func GetClusterFilter(db db.ArgoDB, distributionFunction DistributionFunction, replicas, shard int) ClusterFilterFunction { + return func(c *v1alpha1.Cluster) bool { + clusterShard := 0 + if c != nil && c.Shard != nil { + requestedShard := int(*c.Shard) + if requestedShard < replicas { + clusterShard = requestedShard + } else { + log.Warnf("Specified cluster shard (%d) for cluster: %s is greater than the number of available shard. Assigning automatically.", requestedShard, c.Name) + } + } else { + clusterShard = distributionFunction(c) + } + return clusterShard == shard + } +} + +// GetDistributionFunction returns which DistributionFunction should be used based on the passed algorithm and +// the current datas. +func GetDistributionFunction(clusters clusterAccessor, apps appAccessor, shardingAlgorithm string, replicasCount int) DistributionFunction { + log.Debugf("Using filter function: %s", shardingAlgorithm) + distributionFunction := LegacyDistributionFunction(replicasCount) + switch shardingAlgorithm { + case common.RoundRobinShardingAlgorithm: + distributionFunction = RoundRobinDistributionFunction(clusters, replicasCount) + case common.LegacyShardingAlgorithm: + distributionFunction = LegacyDistributionFunction(replicasCount) + default: + log.Warnf("distribution type %s is not supported, defaulting to %s", shardingAlgorithm, common.DefaultShardingAlgorithm) + } + return distributionFunction +} + +// LegacyDistributionFunction returns a DistributionFunction using a stable distribution algorithm: +// for a given cluster the function will return the shard number based on the cluster id. This function +// is lightweight and can be distributed easily, however, it does not ensure an homogenous distribution as +// some shards may get assigned more clusters than others. It is the legacy function distribution that is +// kept for compatibility reasons +func LegacyDistributionFunction(replicas int) DistributionFunction { + return func(c *v1alpha1.Cluster) int { + if replicas == 0 { + log.Debugf("Replicas count is : %d, returning -1", replicas) + return -1 + } + if c == nil { + log.Debug("In-cluster: returning 0") + return 0 + } + // if Shard is manually set and the assigned value is lower than the number of replicas, + // then its value is returned otherwise it is the default calculated value + if c.Shard != nil && int(*c.Shard) < replicas { + return int(*c.Shard) + } + id := c.ID + log.Debugf("Calculating cluster shard for cluster id: %s", id) + if id == "" { + return 0 + } else { + h := fnv.New32a() + _, _ = h.Write([]byte(id)) + shard := int32(h.Sum32() % uint32(replicas)) + log.Debugf("Cluster with id=%s will be processed by shard %d", id, shard) + return int(shard) + } + } +} + +// RoundRobinDistributionFunction returns a DistributionFunction using an homogeneous distribution algorithm: +// for a given cluster the function will return the shard number based on the modulo of the cluster rank in +// the cluster's list sorted by uid on the shard number. +// This function ensures an homogenous distribution: each shards got assigned the same number of +// clusters +/-1 , but with the drawback of a reshuffling of clusters accross shards in case of some changes +// in the cluster list + +func RoundRobinDistributionFunction(clusters clusterAccessor, replicas int) DistributionFunction { + return func(c *v1alpha1.Cluster) int { + if replicas > 0 { + if c == nil { // in-cluster does not necessarly have a secret assigned. So we are receiving a nil cluster here. + return 0 + } + // if Shard is manually set and the assigned value is lower than the number of replicas, + // then its value is returned otherwise it is the default calculated value + if c.Shard != nil && int(*c.Shard) < replicas { + return int(*c.Shard) + } else { + clusterIndexdByClusterIdMap := createClusterIndexByClusterIdMap(clusters) + clusterIndex, ok := clusterIndexdByClusterIdMap[c.ID] + if !ok { + log.Warnf("Cluster with id=%s not found in cluster map.", c.ID) + return -1 + } + shard := int(clusterIndex % replicas) + log.Debugf("Cluster with id=%s will be processed by shard %d", c.ID, shard) + return shard + } + } + log.Warnf("The number of replicas (%d) is lower than 1", replicas) + return -1 + } +} + +// NoShardingDistributionFunction returns a DistributionFunction that will process all cluster by shard 0 +// the function is created for API compatibility purposes and is not supposed to be activated. +func NoShardingDistributionFunction() DistributionFunction { + return func(c *v1alpha1.Cluster) int { return 0 } +} + +// InferShard extracts the shard index based on its hostname. func InferShard() (int, error) { - hostname, err := os.Hostname() + hostname, err := osHostnameFunction() if err != nil { - return 0, err + return -1, 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) + log.Warnf("hostname should end with shard number separated by '-' but got: %s", hostname) + return 0, nil } 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) + log.Warnf("hostname should end with shard number separated by '-' but got: %s", hostname) + return 0, nil } - return shard, nil + return int(shard), nil +} + +func getSortedClustersList(getCluster clusterAccessor) []*v1alpha1.Cluster { + clusters := getCluster() + sort.Slice(clusters, func(i, j int) bool { + return clusters[i].ID < clusters[j].ID + }) + return clusters } -// GetShardByID calculates cluster shard as `clusterSecret.UID % replicas count` -func GetShardByID(id string, replicas int) int { - if id == "" { - return 0 +func createClusterIndexByClusterIdMap(getCluster clusterAccessor) map[string]int { + clusters := getSortedClustersList(getCluster) + log.Debugf("ClustersList has %d items", len(clusters)) + clusterById := make(map[string]*v1alpha1.Cluster) + clusterIndexedByClusterId := make(map[string]int) + for i, cluster := range clusters { + log.Debugf("Adding cluster with id=%s and name=%s to cluster's map", cluster.ID, cluster.Name) + clusterById[cluster.ID] = cluster + clusterIndexedByClusterId[cluster.ID] = i + } + return clusterIndexedByClusterId +} + +// GetOrUpdateShardFromConfigMap finds the shard number from the shard mapping configmap. If the shard mapping configmap does not exist, +// the function creates the shard mapping configmap. +// The function takes the shard number from the environment variable (default value -1, if not set) and passes it to this function. +// If the shard value passed to this function is -1, that is, the shard was not set as an environment variable, +// we default the shard number to 0 for computing the default config map. +func GetOrUpdateShardFromConfigMap(kubeClient kubernetes.Interface, settingsMgr *settings.SettingsManager, replicas, shard int) (int, error) { + hostname, err := osHostnameFunction() + if err != nil { + return -1, err + } + + // fetch the shard mapping configMap + shardMappingCM, err := kubeClient.CoreV1().ConfigMaps(settingsMgr.GetNamespace()).Get(context.Background(), common.ArgoCDAppControllerShardConfigMapName, metav1.GetOptions{}) + + if err != nil { + if !kubeerrors.IsNotFound(err) { + return -1, fmt.Errorf("error getting sharding config map: %s", err) + } + log.Infof("shard mapping configmap %s not found. Creating default shard mapping configmap.", common.ArgoCDAppControllerShardConfigMapName) + + // if the shard is not set as an environment variable, set the default value of shard to 0 for generating default CM + if shard == -1 { + shard = 0 + } + shardMappingCM, err = generateDefaultShardMappingCM(settingsMgr.GetNamespace(), hostname, replicas, shard) + if err != nil { + return -1, fmt.Errorf("error generating default shard mapping configmap %s", err) + } + if _, err = kubeClient.CoreV1().ConfigMaps(settingsMgr.GetNamespace()).Create(context.Background(), shardMappingCM, metav1.CreateOptions{}); err != nil { + return -1, fmt.Errorf("error creating shard mapping configmap %s", err) + } + // return 0 as the controller is assigned to shard 0 while generating default shard mapping ConfigMap + return shard, nil } else { - h := fnv.New32a() - _, _ = h.Write([]byte(id)) - return int(h.Sum32() % uint32(replicas)) + // Identify the available shard and update the ConfigMap + data := shardMappingCM.Data[ShardControllerMappingKey] + var shardMappingData []shardApplicationControllerMapping + err := json.Unmarshal([]byte(data), &shardMappingData) + if err != nil { + return -1, fmt.Errorf("error unmarshalling shard config map data: %s", err) + } + + shard, shardMappingData := getOrUpdateShardNumberForController(shardMappingData, hostname, replicas, shard) + updatedShardMappingData, err := json.Marshal(shardMappingData) + if err != nil { + return -1, fmt.Errorf("error marshalling data of shard mapping ConfigMap: %s", err) + } + shardMappingCM.Data[ShardControllerMappingKey] = string(updatedShardMappingData) + + _, err = kubeClient.CoreV1().ConfigMaps(settingsMgr.GetNamespace()).Update(context.Background(), shardMappingCM, metav1.UpdateOptions{}) + if err != nil { + return -1, err + } + return shard, nil } } -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) +// getOrUpdateShardNumberForController takes list of shardApplicationControllerMapping and performs computation to find the matching or empty shard number +func getOrUpdateShardNumberForController(shardMappingData []shardApplicationControllerMapping, hostname string, replicas, shard int) (int, []shardApplicationControllerMapping) { + + // if current length of shardMappingData in shard mapping configMap is less than the number of replicas, + // create additional empty entries for missing shard numbers in shardMappingDataconfigMap + if len(shardMappingData) < replicas { + // generate extra default mappings + for currentShard := len(shardMappingData); currentShard < replicas; currentShard++ { + shardMappingData = append(shardMappingData, shardApplicationControllerMapping{ + ShardNumber: currentShard, + }) + } + } + + // if current length of shardMappingData in shard mapping configMap is more than the number of replicas, + // we replace the config map with default config map and let controllers self assign the new shard to itself + if len(shardMappingData) > replicas { + shardMappingData = getDefaultShardMappingData(replicas) + } + + if shard != -1 && shard < replicas { + log.Debugf("update heartbeat for shard %d", shard) + for i := range shardMappingData { + shardMapping := shardMappingData[i] + if shardMapping.ShardNumber == shard { + log.Debugf("Shard found. Updating heartbeat!!") + shardMapping.ControllerName = hostname + shardMapping.HeartbeatTime = heartbeatCurrentTime() + shardMappingData[i] = shardMapping + break } } - return clusterShard == shard + } else { + // find the matching shard with assigned controllerName + for i := range shardMappingData { + shardMapping := shardMappingData[i] + if shardMapping.ControllerName == hostname { + log.Debugf("Shard matched. Updating heartbeat!!") + shard = int(shardMapping.ShardNumber) + shardMapping.HeartbeatTime = heartbeatCurrentTime() + shardMappingData[i] = shardMapping + break + } + } + } + + // at this point, we have still not found a shard with matching hostname. + // So, find a shard with either no controller assigned or assigned controller + // with heartbeat past threshold + if shard == -1 { + for i := range shardMappingData { + shardMapping := shardMappingData[i] + if (shardMapping.ControllerName == "") || (metav1.Now().After(shardMapping.HeartbeatTime.Add(time.Duration(HeartbeatTimeout) * time.Second))) { + shard = int(shardMapping.ShardNumber) + log.Debugf("Empty shard found %d", shard) + shardMapping.ControllerName = hostname + shardMapping.HeartbeatTime = heartbeatCurrentTime() + shardMappingData[i] = shardMapping + break + } + } + } + return shard, shardMappingData +} + +// generateDefaultShardMappingCM creates a default shard mapping configMap. Assigns current controller to shard 0. +func generateDefaultShardMappingCM(namespace, hostname string, replicas, shard int) (*v1.ConfigMap, error) { + + shardingCM := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.ArgoCDAppControllerShardConfigMapName, + Namespace: namespace, + }, + Data: map[string]string{}, + } + + shardMappingData := getDefaultShardMappingData(replicas) + + // if shard is not assigned to a controller, we use shard 0 + if shard == -1 || shard > replicas { + shard = 0 + } + shardMappingData[shard].ControllerName = hostname + shardMappingData[shard].HeartbeatTime = heartbeatCurrentTime() + + data, err := json.Marshal(shardMappingData) + if err != nil { + return nil, fmt.Errorf("error generating default ConfigMap: %s", err) + } + shardingCM.Data[ShardControllerMappingKey] = string(data) + + return shardingCM, nil +} + +func getDefaultShardMappingData(replicas int) []shardApplicationControllerMapping { + shardMappingData := make([]shardApplicationControllerMapping, 0) + + for i := 0; i < replicas; i++ { + mapping := shardApplicationControllerMapping{ + ShardNumber: i, + } + shardMappingData = append(shardMappingData, mapping) + } + return shardMappingData +} + +func GetClusterSharding(kubeClient kubernetes.Interface, settingsMgr *settings.SettingsManager, shardingAlgorithm string, enableDynamicClusterDistribution bool) (ClusterShardingCache, error) { + var replicasCount int + if enableDynamicClusterDistribution { + applicationControllerName := env.StringFromEnv(common.EnvAppControllerName, common.DefaultApplicationControllerName) + appControllerDeployment, err := kubeClient.AppsV1().Deployments(settingsMgr.GetNamespace()).Get(context.Background(), applicationControllerName, metav1.GetOptions{}) + + // if app controller deployment is not found when dynamic cluster distribution is enabled error out + if err != nil { + return nil, fmt.Errorf("(dynamic cluster distribution) failed to get app controller deployment: %v", err) + } + + if appControllerDeployment != nil && appControllerDeployment.Spec.Replicas != nil { + replicasCount = int(*appControllerDeployment.Spec.Replicas) + } else { + return nil, fmt.Errorf("(dynamic cluster distribution) failed to get app controller deployment replica count") + } + + } else { + replicasCount = env.ParseNumFromEnv(common.EnvControllerReplicas, 0, 0, math.MaxInt32) + } + shardNumber := env.ParseNumFromEnv(common.EnvControllerShard, -1, -math.MaxInt32, math.MaxInt32) + if replicasCount > 1 { + // check for shard mapping using configmap if application-controller is a deployment + // else use existing logic to infer shard from pod name if application-controller is a statefulset + if enableDynamicClusterDistribution { + var err error + // retry 3 times if we find a conflict while updating shard mapping configMap. + // If we still see conflicts after the retries, wait for next iteration of heartbeat process. + for i := 0; i <= common.AppControllerHeartbeatUpdateRetryCount; i++ { + shardNumber, err = GetOrUpdateShardFromConfigMap(kubeClient, settingsMgr, replicasCount, shardNumber) + if err != nil && !kubeerrors.IsConflict(err) { + err = fmt.Errorf("unable to get shard due to error updating the sharding config map: %s", err) + break + } + log.Warnf("conflict when getting shard from shard mapping configMap. Retrying (%d/3)", i) + } + errors.CheckError(err) + } else { + if shardNumber < 0 { + var err error + shardNumber, err = InferShard() + errors.CheckError(err) + } + if shardNumber > replicasCount { + log.Warnf("Calculated shard number %d is greated than the number of replicas count. Defaulting to 0", shardNumber) + shardNumber = 0 + } + } + } else { + log.Info("Processing all cluster shards") + shardNumber = 0 } + db := db.NewDB(settingsMgr.GetNamespace(), settingsMgr, kubeClient) + return NewClusterSharding(db, shardNumber, replicasCount, shardingAlgorithm), nil } diff --git a/controller/sharding/sharding_test.go b/controller/sharding/sharding_test.go index dc27726f8a6fa..1c338aac5f271 100644 --- a/controller/sharding/sharding_test.go +++ b/controller/sharding/sharding_test.go @@ -1,29 +1,954 @@ package sharding import ( + "context" + "encoding/json" + "errors" + "fmt" + "os" + "strconv" "testing" + "time" + "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - + dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" + "github.com/argoproj/argo-cd/v2/util/settings" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + appsv1 "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + kubefake "k8s.io/client-go/kubernetes/fake" + "sigs.k8s.io/yaml" ) 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)) + db := &dbmocks.ArgoDB{} + replicasCount := 1 + db.On("GetApplicationControllerReplicas").Return(replicasCount) + assert.Equal(t, 0, LegacyDistributionFunction(replicasCount)(&v1alpha1.Cluster{ID: "1"})) + assert.Equal(t, 0, LegacyDistributionFunction(replicasCount)(&v1alpha1.Cluster{ID: "2"})) + assert.Equal(t, 0, LegacyDistributionFunction(replicasCount)(&v1alpha1.Cluster{ID: "3"})) + assert.Equal(t, 0, LegacyDistributionFunction(replicasCount)(&v1alpha1.Cluster{ID: "4"})) } func TestGetShardByID_EmptyID(t *testing.T) { - shard := GetShardByID("", 10) + db := &dbmocks.ArgoDB{} + replicasCount := 1 + db.On("GetApplicationControllerReplicas").Return(replicasCount) + distributionFunction := LegacyDistributionFunction + shard := distributionFunction(replicasCount)(&v1alpha1.Cluster{}) 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"})) +func TestGetShardByID_NoReplicas(t *testing.T) { + db := &dbmocks.ArgoDB{} + db.On("GetApplicationControllerReplicas").Return(0) + distributionFunction := LegacyDistributionFunction + shard := distributionFunction(0)(&v1alpha1.Cluster{}) + assert.Equal(t, -1, shard) +} + +func TestGetShardByID_NoReplicasUsingHashDistributionFunction(t *testing.T) { + db := &dbmocks.ArgoDB{} + db.On("GetApplicationControllerReplicas").Return(0) + distributionFunction := LegacyDistributionFunction + shard := distributionFunction(0)(&v1alpha1.Cluster{}) + assert.Equal(t, -1, shard) +} + +func TestGetShardByID_NoReplicasUsingHashDistributionFunctionWithClusters(t *testing.T) { + clusters, db, cluster1, cluster2, cluster3, cluster4, cluster5 := createTestClusters() + // Test with replicas set to 0 + db.On("GetApplicationControllerReplicas").Return(0) + t.Setenv(common.EnvControllerShardingAlgorithm, common.RoundRobinShardingAlgorithm) + distributionFunction := RoundRobinDistributionFunction(clusters, 0) + assert.Equal(t, -1, distributionFunction(nil)) + assert.Equal(t, -1, distributionFunction(&cluster1)) + assert.Equal(t, -1, distributionFunction(&cluster2)) + assert.Equal(t, -1, distributionFunction(&cluster3)) + assert.Equal(t, -1, distributionFunction(&cluster4)) + assert.Equal(t, -1, distributionFunction(&cluster5)) +} + +func TestGetClusterFilterDefault(t *testing.T) { + //shardIndex := 1 // ensuring that a shard with index 1 will process all the clusters with an "even" id (2,4,6,...) + clusterAccessor, _, cluster1, cluster2, cluster3, cluster4, _ := createTestClusters() + os.Unsetenv(common.EnvControllerShardingAlgorithm) + replicasCount := 2 + distributionFunction := RoundRobinDistributionFunction(clusterAccessor, replicasCount) + assert.Equal(t, 0, distributionFunction(nil)) + assert.Equal(t, 0, distributionFunction(&cluster1)) + assert.Equal(t, 1, distributionFunction(&cluster2)) + assert.Equal(t, 0, distributionFunction(&cluster3)) + assert.Equal(t, 1, distributionFunction(&cluster4)) +} + +func TestGetClusterFilterLegacy(t *testing.T) { + //shardIndex := 1 // ensuring that a shard with index 1 will process all the clusters with an "even" id (2,4,6,...) + clusterAccessor, db, cluster1, cluster2, cluster3, cluster4, _ := createTestClusters() + replicasCount := 2 + db.On("GetApplicationControllerReplicas").Return(replicasCount) + t.Setenv(common.EnvControllerShardingAlgorithm, common.LegacyShardingAlgorithm) + distributionFunction := RoundRobinDistributionFunction(clusterAccessor, replicasCount) + assert.Equal(t, 0, distributionFunction(nil)) + assert.Equal(t, 0, distributionFunction(&cluster1)) + assert.Equal(t, 1, distributionFunction(&cluster2)) + assert.Equal(t, 0, distributionFunction(&cluster3)) + assert.Equal(t, 1, distributionFunction(&cluster4)) +} + +func TestGetClusterFilterUnknown(t *testing.T) { + clusterAccessor, db, cluster1, cluster2, cluster3, cluster4, _ := createTestClusters() + appAccessor, _, _, _, _, _ := createTestApps() + // Test with replicas set to 0 + t.Setenv(common.EnvControllerReplicas, "2") + os.Unsetenv(common.EnvControllerShardingAlgorithm) + t.Setenv(common.EnvControllerShardingAlgorithm, "unknown") + replicasCount := 2 + db.On("GetApplicationControllerReplicas").Return(replicasCount) + distributionFunction := GetDistributionFunction(clusterAccessor, appAccessor, "unknown", replicasCount) + assert.Equal(t, 0, distributionFunction(nil)) + assert.Equal(t, 0, distributionFunction(&cluster1)) + assert.Equal(t, 1, distributionFunction(&cluster2)) + assert.Equal(t, 0, distributionFunction(&cluster3)) + assert.Equal(t, 1, distributionFunction(&cluster4)) +} + +func TestLegacyGetClusterFilterWithFixedShard(t *testing.T) { + //shardIndex := 1 // ensuring that a shard with index 1 will process all the clusters with an "even" id (2,4,6,...) + t.Setenv(common.EnvControllerReplicas, "5") + clusterAccessor, db, cluster1, cluster2, cluster3, cluster4, _ := createTestClusters() + appAccessor, _, _, _, _, _ := createTestApps() + replicasCount := 5 + db.On("GetApplicationControllerReplicas").Return(replicasCount) + filter := GetDistributionFunction(clusterAccessor, appAccessor, common.DefaultShardingAlgorithm, replicasCount) + assert.Equal(t, 0, filter(nil)) + assert.Equal(t, 4, filter(&cluster1)) + assert.Equal(t, 1, filter(&cluster2)) + assert.Equal(t, 2, filter(&cluster3)) + assert.Equal(t, 2, filter(&cluster4)) + + var fixedShard int64 = 4 + cluster5 := &v1alpha1.Cluster{ID: "5", Shard: &fixedShard} + clusterAccessor = getClusterAccessor([]v1alpha1.Cluster{cluster1, cluster2, cluster2, cluster4, *cluster5}) + filter = GetDistributionFunction(clusterAccessor, appAccessor, common.DefaultShardingAlgorithm, replicasCount) + assert.Equal(t, int(fixedShard), filter(cluster5)) + + fixedShard = 1 + cluster5.Shard = &fixedShard + clusterAccessor = getClusterAccessor([]v1alpha1.Cluster{cluster1, cluster2, cluster2, cluster4, *cluster5}) + filter = GetDistributionFunction(clusterAccessor, appAccessor, common.DefaultShardingAlgorithm, replicasCount) + assert.Equal(t, int(fixedShard), filter(&v1alpha1.Cluster{ID: "4", Shard: &fixedShard})) +} + +func TestRoundRobinGetClusterFilterWithFixedShard(t *testing.T) { + //shardIndex := 1 // ensuring that a shard with index 1 will process all the clusters with an "even" id (2,4,6,...) + t.Setenv(common.EnvControllerReplicas, "4") + clusterAccessor, db, cluster1, cluster2, cluster3, cluster4, _ := createTestClusters() + appAccessor, _, _, _, _, _ := createTestApps() + replicasCount := 4 + db.On("GetApplicationControllerReplicas").Return(replicasCount) + + filter := GetDistributionFunction(clusterAccessor, appAccessor, common.RoundRobinShardingAlgorithm, replicasCount) + assert.Equal(t, filter(nil), 0) + assert.Equal(t, filter(&cluster1), 0) + assert.Equal(t, filter(&cluster2), 1) + assert.Equal(t, filter(&cluster3), 2) + assert.Equal(t, filter(&cluster4), 3) + + // a cluster with a fixed shard should be processed by the specified exact + // same shard unless the specified shard index is greater than the number of replicas. + var fixedShard int64 = 1 + cluster5 := v1alpha1.Cluster{Name: "cluster5", ID: "5", Shard: &fixedShard} + clusters := []v1alpha1.Cluster{cluster1, cluster2, cluster3, cluster4, cluster5} + clusterAccessor = getClusterAccessor(clusters) + filter = GetDistributionFunction(clusterAccessor, appAccessor, common.RoundRobinShardingAlgorithm, replicasCount) + assert.Equal(t, int(fixedShard), filter(&cluster5)) + + fixedShard = 1 + cluster5 = v1alpha1.Cluster{Name: "cluster5", ID: "5", Shard: &fixedShard} + clusters = []v1alpha1.Cluster{cluster1, cluster2, cluster3, cluster4, cluster5} + clusterAccessor = getClusterAccessor(clusters) + filter = GetDistributionFunction(clusterAccessor, appAccessor, common.RoundRobinShardingAlgorithm, replicasCount) + assert.Equal(t, int(fixedShard), filter(&v1alpha1.Cluster{Name: "cluster4", ID: "4", Shard: &fixedShard})) +} + +func TestGetShardByIndexModuloReplicasCountDistributionFunction2(t *testing.T) { + clusters, db, cluster1, cluster2, cluster3, cluster4, cluster5 := createTestClusters() + + t.Run("replicas set to 1", func(t *testing.T) { + replicasCount := 1 + db.On("GetApplicationControllerReplicas").Return(replicasCount).Once() + distributionFunction := RoundRobinDistributionFunction(clusters, replicasCount) + assert.Equal(t, 0, distributionFunction(nil)) + assert.Equal(t, 0, distributionFunction(&cluster1)) + assert.Equal(t, 0, distributionFunction(&cluster2)) + assert.Equal(t, 0, distributionFunction(&cluster3)) + assert.Equal(t, 0, distributionFunction(&cluster4)) + assert.Equal(t, 0, distributionFunction(&cluster5)) + }) + + t.Run("replicas set to 2", func(t *testing.T) { + replicasCount := 2 + db.On("GetApplicationControllerReplicas").Return(replicasCount).Once() + distributionFunction := RoundRobinDistributionFunction(clusters, replicasCount) + assert.Equal(t, 0, distributionFunction(nil)) + assert.Equal(t, 0, distributionFunction(&cluster1)) + assert.Equal(t, 1, distributionFunction(&cluster2)) + assert.Equal(t, 0, distributionFunction(&cluster3)) + assert.Equal(t, 1, distributionFunction(&cluster4)) + assert.Equal(t, 0, distributionFunction(&cluster5)) + }) + + t.Run("replicas set to 3", func(t *testing.T) { + replicasCount := 3 + db.On("GetApplicationControllerReplicas").Return(replicasCount).Once() + distributionFunction := RoundRobinDistributionFunction(clusters, replicasCount) + assert.Equal(t, 0, distributionFunction(nil)) + assert.Equal(t, 0, distributionFunction(&cluster1)) + assert.Equal(t, 1, distributionFunction(&cluster2)) + assert.Equal(t, 2, distributionFunction(&cluster3)) + assert.Equal(t, 0, distributionFunction(&cluster4)) + assert.Equal(t, 1, distributionFunction(&cluster5)) + }) +} + +func TestGetShardByIndexModuloReplicasCountDistributionFunctionWhenClusterNumberIsHigh(t *testing.T) { + // Unit test written to evaluate the cost of calling db.ListCluster on every call of distributionFunction + // Doing that allows to accept added and removed clusters on the fly. + // Initial tests where showing that under 1024 clusters, execution time was around 400ms + // and for 4096 clusters, execution time was under 9s + // The other implementation was giving almost linear time of 400ms up to 10'000 clusters + clusterPointers := []*v1alpha1.Cluster{} + for i := 0; i < 2048; i++ { + cluster := createCluster(fmt.Sprintf("cluster-%d", i), fmt.Sprintf("%d", i)) + clusterPointers = append(clusterPointers, &cluster) + } + replicasCount := 2 + t.Setenv(common.EnvControllerReplicas, strconv.Itoa(replicasCount)) + _, db, _, _, _, _, _ := createTestClusters() + clusterAccessor := func() []*v1alpha1.Cluster { return clusterPointers } + db.On("GetApplicationControllerReplicas").Return(replicasCount) + distributionFunction := RoundRobinDistributionFunction(clusterAccessor, replicasCount) + for i, c := range clusterPointers { + assert.Equal(t, i%2, distributionFunction(c)) + } +} + +func TestGetShardByIndexModuloReplicasCountDistributionFunctionWhenClusterIsAddedAndRemoved(t *testing.T) { + db := dbmocks.ArgoDB{} + cluster1 := createCluster("cluster1", "1") + cluster2 := createCluster("cluster2", "2") + cluster3 := createCluster("cluster3", "3") + cluster4 := createCluster("cluster4", "4") + cluster5 := createCluster("cluster5", "5") + cluster6 := createCluster("cluster6", "6") + + clusters := []v1alpha1.Cluster{cluster1, cluster2, cluster3, cluster4, cluster5} + clusterAccessor := getClusterAccessor(clusters) + + clusterList := &v1alpha1.ClusterList{Items: []v1alpha1.Cluster{cluster1, cluster2, cluster3, cluster4, cluster5}} + db.On("ListClusters", mock.Anything).Return(clusterList, nil) + // Test with replicas set to 2 + replicasCount := 2 + db.On("GetApplicationControllerReplicas").Return(replicasCount) + distributionFunction := RoundRobinDistributionFunction(clusterAccessor, replicasCount) + assert.Equal(t, 0, distributionFunction(nil)) + assert.Equal(t, 0, distributionFunction(&cluster1)) + assert.Equal(t, 1, distributionFunction(&cluster2)) + assert.Equal(t, 0, distributionFunction(&cluster3)) + assert.Equal(t, 1, distributionFunction(&cluster4)) + assert.Equal(t, 0, distributionFunction(&cluster5)) + assert.Equal(t, -1, distributionFunction(&cluster6)) // as cluster6 is not in the DB, this one should not have a shard assigned + + // Now, the database knows cluster6. Shard should be assigned a proper shard + clusterList.Items = append(clusterList.Items, cluster6) + distributionFunction = RoundRobinDistributionFunction(getClusterAccessor(clusterList.Items), replicasCount) + assert.Equal(t, 1, distributionFunction(&cluster6)) + + // Now, we remove the last added cluster, it should be unassigned as well + clusterList.Items = clusterList.Items[:len(clusterList.Items)-1] + distributionFunction = RoundRobinDistributionFunction(getClusterAccessor(clusterList.Items), replicasCount) + assert.Equal(t, -1, distributionFunction(&cluster6)) +} + +func TestGetShardByIndexModuloReplicasCountDistributionFunction(t *testing.T) { + clusters, db, cluster1, cluster2, _, _, _ := createTestClusters() + replicasCount := 2 + db.On("GetApplicationControllerReplicas").Return(replicasCount) + distributionFunction := RoundRobinDistributionFunction(clusters, replicasCount) + + // Test that the function returns the correct shard for cluster1 and cluster2 + expectedShardForCluster1 := 0 + expectedShardForCluster2 := 1 + shardForCluster1 := distributionFunction(&cluster1) + shardForCluster2 := distributionFunction(&cluster2) + + if shardForCluster1 != expectedShardForCluster1 { + t.Errorf("Expected shard for cluster1 to be %d but got %d", expectedShardForCluster1, shardForCluster1) + } + if shardForCluster2 != expectedShardForCluster2 { + t.Errorf("Expected shard for cluster2 to be %d but got %d", expectedShardForCluster2, shardForCluster2) + } +} + +func TestInferShard(t *testing.T) { + // Override the os.Hostname function to return a specific hostname for testing + defer func() { osHostnameFunction = os.Hostname }() + + expectedShard := 3 + osHostnameFunction = func() (string, error) { return "example-shard-3", nil } + actualShard, _ := InferShard() + assert.Equal(t, expectedShard, actualShard) + + osHostnameError := errors.New("cannot resolve hostname") + osHostnameFunction = func() (string, error) { return "exampleshard", osHostnameError } + _, err := InferShard() + assert.NotNil(t, err) + assert.Equal(t, err, osHostnameError) + + osHostnameFunction = func() (string, error) { return "exampleshard", nil } + _, err = InferShard() + assert.Nil(t, err) + + osHostnameFunction = func() (string, error) { return "example-shard", nil } + _, err = InferShard() + assert.Nil(t, err) +} + +func createTestClusters() (clusterAccessor, *dbmocks.ArgoDB, v1alpha1.Cluster, v1alpha1.Cluster, v1alpha1.Cluster, v1alpha1.Cluster, v1alpha1.Cluster) { + db := dbmocks.ArgoDB{} + cluster1 := createCluster("cluster1", "1") + cluster2 := createCluster("cluster2", "2") + cluster3 := createCluster("cluster3", "3") + cluster4 := createCluster("cluster4", "4") + cluster5 := createCluster("cluster5", "5") + + clusters := []v1alpha1.Cluster{cluster1, cluster2, cluster3, cluster4, cluster5} + + db.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{ + cluster1, cluster2, cluster3, cluster4, cluster5, + }}, nil) + return getClusterAccessor(clusters), &db, cluster1, cluster2, cluster3, cluster4, cluster5 +} + +func getClusterAccessor(clusters []v1alpha1.Cluster) clusterAccessor { + // Convert the array to a slice of pointers + clusterPointers := getClusterPointers(clusters) + clusterAccessor := func() []*v1alpha1.Cluster { return clusterPointers } + return clusterAccessor +} + +func getClusterPointers(clusters []v1alpha1.Cluster) []*v1alpha1.Cluster { + var clusterPointers []*v1alpha1.Cluster + for i := range clusters { + clusterPointers = append(clusterPointers, &clusters[i]) + } + return clusterPointers +} + +func createCluster(name string, id string) v1alpha1.Cluster { + cluster := v1alpha1.Cluster{ + Name: name, + ID: id, + Server: "https://kubernetes.default.svc?" + id, + } + return cluster +} + +func Test_getDefaultShardMappingData(t *testing.T) { + expectedData := []shardApplicationControllerMapping{ + { + ShardNumber: 0, + ControllerName: "", + }, { + ShardNumber: 1, + ControllerName: "", + }, + } + + shardMappingData := getDefaultShardMappingData(2) + assert.Equal(t, expectedData, shardMappingData) +} + +func Test_generateDefaultShardMappingCM_NoPredefinedShard(t *testing.T) { + replicas := 2 + expectedTime := metav1.Now() + defer func() { osHostnameFunction = os.Hostname }() + defer func() { heartbeatCurrentTime = metav1.Now }() + + expectedMapping := []shardApplicationControllerMapping{ + { + ShardNumber: 0, + ControllerName: "test-example", + HeartbeatTime: expectedTime, + }, { + ShardNumber: 1, + }, + } + + expectedMappingCM, err := json.Marshal(expectedMapping) + assert.NoError(t, err) + + expectedShadingCM := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.ArgoCDAppControllerShardConfigMapName, + Namespace: "test", + }, + Data: map[string]string{ + "shardControllerMapping": string(expectedMappingCM), + }, + } + heartbeatCurrentTime = func() metav1.Time { return expectedTime } + osHostnameFunction = func() (string, error) { return "test-example", nil } + shardingCM, err := generateDefaultShardMappingCM("test", "test-example", replicas, -1) + assert.NoError(t, err) + assert.Equal(t, expectedShadingCM, shardingCM) + +} + +func Test_generateDefaultShardMappingCM_PredefinedShard(t *testing.T) { + replicas := 2 + expectedTime := metav1.Now() + defer func() { osHostnameFunction = os.Hostname }() + defer func() { heartbeatCurrentTime = metav1.Now }() + + expectedMapping := []shardApplicationControllerMapping{ + { + ShardNumber: 0, + }, { + ShardNumber: 1, + ControllerName: "test-example", + HeartbeatTime: expectedTime, + }, + } + + expectedMappingCM, err := json.Marshal(expectedMapping) + assert.NoError(t, err) + + expectedShadingCM := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.ArgoCDAppControllerShardConfigMapName, + Namespace: "test", + }, + Data: map[string]string{ + "shardControllerMapping": string(expectedMappingCM), + }, + } + heartbeatCurrentTime = func() metav1.Time { return expectedTime } + osHostnameFunction = func() (string, error) { return "test-example", nil } + shardingCM, err := generateDefaultShardMappingCM("test", "test-example", replicas, 1) + assert.NoError(t, err) + assert.Equal(t, expectedShadingCM, shardingCM) + +} + +func Test_getOrUpdateShardNumberForController(t *testing.T) { + expectedTime := metav1.Now() + + testCases := []struct { + name string + shardApplicationControllerMapping []shardApplicationControllerMapping + hostname string + replicas int + shard int + expectedShard int + expectedShardMappingData []shardApplicationControllerMapping + }{ + { + name: "length of shard mapping less than number of replicas - Existing controller", + shardApplicationControllerMapping: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: metav1.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + }, + }, + hostname: "test-example", + replicas: 2, + shard: -1, + expectedShard: 0, + expectedShardMappingData: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, { + ControllerName: "", + ShardNumber: 1, + HeartbeatTime: metav1.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + }, + }, + }, + { + name: "length of shard mapping less than number of replicas - New controller", + shardApplicationControllerMapping: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, + }, + hostname: "test-example-1", + replicas: 2, + shard: -1, + expectedShard: 1, + expectedShardMappingData: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, { + ControllerName: "test-example-1", + ShardNumber: 1, + HeartbeatTime: expectedTime, + }, + }, + }, + { + name: "length of shard mapping more than number of replicas", + shardApplicationControllerMapping: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, { + ControllerName: "test-example-1", + ShardNumber: 1, + HeartbeatTime: expectedTime, + }, + }, + hostname: "test-example", + replicas: 1, + shard: -1, + expectedShard: 0, + expectedShardMappingData: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, + }, + }, + { + name: "shard number is pre-specified and length of shard mapping less than number of replicas - Existing controller", + shardApplicationControllerMapping: []shardApplicationControllerMapping{ + { + ControllerName: "test-example-1", + ShardNumber: 1, + HeartbeatTime: metav1.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + }, { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, + }, + hostname: "test-example-1", + replicas: 2, + shard: 1, + expectedShard: 1, + expectedShardMappingData: []shardApplicationControllerMapping{ + { + ControllerName: "test-example-1", + ShardNumber: 1, + HeartbeatTime: expectedTime, + }, { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, + }, + }, + { + name: "shard number is pre-specified and length of shard mapping less than number of replicas - New controller", + shardApplicationControllerMapping: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, + }, + hostname: "test-example-1", + replicas: 2, + shard: 1, + expectedShard: 1, + expectedShardMappingData: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, { + ControllerName: "test-example-1", + ShardNumber: 1, + HeartbeatTime: expectedTime, + }, + }, + }, + { + name: "shard number is pre-specified and length of shard mapping more than number of replicas", + shardApplicationControllerMapping: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, { + ControllerName: "test-example-1", + ShardNumber: 1, + HeartbeatTime: expectedTime, + }, { + ControllerName: "test-example-2", + ShardNumber: 2, + HeartbeatTime: expectedTime, + }, + }, + hostname: "test-example", + replicas: 2, + shard: 1, + expectedShard: 1, + expectedShardMappingData: []shardApplicationControllerMapping{ + { + ControllerName: "", + ShardNumber: 0, + HeartbeatTime: metav1.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + }, { + ControllerName: "test-example", + ShardNumber: 1, + HeartbeatTime: expectedTime, + }, + }, + }, + { + name: "updating heartbeat", + shardApplicationControllerMapping: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, { + ControllerName: "test-example-1", + ShardNumber: 1, + HeartbeatTime: metav1.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + }, + }, + hostname: "test-example-1", + replicas: 2, + shard: -1, + expectedShard: 1, + expectedShardMappingData: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, { + ControllerName: "test-example-1", + ShardNumber: 1, + HeartbeatTime: expectedTime, + }, + }, + }, + { + name: "updating heartbeat - shard pre-defined", + shardApplicationControllerMapping: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, { + ControllerName: "test-example-1", + ShardNumber: 1, + HeartbeatTime: metav1.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + }, + }, + hostname: "test-example-1", + replicas: 2, + shard: 1, + expectedShard: 1, + expectedShardMappingData: []shardApplicationControllerMapping{ + { + ControllerName: "test-example", + ShardNumber: 0, + HeartbeatTime: expectedTime, + }, { + ControllerName: "test-example-1", + ShardNumber: 1, + HeartbeatTime: expectedTime, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + defer func() { osHostnameFunction = os.Hostname }() + heartbeatCurrentTime = func() metav1.Time { return expectedTime } + shard, shardMappingData := getOrUpdateShardNumberForController(tc.shardApplicationControllerMapping, tc.hostname, tc.replicas, tc.shard) + assert.Equal(t, tc.expectedShard, shard) + assert.Equal(t, tc.expectedShardMappingData, shardMappingData) + }) + } +} + +func TestGetClusterSharding(t *testing.T) { + IntPtr := func(i int32) *int32 { + return &i + } + + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.DefaultApplicationControllerName, + Namespace: "argocd", + }, + Spec: appsv1.DeploymentSpec{ + Replicas: IntPtr(1), + }, + } + + deploymentMultiReplicas := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-application-controller-multi-replicas", + Namespace: "argocd", + }, + Spec: appsv1.DeploymentSpec{ + Replicas: IntPtr(3), + }, + } + + objects := append([]runtime.Object{}, deployment, deploymentMultiReplicas) + kubeclientset := kubefake.NewSimpleClientset(objects...) + + settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "argocd", settings.WithRepoOrClusterChangedHandler(func() { + })) + + testCases := []struct { + name string + useDynamicSharding bool + envsSetter func(t *testing.T) + cleanup func() + expectedShard int + expectedReplicas int + expectedErr error + }{ + { + name: "Default sharding with statefulset", + envsSetter: func(t *testing.T) { + t.Setenv(common.EnvControllerReplicas, "1") + }, + cleanup: func() {}, + useDynamicSharding: false, + expectedShard: 0, + expectedReplicas: 1, + expectedErr: nil, + }, + { + name: "Default sharding with deployment", + envsSetter: func(t *testing.T) { + t.Setenv(common.EnvAppControllerName, common.DefaultApplicationControllerName) + }, + cleanup: func() {}, + useDynamicSharding: true, + expectedShard: 0, + expectedReplicas: 1, + expectedErr: nil, + }, + { + name: "Default sharding with deployment and multiple replicas", + envsSetter: func(t *testing.T) { + t.Setenv(common.EnvAppControllerName, "argocd-application-controller-multi-replicas") + }, + cleanup: func() {}, + useDynamicSharding: true, + expectedShard: 0, + expectedReplicas: 3, + expectedErr: nil, + }, + { + name: "Statefulset multiple replicas", + envsSetter: func(t *testing.T) { + t.Setenv(common.EnvControllerReplicas, "3") + osHostnameFunction = func() (string, error) { return "example-shard-3", nil } + }, + cleanup: func() { + osHostnameFunction = os.Hostname + }, + useDynamicSharding: false, + expectedShard: 3, + expectedReplicas: 3, + expectedErr: nil, + }, + { + name: "Explicit shard with statefulset and 1 replica", + envsSetter: func(t *testing.T) { + t.Setenv(common.EnvControllerReplicas, "1") + t.Setenv(common.EnvControllerShard, "3") + }, + cleanup: func() {}, + useDynamicSharding: false, + expectedShard: 0, + expectedReplicas: 1, + expectedErr: nil, + }, + { + name: "Explicit shard with statefulset and 2 replica - and to high shard", + envsSetter: func(t *testing.T) { + t.Setenv(common.EnvControllerReplicas, "2") + t.Setenv(common.EnvControllerShard, "3") + }, + cleanup: func() {}, + useDynamicSharding: false, + expectedShard: 0, + expectedReplicas: 2, + expectedErr: nil, + }, + { + name: "Explicit shard with statefulset and 2 replica", + envsSetter: func(t *testing.T) { + t.Setenv(common.EnvControllerReplicas, "2") + t.Setenv(common.EnvControllerShard, "1") + }, + cleanup: func() {}, + useDynamicSharding: false, + expectedShard: 1, + expectedReplicas: 2, + expectedErr: nil, + }, + { + name: "Explicit shard with deployment", + envsSetter: func(t *testing.T) { + t.Setenv(common.EnvControllerShard, "3") + }, + cleanup: func() {}, + useDynamicSharding: true, + expectedShard: 0, + expectedReplicas: 1, + expectedErr: nil, + }, + { + name: "Explicit shard with deployment and multiple replicas will read from configmap", + envsSetter: func(t *testing.T) { + t.Setenv(common.EnvAppControllerName, "argocd-application-controller-multi-replicas") + t.Setenv(common.EnvControllerShard, "3") + }, + cleanup: func() {}, + useDynamicSharding: true, + expectedShard: 0, + expectedReplicas: 3, + expectedErr: nil, + }, + { + name: "Dynamic sharding but missing deployment", + envsSetter: func(t *testing.T) { + t.Setenv(common.EnvAppControllerName, "missing-deployment") + }, + cleanup: func() {}, + useDynamicSharding: true, + expectedShard: 0, + expectedReplicas: 1, + expectedErr: fmt.Errorf("(dynamic cluster distribution) failed to get app controller deployment: deployments.apps \"missing-deployment\" not found"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + tc.envsSetter(t) + defer tc.cleanup() + shardingCache, err := GetClusterSharding(kubeclientset, settingsMgr, "round-robin", tc.useDynamicSharding) + + if shardingCache != nil { + clusterSharding := shardingCache.(*ClusterSharding) + assert.Equal(t, tc.expectedShard, clusterSharding.Shard) + assert.Equal(t, tc.expectedReplicas, clusterSharding.Replicas) + } + + if tc.expectedErr != nil { + if err != nil { + assert.Equal(t, tc.expectedErr.Error(), err.Error()) + } else { + t.Errorf("Expected error %v but got nil", tc.expectedErr) + } + } else { + assert.Nil(t, err) + } + }) + } +} + +func TestAppAwareCache(t *testing.T) { + _, db, cluster1, cluster2, cluster3, cluster4, cluster5 := createTestClusters() + _, app1, app2, app3, app4, app5 := createTestApps() + + clusterSharding := NewClusterSharding(db, 0, 1, "legacy") + + clusterList := &v1alpha1.ClusterList{Items: []v1alpha1.Cluster{cluster1, cluster2, cluster3, cluster4, cluster5}} + appList := &v1alpha1.ApplicationList{Items: []v1alpha1.Application{app1, app2, app3, app4, app5}} + clusterSharding.Init(clusterList, appList) + + appDistribution := clusterSharding.GetAppDistribution() + + assert.Equal(t, 2, appDistribution["cluster1"]) + assert.Equal(t, 2, appDistribution["cluster2"]) + assert.Equal(t, 1, appDistribution["cluster3"]) + + app6 := createApp("app6", "cluster4") + clusterSharding.AddApp(&app6) + + app1Update := createApp("app1", "cluster2") + clusterSharding.UpdateApp(&app1Update) + + clusterSharding.DeleteApp(&app3) + + appDistribution = clusterSharding.GetAppDistribution() + + assert.Equal(t, 1, appDistribution["cluster1"]) + assert.Equal(t, 2, appDistribution["cluster2"]) + assert.Equal(t, 1, appDistribution["cluster3"]) + assert.Equal(t, 1, appDistribution["cluster4"]) +} + +func createTestApps() (appAccessor, v1alpha1.Application, v1alpha1.Application, v1alpha1.Application, v1alpha1.Application, v1alpha1.Application) { + app1 := createApp("app1", "cluster1") + app2 := createApp("app2", "cluster1") + app3 := createApp("app3", "cluster2") + app4 := createApp("app4", "cluster2") + app5 := createApp("app5", "cluster3") + + apps := []v1alpha1.Application{app1, app2, app3, app4, app5} + + return getAppAccessor(apps), app1, app2, app3, app4, app5 +} + +func getAppAccessor(apps []v1alpha1.Application) appAccessor { + // Convert the array to a slice of pointers + appPointers := getAppPointers(apps) + appAccessor := func() []*v1alpha1.Application { return appPointers } + return appAccessor +} + +func getAppPointers(apps []v1alpha1.Application) []*v1alpha1.Application { + var appPointers []*v1alpha1.Application + for i := range apps { + appPointers = append(appPointers, &apps[i]) + } + return appPointers +} + +func createApp(name string, server string) v1alpha1.Application { + var testApp = ` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: ` + name + ` +spec: + destination: + server: ` + server + ` +` + + var app v1alpha1.Application + err := yaml.Unmarshal([]byte(testApp), &app) + if err != nil { + panic(err) + } + return app } diff --git a/controller/sharding/shuffle_test.go b/controller/sharding/shuffle_test.go new file mode 100644 index 0000000000000..1cca783a2afe9 --- /dev/null +++ b/controller/sharding/shuffle_test.go @@ -0,0 +1,86 @@ +package sharding + +import ( + "fmt" + "math" + "strconv" + "testing" + + "github.com/argoproj/argo-cd/v2/common" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestLargeShuffle(t *testing.T) { + t.Skip() + db := dbmocks.ArgoDB{} + clusterList := &v1alpha1.ClusterList{Items: []v1alpha1.Cluster{}} + for i := 0; i < math.MaxInt/4096; i += 256 { + //fmt.Fprintf(os.Stdout, "%d", i) + cluster := createCluster(fmt.Sprintf("cluster-%d", i), fmt.Sprintf("%d", i)) + clusterList.Items = append(clusterList.Items, cluster) + } + db.On("ListClusters", mock.Anything).Return(clusterList, nil) + clusterAccessor := getClusterAccessor(clusterList.Items) + // Test with replicas set to 256 + replicasCount := 256 + t.Setenv(common.EnvControllerReplicas, strconv.Itoa(replicasCount)) + distributionFunction := RoundRobinDistributionFunction(clusterAccessor, replicasCount) + for i, c := range clusterList.Items { + assert.Equal(t, i%2567, distributionFunction(&c)) + } + +} + +func TestShuffle(t *testing.T) { + t.Skip() + db := dbmocks.ArgoDB{} + cluster1 := createCluster("cluster1", "10") + cluster2 := createCluster("cluster2", "20") + cluster3 := createCluster("cluster3", "30") + cluster4 := createCluster("cluster4", "40") + cluster5 := createCluster("cluster5", "50") + cluster6 := createCluster("cluster6", "60") + cluster25 := createCluster("cluster6", "25") + + clusterList := &v1alpha1.ClusterList{Items: []v1alpha1.Cluster{cluster1, cluster2, cluster3, cluster4, cluster5, cluster6}} + db.On("ListClusters", mock.Anything).Return(clusterList, nil) + clusterAccessor := getClusterAccessor(clusterList.Items) + // Test with replicas set to 3 + t.Setenv(common.EnvControllerReplicas, "3") + replicasCount := 3 + distributionFunction := RoundRobinDistributionFunction(clusterAccessor, replicasCount) + assert.Equal(t, 0, distributionFunction(nil)) + assert.Equal(t, 0, distributionFunction(&cluster1)) + assert.Equal(t, 1, distributionFunction(&cluster2)) + assert.Equal(t, 2, distributionFunction(&cluster3)) + assert.Equal(t, 0, distributionFunction(&cluster4)) + assert.Equal(t, 1, distributionFunction(&cluster5)) + assert.Equal(t, 2, distributionFunction(&cluster6)) + + // Now, we remove cluster1, it should be unassigned, and all the other should be resuffled + clusterList.Items = Remove(clusterList.Items, 0) + assert.Equal(t, -1, distributionFunction(&cluster1)) + assert.Equal(t, 0, distributionFunction(&cluster2)) + assert.Equal(t, 1, distributionFunction(&cluster3)) + assert.Equal(t, 2, distributionFunction(&cluster4)) + assert.Equal(t, 0, distributionFunction(&cluster5)) + assert.Equal(t, 1, distributionFunction(&cluster6)) + + // Now, we add a cluster with an id=25 so it will be placed right after cluster2 + clusterList.Items = append(clusterList.Items, cluster25) + assert.Equal(t, -1, distributionFunction(&cluster1)) + assert.Equal(t, 0, distributionFunction(&cluster2)) + assert.Equal(t, 1, distributionFunction(&cluster25)) + assert.Equal(t, 2, distributionFunction(&cluster3)) + assert.Equal(t, 0, distributionFunction(&cluster4)) + assert.Equal(t, 1, distributionFunction(&cluster5)) + assert.Equal(t, 2, distributionFunction(&cluster6)) + +} + +func Remove(slice []v1alpha1.Cluster, s int) []v1alpha1.Cluster { + return append(slice[:s], slice[s+1:]...) +} diff --git a/controller/state.go b/controller/state.go index b9eb784e89023..704411558669b 100644 --- a/controller/state.go +++ b/controller/state.go @@ -3,11 +3,15 @@ package controller import ( "context" "encoding/json" + "errors" "fmt" "reflect" "strings" + goSync "sync" "time" + v1 "k8s.io/api/core/v1" + "github.com/argoproj/gitops-engine/pkg/diff" "github.com/argoproj/gitops-engine/pkg/health" "github.com/argoproj/gitops-engine/pkg/sync" @@ -27,7 +31,6 @@ import ( statecache "github.com/argoproj/argo-cd/v2/controller/cache" "github.com/argoproj/argo-cd/v2/controller/metrics" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/util/argo" @@ -40,6 +43,10 @@ import ( "github.com/argoproj/argo-cd/v2/util/stats" ) +var ( + CompareStateRepoError = errors.New("failed to get repo objects") +) + type resourceInfoProviderStub struct { } @@ -62,8 +69,9 @@ type managedResource struct { // AppStateManager defines methods which allow to compare application spec and actual application state. type AppStateManager interface { - CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localObjects []string, hasMultipleSources bool) *comparisonResult + CompareAppState(app *v1alpha1.Application, project *v1alpha1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localObjects []string, hasMultipleSources bool) (*comparisonResult, error) SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState) + GetRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, error) } // comparisonResult holds the state of an application after the reconciliation @@ -78,8 +86,9 @@ type comparisonResult struct { // appSourceTypes stores the SourceType for each application source under sources field appSourceTypes []v1alpha1.ApplicationSourceType // timings maps phases of comparison to the duration it took to complete (for statistical purposes) - timings map[string]time.Duration - diffResultList *diff.DiffResultList + timings map[string]time.Duration + diffResultList *diff.DiffResultList + hasPostDeleteHooks bool } func (res *comparisonResult) GetSyncStatus() *v1alpha1.SyncStatus { @@ -105,66 +114,64 @@ type appStateManager struct { statusRefreshTimeout time.Duration resourceTracking argo.ResourceTracking persistResourceHealth bool + repoErrorCache goSync.Map + repoErrorGracePeriod time.Duration + serverSideDiff bool } -func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, map[*v1alpha1.ApplicationSource]*apiclient.ManifestResponse, error) { - +// GetRepoObjs will generate the manifests for the given application delegating the +// task to the repo-server. It returns the list of generated manifests as unstructured +// objects. It also returns the full response from all calls to the repo server as the +// second argument. +func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, error) { ts := stats.NewTimingStats() helmRepos, err := m.db.ListHelmRepositories(context.Background()) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to list Helm repositories: %w", err) } permittedHelmRepos, err := argo.GetPermittedRepos(proj, helmRepos) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to get permitted Helm repositories for project %q: %w", proj.Name, err) } ts.AddCheckpoint("repo_ms") helmRepositoryCredentials, err := m.db.GetAllHelmRepositoryCredentials(context.Background()) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to get Helm credentials: %w", err) } permittedHelmCredentials, err := argo.GetPermittedReposCredentials(proj, helmRepositoryCredentials) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to get permitted Helm credentials for project %q: %w", proj.Name, err) } - plugins, err := m.settingsMgr.GetConfigManagementPlugins() - if err != nil { - return nil, nil, err - } enabledSourceTypes, err := m.settingsMgr.GetEnabledSourceTypes() if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to get enabled source types: %w", err) } ts.AddCheckpoint("plugins_ms") - tools := make([]*appv1.ConfigManagementPlugin, len(plugins)) - for i := range plugins { - tools[i] = &plugins[i] - } kustomizeSettings, err := m.settingsMgr.GetKustomizeSettings() if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to get Kustomize settings: %w", err) } helmOptions, err := m.settingsMgr.GetHelmSettings() if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to get Helm settings: %w", err) } ts.AddCheckpoint("build_options_ms") serverVersion, apiResources, err := m.liveStateCache.GetVersionsInfo(app.Spec.Destination.Server) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to get cluster version for cluster %q: %w", app.Spec.Destination.Server, err) } conn, repoClient, err := m.repoClientset.NewRepoServerClient() if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to connect to repo server: %w", err) } defer io.Close(conn) - manifestInfoMap := make(map[*v1alpha1.ApplicationSource]*apiclient.ManifestResponse) + manifestInfos := make([]*apiclient.ManifestResponse, 0) targetObjs := make([]*unstructured.Unstructured, 0) // Store the map of all sources having ref field into a map for applications with sources field @@ -180,11 +187,11 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alp ts.AddCheckpoint("helm_ms") repo, err := m.db.GetRepository(context.Background(), source.RepoURL) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to get repo %q: %w", source.RepoURL, err) } kustomizeOptions, err := kustomizeSettings.GetOptions(source) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to get Kustomize options for source %d of %d: %w", i+1, len(sources), err) } ts.AddCheckpoint("version_ms") @@ -199,7 +206,6 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alp AppName: app.InstanceName(m.namespace), Namespace: app.Spec.Destination.Namespace, ApplicationSource: &source, - Plugins: tools, KustomizeOptions: kustomizeOptions, KubeVersion: serverVersion, ApiVersions: argo.APIResourcesToStrings(apiResources, true), @@ -210,25 +216,21 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alp HelmOptions: helmOptions, HasMultipleSources: app.Spec.HasMultipleSources(), RefSources: refSources, + ProjectName: proj.Name, + ProjectSourceRepos: proj.Spec.SourceRepos, }) if err != nil { - return nil, nil, err - } - - // GenerateManifest can return empty ManifestResponse without error if app has multiple sources - // and if any of the source does not have path and chart field not specified. - // In that scenario, we continue to the next source - if app.Spec.HasMultipleSources() && len(manifestInfo.Manifests) == 0 { - continue + return nil, nil, fmt.Errorf("failed to generate manifest for source %d of %d: %w", i+1, len(sources), err) } targetObj, err := unmarshalManifests(manifestInfo.Manifests) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to unmarshal manifests for source %d of %d: %w", i+1, len(sources), err) } targetObjs = append(targetObjs, targetObj...) - manifestInfoMap[&source] = manifestInfo + + manifestInfos = append(manifestInfos, manifestInfo) } ts.AddCheckpoint("unmarshal_ms") @@ -237,8 +239,8 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alp logCtx = logCtx.WithField(k, v.Milliseconds()) } logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds()) - logCtx.Info("getRepoObjs stats") - return targetObjs, manifestInfoMap, nil + logCtx.Info("GetRepoObjs stats") + return targetObjs, manifestInfos, nil } func unmarshalManifests(manifests []string) ([]*unstructured.Unstructured, error) { @@ -282,8 +284,8 @@ func DeduplicateTargetObjects( for key, targets := range targetByKey { if len(targets) > 1 { now := metav1.Now() - conditions = append(conditions, appv1.ApplicationCondition{ - Type: appv1.ApplicationConditionRepeatedResourceWarning, + conditions = append(conditions, v1alpha1.ApplicationCondition{ + Type: v1alpha1.ApplicationConditionRepeatedResourceWarning, Message: fmt.Sprintf("Resource %s appeared %d times among application resources.", key.String(), len(targets)), LastTransitionTime: &now, }) @@ -314,9 +316,9 @@ func (m *appStateManager) getComparisonSettings() (string, map[string]v1alpha1.R // verifyGnuPGSignature verifies the result of a GnuPG operation for a given git // revision. -func verifyGnuPGSignature(revision string, project *appv1.AppProject, manifestInfo *apiclient.ManifestResponse) []appv1.ApplicationCondition { +func verifyGnuPGSignature(revision string, project *v1alpha1.AppProject, manifestInfo *apiclient.ManifestResponse) []v1alpha1.ApplicationCondition { now := metav1.Now() - conditions := make([]appv1.ApplicationCondition, 0) + conditions := make([]v1alpha1.ApplicationCondition, 0) // We need to have some data in the verification result to parse, otherwise there was no signature if manifestInfo.VerifyResult != "" { verifyResult := gpg.ParseGitCommitVerification(manifestInfo.VerifyResult) @@ -351,10 +353,14 @@ func verifyGnuPGSignature(revision string, project *appv1.AppProject, manifestIn return conditions } +func isManagedNamespace(ns *unstructured.Unstructured, app *v1alpha1.Application) bool { + return ns != nil && ns.GetKind() == kubeutil.NamespaceKind && ns.GetName() == app.Spec.Destination.Namespace && app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.ManagedNamespaceMetadata != nil +} + // CompareAppState compares application git state to the live app state, using the specified // revision and supplied source. If revision or overrides are empty, then compares against // revision and overrides in the app spec. -func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localManifests []string, hasMultipleSources bool) *comparisonResult { +func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1alpha1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localManifests []string, hasMultipleSources bool) (*comparisonResult, error) { ts := stats.NewTimingStats() appLabelKey, resourceOverrides, resFilter, err := m.getComparisonSettings() @@ -365,21 +371,21 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap if hasMultipleSources { return &comparisonResult{ syncStatus: &v1alpha1.SyncStatus{ - ComparedTo: appv1.ComparedTo{Destination: app.Spec.Destination, Sources: sources}, - Status: appv1.SyncStatusCodeUnknown, + ComparedTo: v1alpha1.ComparedTo{Destination: app.Spec.Destination, Sources: sources, IgnoreDifferences: app.Spec.IgnoreDifferences}, + Status: v1alpha1.SyncStatusCodeUnknown, Revisions: revisions, }, - healthStatus: &appv1.HealthStatus{Status: health.HealthStatusUnknown}, - } + healthStatus: &v1alpha1.HealthStatus{Status: health.HealthStatusUnknown}, + }, nil } else { return &comparisonResult{ syncStatus: &v1alpha1.SyncStatus{ - ComparedTo: appv1.ComparedTo{Source: sources[0], Destination: app.Spec.Destination}, - Status: appv1.SyncStatusCodeUnknown, + ComparedTo: v1alpha1.ComparedTo{Source: sources[0], Destination: app.Spec.Destination, IgnoreDifferences: app.Spec.IgnoreDifferences}, + Status: v1alpha1.SyncStatusCodeUnknown, Revision: revisions[0], }, - healthStatus: &appv1.HealthStatus{Status: health.HealthStatusUnknown}, - } + healthStatus: &v1alpha1.HealthStatus{Status: health.HealthStatusUnknown}, + }, nil } } @@ -399,7 +405,8 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap var targetObjs []*unstructured.Unstructured now := metav1.Now() - var manifestInfoMap map[*v1alpha1.ApplicationSource]*apiclient.ManifestResponse + var manifestInfos []*apiclient.ManifestResponse + targetNsExists := false if len(localManifests) == 0 { // If the length of revisions is not same as the length of sources, @@ -411,11 +418,26 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap } } - targetObjs, manifestInfoMap, err = m.getRepoObjs(app, sources, appLabelKey, revisions, noCache, noRevisionCache, verifySignature, project) + targetObjs, manifestInfos, err = m.GetRepoObjs(app, sources, appLabelKey, revisions, noCache, noRevisionCache, verifySignature, project) if err != nil { targetObjs = make([]*unstructured.Unstructured, 0) - conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) + msg := fmt.Sprintf("Failed to load target state: %s", err.Error()) + conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now}) + if firstSeen, ok := m.repoErrorCache.Load(app.Name); ok { + if time.Since(firstSeen.(time.Time)) <= m.repoErrorGracePeriod && !noRevisionCache { + // if first seen is less than grace period and it's not a Level 3 comparison, + // ignore error and short circuit + logCtx.Debugf("Ignoring repo error %v, already encountered error in grace period", err.Error()) + return nil, CompareStateRepoError + } + } else if !noRevisionCache { + logCtx.Debugf("Ignoring repo error %v, new occurrence", err.Error()) + m.repoErrorCache.Store(app.Name, time.Now()) + return nil, CompareStateRepoError + } failedToLoadObjs = true + } else { + m.repoErrorCache.Delete(app.Name) } } else { // Prevent applying local manifests for now when signature verification is enabled @@ -429,14 +451,13 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap targetObjs, err = unmarshalManifests(localManifests) if err != nil { targetObjs = make([]*unstructured.Unstructured, 0) - conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) + msg := fmt.Sprintf("Failed to load local manifests: %s", err.Error()) + conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now}) failedToLoadObjs = true } } // empty out manifestInfoMap - for as := range manifestInfoMap { - delete(manifestInfoMap, as) - } + manifestInfos = make([]*apiclient.ManifestResponse, 0) } ts.AddCheckpoint("git_ms") @@ -447,7 +468,8 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap } targetObjs, dedupConditions, err := DeduplicateTargetObjects(app.Spec.Destination.Namespace, targetObjs, infoProvider) if err != nil { - conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) + msg := fmt.Sprintf("Failed to deduplicate target state: %s", err.Error()) + conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now}) } conditions = append(conditions, dedupConditions...) for i := len(targetObjs) - 1; i >= 0; i-- { @@ -461,26 +483,39 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap LastTransitionTime: &now, }) } + + // If we reach this path, this means that a namespace has been both defined in Git, as well in the + // application's managedNamespaceMetadata. We want to ensure that this manifest is the one being used instead + // of what is present in managedNamespaceMetadata. + if isManagedNamespace(targetObj, app) { + targetNsExists = true + } } ts.AddCheckpoint("dedup_ms") liveObjByKey, err := m.liveStateCache.GetManagedLiveObjs(app, targetObjs) if err != nil { liveObjByKey = make(map[kubeutil.ResourceKey]*unstructured.Unstructured) - conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) + msg := fmt.Sprintf("Failed to load live state: %s", err.Error()) + conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now}) failedToLoadObjs = true } - logCtx.Debugf("Retrieved lived manifests") + logCtx.Debugf("Retrieved live manifests") // filter out all resources which are not permitted in the application project for k, v := range liveObjByKey { - permitted, err := project.IsLiveResourcePermitted(v, app.Spec.Destination.Server, app.Spec.Destination.Name, func(project string) ([]*appv1.Cluster, error) { - return m.db.GetProjectClusters(context.TODO(), project) + permitted, err := project.IsLiveResourcePermitted(v, app.Spec.Destination.Server, app.Spec.Destination.Name, func(project string) ([]*v1alpha1.Cluster, error) { + clusters, err := m.db.GetProjectClusters(context.TODO(), project) + if err != nil { + return nil, fmt.Errorf("failed to get clusters for project %q: %v", project, err) + } + return clusters, nil }) if err != nil { - conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) + msg := fmt.Sprintf("Failed to check if live resource %q is permitted in project %q: %s", k.String(), app.Spec.Project, err.Error()) + conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now}) failedToLoadObjs = true continue } @@ -503,6 +538,44 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap LastTransitionTime: &now, }) } + + // For the case when a namespace is managed with `managedNamespaceMetadata` AND it has resource tracking + // enabled (e.g. someone manually adds resource tracking labels or annotations), we need to do some + // bookkeeping in order to prevent the managed namespace from being pruned. + // + // Live namespaces which are managed namespaces (i.e. application namespaces which are managed with + // CreateNamespace=true and has non-nil managedNamespaceMetadata) will (usually) not have a corresponding + // entry in source control. In order for the namespace not to risk being pruned, we'll need to generate a + // namespace which we can compare the live namespace with. For that, we'll do the same as is done in + // gitops-engine, the difference here being that we create a managed namespace which is only used for comparison. + // + // targetNsExists == true implies that it already exists as a target, so no need to add the namespace to the + // targetObjs array. + if isManagedNamespace(liveObj, app) && !targetNsExists { + nsSpec := &v1.Namespace{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: kubeutil.NamespaceKind}, ObjectMeta: metav1.ObjectMeta{Name: liveObj.GetName()}} + managedNs, err := kubeutil.ToUnstructured(nsSpec) + + if err != nil { + conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) + failedToLoadObjs = true + continue + } + + // No need to care about the return value here, we just want the modified managedNs + _, err = syncNamespace(m.resourceTracking, appLabelKey, trackingMethod, app.Name, app.Spec.SyncPolicy)(managedNs, liveObj) + if err != nil { + conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) + failedToLoadObjs = true + } else { + targetObjs = append(targetObjs, managedNs) + } + } + } + } + hasPostDeleteHooks := false + for _, obj := range targetObjs { + if isPostDeleteHook(obj) { + hasPostDeleteHooks = true } } @@ -516,25 +589,33 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap } manifestRevisions := make([]string, 0) - for _, manifestInfo := range manifestInfoMap { + for _, manifestInfo := range manifestInfos { manifestRevisions = append(manifestRevisions, manifestInfo.Revision) } - // restore comparison using cached diff result if previous comparison was performed for the same revision - revisionChanged := len(manifestInfoMap) != len(sources) || !reflect.DeepEqual(app.Status.Sync.Revisions, manifestRevisions) - specChanged := !reflect.DeepEqual(app.Status.Sync.ComparedTo, appv1.ComparedTo{Source: app.Spec.GetSource(), Destination: app.Spec.Destination, Sources: sources}) + serverSideDiff := m.serverSideDiff || + resourceutil.HasAnnotationOption(app, common.AnnotationCompareOptions, "ServerSideDiff=true") + + // This allows turning SSD off for a given app if it is enabled at the + // controller level + if resourceutil.HasAnnotationOption(app, common.AnnotationCompareOptions, "ServerSideDiff=false") { + serverSideDiff = false + } - _, refreshRequested := app.IsRefreshRequested() - noCache = noCache || refreshRequested || app.Status.Expired(m.statusRefreshTimeout) || specChanged || revisionChanged + useDiffCache := useDiffCache(noCache, manifestInfos, sources, app, manifestRevisions, m.statusRefreshTimeout, serverSideDiff, logCtx) diffConfigBuilder := argodiff.NewDiffConfigBuilder(). WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles). WithTracking(appLabelKey, string(trackingMethod)) - if noCache { - diffConfigBuilder.WithNoCache() + if useDiffCache { + diffConfigBuilder.WithCache(m.cache, app.InstanceName(m.namespace)) } else { - diffConfigBuilder.WithCache(m.cache, app.GetName()) + diffConfigBuilder.WithNoCache() + } + + if resourceutil.HasAnnotationOption(app, common.AnnotationCompareOptions, "IncludeMutationWebhook=true") { + diffConfigBuilder.WithIgnoreMutationWebhook(false) } gvkParser, err := m.getGVKParser(app.Spec.Destination.Server) @@ -544,6 +625,18 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap diffConfigBuilder.WithGVKParser(gvkParser) diffConfigBuilder.WithManager(common.ArgoCDSSAManager) + diffConfigBuilder.WithServerSideDiff(serverSideDiff) + + if serverSideDiff { + resourceOps, cleanup, err := m.getResourceOperations(app.Spec.Destination.Server) + if err != nil { + log.Errorf("CompareAppState error getting resource operations: %s", err) + conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionUnknownError, Message: err.Error(), LastTransitionTime: &now}) + } + defer cleanup() + diffConfigBuilder.WithServerSideDryRunner(diff.NewK8sServerSideDryRunner(resourceOps)) + } + // enable structured merge diff if application syncs with server-side apply if app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.SyncOptions.HasOption("ServerSideApply=true") { diffConfigBuilder.WithStructuredMergeDiff(true) @@ -557,7 +650,8 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap if err != nil { diffResults = &diff.DiffResultList{} failedToLoadObjs = true - conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) + msg := fmt.Sprintf("Failed to compare desired state to live state: %s", err.Error()) + conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now}) } ts.AddCheckpoint("diff_ms") @@ -583,7 +677,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap Kind: gvk.Kind, Version: gvk.Version, Group: gvk.Group, - Hook: hookutil.IsHook(obj), + Hook: isHook(obj), RequiresPruning: targetObj == nil && liveObj != nil && isSelfReferencedObj, } if targetObj != nil { @@ -596,12 +690,22 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap } else { diffResult = diff.DiffResult{Modified: false, NormalizedLive: []byte("{}"), PredictedLive: []byte("{}")} } + + // For the case when a namespace is managed with `managedNamespaceMetadata` AND it has resource tracking + // enabled (e.g. someone manually adds resource tracking labels or annotations), we need to do some + // bookkeeping in order to ensure that it's not considered `OutOfSync` (since it does not exist in source + // control). + // + // This is in addition to the bookkeeping we do (see `isManagedNamespace` and its references) to prevent said + // namespace from being pruned. + isManagedNs := isManagedNamespace(targetObj, app) && liveObj == nil + if resState.Hook || ignore.Ignore(obj) || (targetObj != nil && hookutil.Skip(targetObj)) || !isSelfReferencedObj { // For resource hooks, skipped resources or objects that may have // been created by another controller with annotations copied from // the source object, don't store sync status, and do not affect // overall sync status - } else if diffResult.Modified || targetObj == nil || liveObj == nil { + } else if !isManagedNs && (diffResult.Modified || targetObj == nil || liveObj == nil) { // Set resource state to OutOfSync since one of the following is true: // * target and live resource are different // * target resource not defined and live resource is extra @@ -622,7 +726,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap } if isNamespaced && obj.GetNamespace() == "" { - conditions = append(conditions, appv1.ApplicationCondition{Type: v1alpha1.ApplicationConditionInvalidSpecError, Message: fmt.Sprintf("Namespace for %s %s is missing.", obj.GetName(), gvk.String()), LastTransitionTime: &now}) + conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionInvalidSpecError, Message: fmt.Sprintf("Namespace for %s %s is missing.", obj.GetName(), gvk.String()), LastTransitionTime: &now}) } // we can't say anything about the status if we were unable to get the target objects @@ -651,6 +755,8 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap if failedToLoadObjs { syncCode = v1alpha1.SyncStatusCodeUnknown + } else if app.HasChangedManagedNamespaceMetadata() { + syncCode = v1alpha1.SyncStatusCodeOutOfSync } var revision string @@ -660,18 +766,20 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap var syncStatus v1alpha1.SyncStatus if hasMultipleSources { syncStatus = v1alpha1.SyncStatus{ - ComparedTo: appv1.ComparedTo{ - Destination: app.Spec.Destination, - Sources: sources, + ComparedTo: v1alpha1.ComparedTo{ + Destination: app.Spec.Destination, + Sources: sources, + IgnoreDifferences: app.Spec.IgnoreDifferences, }, Status: syncCode, Revisions: manifestRevisions, } } else { syncStatus = v1alpha1.SyncStatus{ - ComparedTo: appv1.ComparedTo{ - Destination: app.Spec.Destination, - Source: app.Spec.GetSource(), + ComparedTo: v1alpha1.ComparedTo{ + Destination: app.Spec.Destination, + Source: app.Spec.GetSource(), + IgnoreDifferences: app.Spec.IgnoreDifferences, }, Status: syncCode, Revision: revision, @@ -682,13 +790,13 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap healthStatus, err := setApplicationHealth(managedResources, resourceSummaries, resourceOverrides, app, m.persistResourceHealth) if err != nil { - conditions = append(conditions, appv1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now}) + conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: fmt.Sprintf("error setting app health: %s", err.Error()), LastTransitionTime: &now}) } // Git has already performed the signature verification via its GPG interface, and the result is available // in the manifest info received from the repository server. We now need to form our opinion about the result // and stop processing if we do not agree about the outcome. - for _, manifestInfo := range manifestInfoMap { + for _, manifestInfo := range manifestInfos { if gpg.IsGPGEnabled() && verifySignature && manifestInfo != nil { conditions = append(conditions, verifyGnuPGSignature(manifestInfo.Revision, project, manifestInfo)...) } @@ -702,31 +810,86 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap reconciliationResult: reconciliation, diffConfig: diffConfig, diffResultList: diffResults, + hasPostDeleteHooks: hasPostDeleteHooks, } if hasMultipleSources { - for _, manifestInfo := range manifestInfoMap { - compRes.appSourceTypes = append(compRes.appSourceTypes, appv1.ApplicationSourceType(manifestInfo.SourceType)) + for _, manifestInfo := range manifestInfos { + compRes.appSourceTypes = append(compRes.appSourceTypes, v1alpha1.ApplicationSourceType(manifestInfo.SourceType)) } } else { - for _, manifestInfo := range manifestInfoMap { + for _, manifestInfo := range manifestInfos { compRes.appSourceType = v1alpha1.ApplicationSourceType(manifestInfo.SourceType) break } } - app.Status.SetConditions(conditions, map[appv1.ApplicationConditionType]bool{ - appv1.ApplicationConditionComparisonError: true, - appv1.ApplicationConditionSharedResourceWarning: true, - appv1.ApplicationConditionRepeatedResourceWarning: true, - appv1.ApplicationConditionExcludedResourceWarning: true, + app.Status.SetConditions(conditions, map[v1alpha1.ApplicationConditionType]bool{ + v1alpha1.ApplicationConditionComparisonError: true, + v1alpha1.ApplicationConditionSharedResourceWarning: true, + v1alpha1.ApplicationConditionRepeatedResourceWarning: true, + v1alpha1.ApplicationConditionExcludedResourceWarning: true, }) ts.AddCheckpoint("health_ms") compRes.timings = ts.Timings() - return &compRes + return &compRes, nil +} + +// useDiffCache will determine if the diff should be calculated based +// on the existing live state cache or not. +func useDiffCache(noCache bool, manifestInfos []*apiclient.ManifestResponse, sources []v1alpha1.ApplicationSource, app *v1alpha1.Application, manifestRevisions []string, statusRefreshTimeout time.Duration, serverSideDiff bool, log *log.Entry) bool { + + if noCache { + log.WithField("useDiffCache", "false").Debug("noCache is true") + return false + } + refreshType, refreshRequested := app.IsRefreshRequested() + if refreshRequested { + log.WithField("useDiffCache", "false").Debugf("refresh type %s requested", string(refreshType)) + return false + } + // serverSideDiff should still use cache even if status is expired. + // This is an attempt to avoid hitting k8s API server too frequently during + // app refresh with serverSideDiff is enabled. If there are negative side + // effects identified with this approach, the serverSideDiff should be removed + // from this condition. + if app.Status.Expired(statusRefreshTimeout) && !serverSideDiff { + log.WithField("useDiffCache", "false").Debug("app.status.expired") + return false + } + + if len(manifestInfos) != len(sources) { + log.WithField("useDiffCache", "false").Debug("manifestInfos len != sources len") + return false + } + + revisionChanged := !reflect.DeepEqual(app.Status.GetRevisions(), manifestRevisions) + if revisionChanged { + log.WithField("useDiffCache", "false").Debug("revisionChanged") + return false + } + + currentSpec := app.BuildComparedToStatus() + specChanged := !reflect.DeepEqual(app.Status.Sync.ComparedTo, currentSpec) + if specChanged { + log.WithField("useDiffCache", "false").Debug("specChanged") + return false + } + + log.WithField("useDiffCache", "true").Debug("using diff cache") + return true } -func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, revisions []string, sources []v1alpha1.ApplicationSource, hasMultipleSources bool, startedAt metav1.Time) error { +func (m *appStateManager) persistRevisionHistory( + app *v1alpha1.Application, + revision string, + source v1alpha1.ApplicationSource, + revisions []string, + sources []v1alpha1.ApplicationSource, + hasMultipleSources bool, + startedAt metav1.Time, + initiatedBy v1alpha1.OperationInitiator, +) error { var nextID int64 if len(app.Status.History) > 0 { nextID = app.Status.History.LastRevisionHistory().ID + 1 @@ -739,6 +902,7 @@ func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revi ID: nextID, Sources: sources, Revisions: revisions, + InitiatedBy: initiatedBy, }) } else { app.Status.History = append(app.Status.History, v1alpha1.RevisionHistory{ @@ -747,6 +911,7 @@ func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revi DeployStartedAt: &startedAt, ID: nextID, Source: source, + InitiatedBy: initiatedBy, }) } @@ -779,6 +944,8 @@ func NewAppStateManager( statusRefreshTimeout time.Duration, resourceTracking argo.ResourceTracking, persistResourceHealth bool, + repoErrorGracePeriod time.Duration, + serverSideDiff bool, ) AppStateManager { return &appStateManager{ liveStateCache: liveStateCache, @@ -794,6 +961,8 @@ func NewAppStateManager( statusRefreshTimeout: statusRefreshTimeout, resourceTracking: resourceTracking, persistResourceHealth: persistResourceHealth, + repoErrorGracePeriod: repoErrorGracePeriod, + serverSideDiff: serverSideDiff, } } diff --git a/controller/state_test.go b/controller/state_test.go index 8883464267538..d21cda62137de 100644 --- a/controller/state_test.go +++ b/controller/state_test.go @@ -2,6 +2,7 @@ package controller import ( "encoding/json" + "fmt" "os" "testing" "time" @@ -10,6 +11,9 @@ import ( synccommon "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/argoproj/gitops-engine/pkg/utils/kube" . "github.com/argoproj/gitops-engine/pkg/utils/testing" + "github.com/imdario/mergo" + "github.com/sirupsen/logrus" + logrustest "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/assert" v1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -19,6 +23,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/argoproj/argo-cd/v2/common" + "github.com/argoproj/argo-cd/v2/controller/testdata" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/test" @@ -37,12 +43,243 @@ func TestCompareAppStateEmpty(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) + assert.NotNil(t, compRes) + assert.NotNil(t, compRes.syncStatus) + assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) + assert.Len(t, app.Status.Conditions, 0) +} + +// TestCompareAppStateRepoError tests the case when CompareAppState notices a repo error +func TestCompareAppStateRepoError(t *testing.T) { + app := newFakeApp() + ctrl := newFakeController(&fakeData{manifestResponses: make([]*apiclient.ManifestResponse, 3)}, fmt.Errorf("test repo error")) + sources := make([]argoappv1.ApplicationSource, 0) + sources = append(sources, app.Spec.GetSource()) + revisions := make([]string, 0) + revisions = append(revisions, "") + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, compRes) + assert.EqualError(t, err, CompareStateRepoError.Error()) + + // expect to still get compare state error to as inside grace period + compRes, err = ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, compRes) + assert.EqualError(t, err, CompareStateRepoError.Error()) + + time.Sleep(10 * time.Second) + // expect to not get error as outside of grace period, but status should be unknown + compRes, err = ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.NotNil(t, compRes) + assert.Nil(t, err) + assert.Equal(t, compRes.syncStatus.Status, argoappv1.SyncStatusCodeUnknown) +} + +// TestCompareAppStateNamespaceMetadataDiffers tests comparison when managed namespace metadata differs +func TestCompareAppStateNamespaceMetadataDiffers(t *testing.T) { + app := newFakeApp() + app.Spec.SyncPolicy.ManagedNamespaceMetadata = &argoappv1.ManagedNamespaceMetadata{ + Labels: map[string]string{ + "foo": "bar", + }, + Annotations: map[string]string{ + "foo": "bar", + }, + } + app.Status.OperationState = &argoappv1.OperationState{ + SyncResult: &argoappv1.SyncOperationResult{}, + } + + data := fakeData{ + manifestResponse: &apiclient.ManifestResponse{ + Manifests: []string{}, + Namespace: test.FakeDestNamespace, + Server: test.FakeClusterURL, + Revision: "abc123", + }, + managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), + } + ctrl := newFakeController(&data, nil) + sources := make([]argoappv1.ApplicationSource, 0) + sources = append(sources, app.Spec.GetSource()) + revisions := make([]string, 0) + revisions = append(revisions, "") + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) + assert.NotNil(t, compRes) + assert.NotNil(t, compRes.syncStatus) + assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources, 0) + assert.Len(t, app.Status.Conditions, 0) +} + +// TestCompareAppStateNamespaceMetadataDiffers tests comparison when managed namespace metadata differs to live and manifest ns +func TestCompareAppStateNamespaceMetadataDiffersToManifest(t *testing.T) { + ns := NewNamespace() + ns.SetName(test.FakeDestNamespace) + ns.SetNamespace(test.FakeDestNamespace) + ns.SetAnnotations(map[string]string{"bar": "bat"}) + + app := newFakeApp() + app.Spec.SyncPolicy.ManagedNamespaceMetadata = &argoappv1.ManagedNamespaceMetadata{ + Labels: map[string]string{ + "foo": "bar", + }, + Annotations: map[string]string{ + "foo": "bar", + }, + } + app.Status.OperationState = &argoappv1.OperationState{ + SyncResult: &argoappv1.SyncOperationResult{}, + } + + liveNs := ns.DeepCopy() + liveNs.SetAnnotations(nil) + + data := fakeData{ + manifestResponse: &apiclient.ManifestResponse{ + Manifests: []string{toJSON(t, liveNs)}, + Namespace: test.FakeDestNamespace, + Server: test.FakeClusterURL, + Revision: "abc123", + }, + managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ + kube.GetResourceKey(ns): ns, + }, + } + ctrl := newFakeController(&data, nil) + sources := make([]argoappv1.ApplicationSource, 0) + sources = append(sources, app.Spec.GetSource()) + revisions := make([]string, 0) + revisions = append(revisions, "") + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) + assert.NotNil(t, compRes) + assert.NotNil(t, compRes.syncStatus) + assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status) + assert.Len(t, compRes.resources, 1) + assert.Len(t, compRes.managedResources, 1) + assert.NotNil(t, compRes.diffResultList) + assert.Len(t, compRes.diffResultList.Diffs, 1) + + result := NewNamespace() + assert.NoError(t, json.Unmarshal(compRes.diffResultList.Diffs[0].PredictedLive, result)) + + labels := result.GetLabels() + delete(labels, "kubernetes.io/metadata.name") + + assert.Equal(t, map[string]string{}, labels) + // Manifests override definitions in managedNamespaceMetadata + assert.Equal(t, map[string]string{"bar": "bat"}, result.GetAnnotations()) + assert.Len(t, app.Status.Conditions, 0) +} + +// TestCompareAppStateNamespaceMetadata tests comparison when managed namespace metadata differs to live +func TestCompareAppStateNamespaceMetadata(t *testing.T) { + ns := NewNamespace() + ns.SetName(test.FakeDestNamespace) + ns.SetNamespace(test.FakeDestNamespace) + ns.SetAnnotations(map[string]string{"bar": "bat"}) + + app := newFakeApp() + app.Spec.SyncPolicy.ManagedNamespaceMetadata = &argoappv1.ManagedNamespaceMetadata{ + Labels: map[string]string{ + "foo": "bar", + }, + Annotations: map[string]string{ + "foo": "bar", + }, + } + app.Status.OperationState = &argoappv1.OperationState{ + SyncResult: &argoappv1.SyncOperationResult{}, + } + + data := fakeData{ + manifestResponse: &apiclient.ManifestResponse{ + Manifests: []string{}, + Namespace: test.FakeDestNamespace, + Server: test.FakeClusterURL, + Revision: "abc123", + }, + managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ + kube.GetResourceKey(ns): ns, + }, + } + ctrl := newFakeController(&data, nil) + sources := make([]argoappv1.ApplicationSource, 0) + sources = append(sources, app.Spec.GetSource()) + revisions := make([]string, 0) + revisions = append(revisions, "") + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) + assert.NotNil(t, compRes) + assert.NotNil(t, compRes.syncStatus) + assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status) + assert.Len(t, compRes.resources, 1) + assert.Len(t, compRes.managedResources, 1) + assert.NotNil(t, compRes.diffResultList) + assert.Len(t, compRes.diffResultList.Diffs, 1) + + result := NewNamespace() + assert.NoError(t, json.Unmarshal(compRes.diffResultList.Diffs[0].PredictedLive, result)) + + labels := result.GetLabels() + delete(labels, "kubernetes.io/metadata.name") + + assert.Equal(t, map[string]string{"foo": "bar"}, labels) + assert.Equal(t, map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true", "bar": "bat", "foo": "bar"}, result.GetAnnotations()) + assert.Len(t, app.Status.Conditions, 0) +} + +// TestCompareAppStateNamespaceMetadataIsTheSame tests comparison when managed namespace metadata is the same +func TestCompareAppStateNamespaceMetadataIsTheSame(t *testing.T) { + app := newFakeApp() + app.Spec.SyncPolicy.ManagedNamespaceMetadata = &argoappv1.ManagedNamespaceMetadata{ + Labels: map[string]string{ + "foo": "bar", + }, + Annotations: map[string]string{ + "foo": "bar", + }, + } + app.Status.OperationState = &argoappv1.OperationState{ + SyncResult: &argoappv1.SyncOperationResult{ + ManagedNamespaceMetadata: &argoappv1.ManagedNamespaceMetadata{ + Labels: map[string]string{ + "foo": "bar", + }, + Annotations: map[string]string{ + "foo": "bar", + }, + }, + }, + } + + data := fakeData{ + manifestResponse: &apiclient.ManifestResponse{ + Manifests: []string{}, + Namespace: test.FakeDestNamespace, + Server: test.FakeClusterURL, + Revision: "abc123", + }, + managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), + } + ctrl := newFakeController(&data, nil) + sources := make([]argoappv1.ApplicationSource, 0) + sources = append(sources, app.Spec.GetSource()) + revisions := make([]string, 0) + revisions = append(revisions, "") + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) @@ -64,12 +301,13 @@ func TestCompareAppStateMissing(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status) @@ -95,12 +333,13 @@ func TestCompareAppStateExtra(t *testing.T) { key: pod, }, } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status) assert.Equal(t, 1, len(compRes.resources)) @@ -125,12 +364,13 @@ func TestCompareAppStateHook(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) assert.Equal(t, 0, len(compRes.resources)) @@ -156,12 +396,13 @@ func TestCompareAppStateSkipHook(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) assert.Equal(t, 1, len(compRes.resources)) @@ -185,13 +426,14 @@ func TestCompareAppStateCompareOptionIgnoreExtraneous(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) @@ -218,12 +460,13 @@ func TestCompareAppStateExtraHook(t *testing.T) { key: pod, }, } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) @@ -233,6 +476,75 @@ func TestCompareAppStateExtraHook(t *testing.T) { assert.Equal(t, 0, len(app.Status.Conditions)) } +// TestAppRevisions tests that revisions are properly propagated for a single source app +func TestAppRevisionsSingleSource(t *testing.T) { + obj1 := NewPod() + obj1.SetNamespace(test.FakeDestNamespace) + data := fakeData{ + manifestResponse: &apiclient.ManifestResponse{ + Manifests: []string{toJSON(t, obj1)}, + Namespace: test.FakeDestNamespace, + Server: test.FakeClusterURL, + Revision: "abc123", + }, + managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), + } + ctrl := newFakeController(&data, nil) + + app := newFakeApp() + revisions := make([]string, 0) + revisions = append(revisions, "") + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, app.Spec.GetSources(), false, false, nil, app.Spec.HasMultipleSources()) + assert.Nil(t, err) + assert.NotNil(t, compRes) + assert.NotNil(t, compRes.syncStatus) + assert.NotEmpty(t, compRes.syncStatus.Revision) + assert.Len(t, compRes.syncStatus.Revisions, 0) +} + +// TestAppRevisions tests that revisions are properly propagated for a multi source app +func TestAppRevisionsMultiSource(t *testing.T) { + obj1 := NewPod() + obj1.SetNamespace(test.FakeDestNamespace) + data := fakeData{ + manifestResponses: []*apiclient.ManifestResponse{ + { + Manifests: []string{toJSON(t, obj1)}, + Namespace: test.FakeDestNamespace, + Server: test.FakeClusterURL, + Revision: "abc123", + }, + { + Manifests: []string{toJSON(t, obj1)}, + Namespace: test.FakeDestNamespace, + Server: test.FakeClusterURL, + Revision: "def456", + }, + { + Manifests: []string{}, + Namespace: test.FakeDestNamespace, + Server: test.FakeClusterURL, + Revision: "ghi789", + }, + }, + managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), + } + ctrl := newFakeController(&data, nil) + + app := newFakeMultiSourceApp() + revisions := make([]string, 0) + revisions = append(revisions, "") + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, app.Spec.GetSources(), false, false, nil, app.Spec.HasMultipleSources()) + assert.Nil(t, err) + assert.NotNil(t, compRes) + assert.NotNil(t, compRes.syncStatus) + assert.Empty(t, compRes.syncStatus.Revision) + assert.Len(t, compRes.syncStatus.Revisions, 3) + assert.Equal(t, "abc123", compRes.syncStatus.Revisions[0]) + assert.Equal(t, "def456", compRes.syncStatus.Revisions[1]) + assert.Equal(t, "ghi789", compRes.syncStatus.Revisions[2]) +} + func toJSON(t *testing.T, obj *unstructured.Unstructured) string { data, err := json.Marshal(obj) assert.NoError(t, err) @@ -265,12 +577,13 @@ func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) { kube.GetResourceKey(obj3): obj3, }, } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.Equal(t, 1, len(app.Status.Conditions)) @@ -280,6 +593,48 @@ func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) { assert.Equal(t, 4, len(compRes.resources)) } +func TestCompareAppStateManagedNamespaceMetadataWithLiveNsDoesNotGetPruned(t *testing.T) { + app := newFakeApp() + app.Spec.SyncPolicy = &argoappv1.SyncPolicy{ + ManagedNamespaceMetadata: &argoappv1.ManagedNamespaceMetadata{ + Labels: nil, + Annotations: nil, + }, + } + + ns := NewNamespace() + ns.SetName(test.FakeDestNamespace) + ns.SetNamespace(test.FakeDestNamespace) + ns.SetAnnotations(map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true"}) + + data := fakeData{ + manifestResponse: &apiclient.ManifestResponse{ + Manifests: []string{}, + Namespace: test.FakeDestNamespace, + Server: test.FakeClusterURL, + Revision: "abc123", + }, + managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ + kube.GetResourceKey(ns): ns, + }, + } + ctrl := newFakeController(&data, nil) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, []string{}, app.Spec.Sources, false, false, nil, false) + assert.Nil(t, err) + + assert.NotNil(t, compRes) + assert.Equal(t, 0, len(app.Status.Conditions)) + assert.NotNil(t, compRes) + assert.NotNil(t, compRes.syncStatus) + // Ensure that ns does not get pruned + assert.NotNil(t, compRes.reconciliationResult.Target[0]) + assert.Equal(t, compRes.reconciliationResult.Target[0].GetName(), ns.GetName()) + assert.Equal(t, compRes.reconciliationResult.Target[0].GetAnnotations(), ns.GetAnnotations()) + assert.Equal(t, compRes.reconciliationResult.Target[0].GetLabels(), ns.GetLabels()) + assert.Len(t, compRes.resources, 1) + assert.Len(t, compRes.managedResources, 1) +} + var defaultProj = argoappv1.AppProject{ ObjectMeta: metav1.ObjectMeta{ Name: "default", @@ -319,13 +674,14 @@ func TestSetHealth(t *testing.T) { managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ kube.GetResourceKey(deployment): deployment, }, - }) + }, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.Equal(t, health.HealthStatusHealthy, compRes.healthStatus.Status) } @@ -355,13 +711,14 @@ func TestSetHealthSelfReferencedApp(t *testing.T) { kube.GetResourceKey(deployment): deployment, kube.GetResourceKey(unstructuredApp): unstructuredApp, }, - }) + }, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.Equal(t, health.HealthStatusHealthy, compRes.healthStatus.Status) } @@ -381,7 +738,7 @@ func TestSetManagedResourcesWithOrphanedResources(t *testing.T) { AppName: "", }, }, - }) + }, nil) tree, err := ctrl.setAppManagedResources(app, &comparisonResult{managedResources: make([]managedResource, 0)}) @@ -410,7 +767,7 @@ func TestSetManagedResourcesWithResourcesOfAnotherApp(t *testing.T) { AppName: "app2", }, }, - }) + }, nil) tree, err := ctrl.setAppManagedResources(app1, &comparisonResult{managedResources: make([]managedResource, 0)}) @@ -429,13 +786,14 @@ func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) { configMapData: map[string]string{ "resource.customizations": "invalid setting", }, - }) + }, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.Equal(t, health.HealthStatusUnknown, compRes.healthStatus.Status) assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status) @@ -462,7 +820,7 @@ func TestSetManagedResourcesKnownOrphanedResourceExceptions(t *testing.T) { ResourceNode: argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: kube.ServiceAccountKind, Name: "kubernetes", Namespace: app.Namespace}}, }, }, - }) + }, nil) tree, err := ctrl.setAppManagedResources(app, &comparisonResult{managedResources: make([]managedResource, 0)}) @@ -475,14 +833,14 @@ func Test_appStateManager_persistRevisionHistory(t *testing.T) { app := newFakeApp() ctrl := newFakeController(&fakeData{ apps: []runtime.Object{app}, - }) + }, nil) manager := ctrl.appStateManager.(*appStateManager) setRevisionHistoryLimit := func(value int) { i := int64(value) app.Spec.RevisionHistoryLimit = &i } addHistory := func() { - err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1.Time{}) + err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1.Time{}, v1alpha1.OperationInitiator{}) assert.NoError(t, err) } addHistory() @@ -518,7 +876,7 @@ func Test_appStateManager_persistRevisionHistory(t *testing.T) { assert.Len(t, app.Status.History, 9) metav1NowTime := metav1.NewTime(time.Now()) - err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1NowTime) + err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1NowTime, v1alpha1.OperationInitiator{}) assert.NoError(t, err) assert.Equal(t, app.Status.History.LastRevisionHistory().DeployStartedAt, &metav1NowTime) } @@ -555,9 +913,8 @@ var signedProj = argoappv1.AppProject{ } func TestSignedResponseNoSignatureRequired(t *testing.T) { - oldval := os.Getenv("ARGOCD_GPG_ENABLED") - os.Setenv("ARGOCD_GPG_ENABLED", "true") - defer os.Setenv("ARGOCD_GPG_ENABLED", oldval) + t.Setenv("ARGOCD_GPG_ENABLED", "true") + // We have a good signature response, but project does not require signed commits { app := newFakeApp() @@ -571,12 +928,13 @@ func TestSignedResponseNoSignatureRequired(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) @@ -597,12 +955,13 @@ func TestSignedResponseNoSignatureRequired(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) @@ -613,9 +972,7 @@ func TestSignedResponseNoSignatureRequired(t *testing.T) { } func TestSignedResponseSignatureRequired(t *testing.T) { - oldval := os.Getenv("ARGOCD_GPG_ENABLED") - os.Setenv("ARGOCD_GPG_ENABLED", "true") - defer os.Setenv("ARGOCD_GPG_ENABLED", oldval) + t.Setenv("ARGOCD_GPG_ENABLED", "true") // We have a good signature response, valid key, and signing is required - sync! { @@ -630,12 +987,13 @@ func TestSignedResponseSignatureRequired(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "") - compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) @@ -656,12 +1014,13 @@ func TestSignedResponseSignatureRequired(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) @@ -682,12 +1041,13 @@ func TestSignedResponseSignatureRequired(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) @@ -708,12 +1068,13 @@ func TestSignedResponseSignatureRequired(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) @@ -735,14 +1096,15 @@ func TestSignedResponseSignatureRequired(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) testProj := signedProj testProj.Spec.SignatureKeys[0].KeyID = "4AEE18F83AFDEB24" sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes := ctrl.appStateManager.CompareAppState(app, &testProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &testProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) @@ -766,12 +1128,13 @@ func TestSignedResponseSignatureRequired(t *testing.T) { } // it doesn't matter for our test whether local manifests are valid localManifests := []string{"foobar"} - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status) @@ -781,7 +1144,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) { assert.Contains(t, app.Status.Conditions[0].Message, "Cannot use local manifests") } - os.Setenv("ARGOCD_GPG_ENABLED", "false") + t.Setenv("ARGOCD_GPG_ENABLED", "false") // We have a bad signature response and signing would be required, but GPG subsystem is disabled - sync { app := newFakeApp() @@ -795,12 +1158,13 @@ func TestSignedResponseSignatureRequired(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) @@ -824,12 +1188,13 @@ func TestSignedResponseSignatureRequired(t *testing.T) { } // it doesn't matter for our test whether local manifests are valid localManifests := []string{""} - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) sources := make([]argoappv1.ApplicationSource, 0) sources = append(sources, app.Spec.GetSource()) revisions := make([]string, 0) revisions = append(revisions, "abc123") - compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false) + compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false) + assert.Nil(t, err) assert.NotNil(t, compRes) assert.NotNil(t, compRes.syncStatus) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) @@ -837,7 +1202,6 @@ func TestSignedResponseSignatureRequired(t *testing.T) { assert.Len(t, compRes.managedResources, 0) assert.Len(t, app.Status.Conditions, 0) } - } func TestComparisonResult_GetHealthStatus(t *testing.T) { @@ -965,7 +1329,7 @@ func TestIsLiveResourceManaged(t *testing.T) { kube.GetResourceKey(unmanagedObjWrongGroup): unmanagedObjWrongGroup, kube.GetResourceKey(unmanagedObjWrongNamespace): unmanagedObjWrongNamespace, }, - }) + }, nil) manager := ctrl.appStateManager.(*appStateManager) appName := "guestbook" @@ -1034,3 +1398,283 @@ func TestIsLiveResourceManaged(t *testing.T) { assert.True(t, manager.isSelfReferencedObj(managedWrongAPIGroup, config, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation)) }) } + +func TestUseDiffCache(t *testing.T) { + type fixture struct { + testName string + noCache bool + manifestInfos []*apiclient.ManifestResponse + sources []argoappv1.ApplicationSource + app *argoappv1.Application + manifestRevisions []string + statusRefreshTimeout time.Duration + expectedUseCache bool + serverSideDiff bool + } + + manifestInfos := func(revision string) []*apiclient.ManifestResponse { + return []*apiclient.ManifestResponse{ + { + Manifests: []string{ + "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"labels\":{\"app.kubernetes.io/instance\":\"httpbin\"},\"name\":\"httpbin-svc\",\"namespace\":\"httpbin\"},\"spec\":{\"ports\":[{\"name\":\"http-port\",\"port\":7777,\"targetPort\":80},{\"name\":\"test\",\"port\":333}],\"selector\":{\"app\":\"httpbin\"}}}", + "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"labels\":{\"app.kubernetes.io/instance\":\"httpbin\"},\"name\":\"httpbin-deployment\",\"namespace\":\"httpbin\"},\"spec\":{\"replicas\":2,\"selector\":{\"matchLabels\":{\"app\":\"httpbin\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"httpbin\"}},\"spec\":{\"containers\":[{\"image\":\"kennethreitz/httpbin\",\"imagePullPolicy\":\"Always\",\"name\":\"httpbin\",\"ports\":[{\"containerPort\":80}]}]}}}}", + }, + Namespace: "", + Server: "", + Revision: revision, + SourceType: "Kustomize", + VerifyResult: "", + }, + } + } + sources := func() []argoappv1.ApplicationSource { + return []argoappv1.ApplicationSource{ + { + RepoURL: "https://some-repo.com", + Path: "argocd/httpbin", + TargetRevision: "HEAD", + }, + } + } + + app := func(namespace string, revision string, refresh bool, a *argoappv1.Application) *argoappv1.Application { + app := &argoappv1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "httpbin", + Namespace: namespace, + }, + Spec: argoappv1.ApplicationSpec{ + Source: &argoappv1.ApplicationSource{ + RepoURL: "https://some-repo.com", + Path: "argocd/httpbin", + TargetRevision: "HEAD", + }, + Destination: argoappv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "httpbin", + }, + Project: "default", + SyncPolicy: &argoappv1.SyncPolicy{ + SyncOptions: []string{ + "CreateNamespace=true", + "ServerSideApply=true", + }, + }, + }, + Status: argoappv1.ApplicationStatus{ + Resources: []argoappv1.ResourceStatus{}, + Sync: argoappv1.SyncStatus{ + Status: argoappv1.SyncStatusCodeSynced, + ComparedTo: argoappv1.ComparedTo{ + Source: argoappv1.ApplicationSource{ + RepoURL: "https://some-repo.com", + Path: "argocd/httpbin", + TargetRevision: "HEAD", + }, + Destination: argoappv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "httpbin", + }, + }, + Revision: revision, + Revisions: []string{}, + }, + ReconciledAt: &metav1.Time{ + Time: time.Now().Add(-time.Hour), + }, + }, + } + if refresh { + annotations := make(map[string]string) + annotations[argoappv1.AnnotationKeyRefresh] = string(argoappv1.RefreshTypeNormal) + app.SetAnnotations(annotations) + } + if a != nil { + err := mergo.Merge(app, a, mergo.WithOverride, mergo.WithOverwriteWithEmptyValue) + if err != nil { + t.Fatalf("error merging app: %s", err) + } + } + return app + } + + cases := []fixture{ + { + testName: "will use diff cache", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, nil), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: true, + serverSideDiff: false, + }, + { + testName: "will use diff cache with sync policy", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: test.YamlToApplication(testdata.DiffCacheYaml), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: true, + serverSideDiff: true, + }, + { + testName: "will use diff cache for multisource", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "", false, &argoappv1.Application{ + Spec: argoappv1.ApplicationSpec{ + Source: nil, + Sources: argoappv1.ApplicationSources{ + { + RepoURL: "multisource repo1", + }, + { + RepoURL: "multisource repo2", + }, + }, + }, + Status: argoappv1.ApplicationStatus{ + Resources: []argoappv1.ResourceStatus{}, + Sync: argoappv1.SyncStatus{ + Status: argoappv1.SyncStatusCodeSynced, + ComparedTo: argoappv1.ComparedTo{ + Source: argoappv1.ApplicationSource{}, + Sources: argoappv1.ApplicationSources{ + { + RepoURL: "multisource repo1", + }, + { + RepoURL: "multisource repo2", + }, + }, + }, + Revisions: []string{"rev1", "rev2"}, + }, + ReconciledAt: &metav1.Time{ + Time: time.Now().Add(-time.Hour), + }, + }, + }), + manifestRevisions: []string{"rev1", "rev2"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: true, + serverSideDiff: false, + }, + { + testName: "will return false if nocache is true", + noCache: true, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, nil), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: false, + serverSideDiff: false, + }, + { + testName: "will return false if requested refresh", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", true, nil), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: false, + serverSideDiff: false, + }, + { + testName: "will return false if status expired", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, nil), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Minute, + expectedUseCache: false, + serverSideDiff: false, + }, + { + testName: "will return true if status expired and server-side diff", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, nil), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Minute, + expectedUseCache: true, + serverSideDiff: true, + }, + { + testName: "will return false if there is a new revision", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, nil), + manifestRevisions: []string{"rev2"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: false, + serverSideDiff: false, + }, + { + testName: "will return false if app spec repo changed", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, &argoappv1.Application{ + Spec: argoappv1.ApplicationSpec{ + Source: &argoappv1.ApplicationSource{ + RepoURL: "new-repo", + }, + }, + }), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: false, + serverSideDiff: false, + }, + { + testName: "will return false if app spec IgnoreDifferences changed", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, &argoappv1.Application{ + Spec: argoappv1.ApplicationSpec{ + IgnoreDifferences: []argoappv1.ResourceIgnoreDifferences{ + { + Group: "app/v1", + Kind: "application", + Name: "httpbin", + Namespace: "httpbin", + JQPathExpressions: []string{"."}, + }, + }, + }, + }), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: false, + serverSideDiff: false, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + // Given + t.Parallel() + logger, _ := logrustest.NewNullLogger() + log := logrus.NewEntry(logger) + + // When + useDiffCache := useDiffCache(tc.noCache, tc.manifestInfos, tc.sources, tc.app, tc.manifestRevisions, tc.statusRefreshTimeout, tc.serverSideDiff, log) + + // Then + assert.Equal(t, useDiffCache, tc.expectedUseCache) + }) + } +} diff --git a/controller/sync.go b/controller/sync.go index 65e86851ee364..401d08bc56ea4 100644 --- a/controller/sync.go +++ b/controller/sync.go @@ -3,6 +3,7 @@ package controller import ( "context" "encoding/json" + goerrors "errors" "fmt" "os" "strconv" @@ -56,6 +57,27 @@ func (m *appStateManager) getGVKParser(server string) (*managedfields.GvkParser, return cluster.GetGVKParser(), nil } +// getResourceOperations will return the kubectl implementation of the ResourceOperations +// interface that provides functionality to manage kubernetes resources. Returns a +// cleanup function that must be called to remove the generated kube config for this +// server. +func (m *appStateManager) getResourceOperations(server string) (kube.ResourceOperations, func(), error) { + clusterCache, err := m.liveStateCache.GetClusterCache(server) + if err != nil { + return nil, nil, fmt.Errorf("error getting cluster cache: %w", err) + } + + cluster, err := m.db.GetCluster(context.Background(), server) + if err != nil { + return nil, nil, fmt.Errorf("error getting cluster: %w", err) + } + ops, cleanup, err := m.kubectl.ManageResources(cluster.RawRestConfig(), clusterCache.GetOpenAPISchema()) + if err != nil { + return nil, nil, fmt.Errorf("error creating kubectl ResourceOperations: %w", err) + } + return ops, cleanup, nil +} + 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 @@ -81,7 +103,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha if syncOp.SyncOptions.HasOption("FailOnSharedResource=true") && hasSharedResource { state.Phase = common.OperationFailed - state.Message = fmt.Sprintf("Shared resouce found: %s", sharedResourceMessage) + state.Message = fmt.Sprintf("Shared resource found: %s", sharedResourceMessage) return } @@ -139,6 +161,12 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha state.Phase = common.OperationError state.Message = fmt.Sprintf("Failed to load application project: %v", err) return + } else if syncWindowPreventsSync(app, proj) { + // If the operation is currently running, simply let the user know the sync is blocked by a current sync window + if state.Phase == common.OperationRunning { + state.Message = "Sync operation blocked by sync window" + } + return } if app.Spec.HasMultipleSources() { @@ -152,7 +180,13 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha revisions = []string{revision} } - compareResult := m.CompareAppState(app, proj, revisions, sources, false, true, syncOp.Manifests, app.Spec.HasMultipleSources()) + // ignore error if CompareStateRepoError, this shouldn't happen as noRevisionCache is true + compareResult, err := m.CompareAppState(app, proj, revisions, sources, false, true, syncOp.Manifests, app.Spec.HasMultipleSources()) + if err != nil && !goerrors.Is(err, CompareStateRepoError) { + state.Phase = common.OperationError + state.Message = err.Error() + return + } // We now have a concrete commit SHA. Save this in the sync result revision so that we remember // what we should be syncing to when resuming operations. @@ -276,6 +310,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha 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 || + isPostDeleteHook(target) || argo.ContainsSyncResource(key.Name, key.Namespace, schema.GroupVersionKind{Kind: key.Kind, Group: key.Group}, syncOp.Resources)) && m.isSelfReferencedObj(live, target, app.GetName(), appLabelKey, trackingMethod) }), @@ -322,7 +357,29 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha var resState []common.ResourceSyncResult state.Phase, state.Message, resState = syncCtx.GetState() state.SyncResult.Resources = nil + + if app.Spec.SyncPolicy != nil { + state.SyncResult.ManagedNamespaceMetadata = app.Spec.SyncPolicy.ManagedNamespaceMetadata + } + + var apiVersion []kube.APIResourceInfo for _, res := range resState { + augmentedMsg, err := argo.AugmentSyncMsg(res, func() ([]kube.APIResourceInfo, error) { + if apiVersion == nil { + _, apiVersion, err = m.liveStateCache.GetVersionsInfo(app.Spec.Destination.Server) + if err != nil { + return nil, fmt.Errorf("failed to get version info from the target cluster %q", app.Spec.Destination.Server) + } + } + return apiVersion, nil + }) + + if err != nil { + log.Errorf("using the original message since: %v", err) + } else { + res.Message = augmentedMsg + } + state.SyncResult.Resources = append(state.SyncResult.Resources, &v1alpha1.ResourceResult{ HookType: res.HookType, Group: res.ResourceKey.Group, @@ -340,7 +397,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha logEntry.WithField("duration", time.Since(start)).Info("sync/terminate complete") if !syncOp.DryRun && len(syncOp.Resources) == 0 && state.Phase.Successful() { - err := m.persistRevisionHistory(app, compareResult.syncStatus.Revision, source, compareResult.syncStatus.Revisions, compareResult.syncStatus.ComparedTo.Sources, app.Spec.HasMultipleSources(), state.StartedAt) + err := m.persistRevisionHistory(app, compareResult.syncStatus.Revision, source, compareResult.syncStatus.Revisions, compareResult.syncStatus.ComparedTo.Sources, app.Spec.HasMultipleSources(), state.StartedAt, state.Operation.InitiatedBy) if err != nil { state.Phase = common.OperationError state.Message = fmt.Sprintf("failed to record sync to history: %v", err) @@ -522,3 +579,12 @@ func delayBetweenSyncWaves(phase common.SyncPhase, wave int, finalWave bool) err } return nil } + +func syncWindowPreventsSync(app *v1alpha1.Application, proj *v1alpha1.AppProject) bool { + window := proj.Spec.SyncWindows.Matches(app) + isManual := false + if app.Status.OperationState != nil { + isManual = !app.Status.OperationState.Operation.InitiatedBy.Automated + } + return !window.CanSync(isManual) +} diff --git a/controller/sync_test.go b/controller/sync_test.go index 13406ddc0cb0e..f9bd81c1c138a 100644 --- a/controller/sync_test.go +++ b/controller/sync_test.go @@ -2,7 +2,6 @@ package controller import ( "context" - "os" "testing" "github.com/argoproj/gitops-engine/pkg/sync" @@ -42,7 +41,7 @@ func TestPersistRevisionHistory(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) // Sync with source unspecified opState := &v1alpha1.OperationState{Operation: v1alpha1.Operation{ @@ -59,6 +58,46 @@ func TestPersistRevisionHistory(t *testing.T) { assert.Equal(t, "abc123", updatedApp.Status.History[0].Revision) } +func TestPersistManagedNamespaceMetadataState(t *testing.T) { + app := newFakeApp() + app.Status.OperationState = nil + app.Status.History = nil + app.Spec.SyncPolicy.ManagedNamespaceMetadata = &v1alpha1.ManagedNamespaceMetadata{ + Labels: map[string]string{ + "foo": "bar", + }, + Annotations: map[string]string{ + "foo": "bar", + }, + } + + defaultProject := &v1alpha1.AppProject{ + ObjectMeta: v1.ObjectMeta{ + Namespace: test.FakeArgoCDNamespace, + Name: "default", + }, + } + data := fakeData{ + apps: []runtime.Object{app, defaultProject}, + manifestResponse: &apiclient.ManifestResponse{ + Manifests: []string{}, + Namespace: test.FakeDestNamespace, + Server: test.FakeClusterURL, + Revision: "abc123", + }, + managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), + } + ctrl := newFakeController(&data, nil) + + // Sync with source unspecified + opState := &v1alpha1.OperationState{Operation: v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{}, + }} + ctrl.appStateManager.SyncAppState(app, opState) + // Ensure we record spec.syncPolicy.managedNamespaceMetadata into sync result + assert.Equal(t, app.Spec.SyncPolicy.ManagedNamespaceMetadata, opState.SyncResult.ManagedNamespaceMetadata) +} + func TestPersistRevisionHistoryRollback(t *testing.T) { app := newFakeApp() app.Status.OperationState = nil @@ -79,7 +118,7 @@ func TestPersistRevisionHistoryRollback(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) // Sync with source specified source := v1alpha1.ApplicationSource{ @@ -133,14 +172,13 @@ func TestSyncComparisonError(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) // Sync with source unspecified opState := &v1alpha1.OperationState{Operation: v1alpha1.Operation{ Sync: &v1alpha1.SyncOperation{}, }} - os.Setenv("ARGOCD_GPG_ENABLED", "true") - defer os.Setenv("ARGOCD_GPG_ENABLED", "false") + t.Setenv("ARGOCD_GPG_ENABLED", "true") ctrl.appStateManager.SyncAppState(app, opState) conditions := app.Status.GetConditions(map[v1alpha1.ApplicationConditionType]bool{v1alpha1.ApplicationConditionComparisonError: true}) @@ -179,7 +217,7 @@ func TestAppStateManager_SyncAppState(t *testing.T) { }, managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } - ctrl := newFakeController(&data) + ctrl := newFakeController(&data, nil) return &fixture{ project: project, @@ -216,6 +254,75 @@ func TestAppStateManager_SyncAppState(t *testing.T) { }) } +func TestSyncWindowDeniesSync(t *testing.T) { + type fixture struct { + project *v1alpha1.AppProject + application *v1alpha1.Application + controller *ApplicationController + } + + setup := func() *fixture { + app := newFakeApp() + app.Status.OperationState = nil + app.Status.History = nil + + project := &v1alpha1.AppProject{ + ObjectMeta: v1.ObjectMeta{ + Namespace: test.FakeArgoCDNamespace, + Name: "default", + }, + Spec: v1alpha1.AppProjectSpec{ + SyncWindows: v1alpha1.SyncWindows{{ + Kind: "deny", + Schedule: "0 0 * * *", + Duration: "24h", + Clusters: []string{"*"}, + Namespaces: []string{"*"}, + Applications: []string{"*"}, + }}, + }, + } + data := fakeData{ + apps: []runtime.Object{app, project}, + manifestResponse: &apiclient.ManifestResponse{ + Manifests: []string{}, + Namespace: test.FakeDestNamespace, + Server: test.FakeClusterURL, + Revision: "abc123", + }, + managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), + } + ctrl := newFakeController(&data, nil) + + return &fixture{ + project: project, + application: app, + controller: ctrl, + } + } + + t.Run("will keep the sync progressing if a sync window prevents the sync", func(t *testing.T) { + // given a project with an active deny sync window and an operation in progress + t.Parallel() + f := setup() + opMessage := "Sync operation blocked by sync window" + + opState := &v1alpha1.OperationState{Operation: v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{ + Source: &v1alpha1.ApplicationSource{}, + }}, + Phase: common.OperationRunning, + } + // when + f.controller.appStateManager.SyncAppState(f.application, opState) + + //then + assert.Equal(t, common.OperationRunning, opState.Phase) + assert.Contains(t, opState.Message, opMessage) + }) + +} + func TestNormalizeTargetResources(t *testing.T) { type fixture struct { comparisonResult *comparisonResult diff --git a/controller/testdata/data.go b/controller/testdata/data.go index a53c6a8a88b35..028a7caaeac6b 100644 --- a/controller/testdata/data.go +++ b/controller/testdata/data.go @@ -11,4 +11,7 @@ var ( //go:embed target-deployment-new-entries.yaml TargetDeploymentNewEntries string + + //go:embed diff-cache.yaml + DiffCacheYaml string ) diff --git a/controller/testdata/diff-cache.yaml b/controller/testdata/diff-cache.yaml new file mode 100644 index 0000000000000..41a1e8a4bbeb1 --- /dev/null +++ b/controller/testdata/diff-cache.yaml @@ -0,0 +1,498 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + argocd-image-updater.argoproj.io/allow-tags: any + argocd-image-updater.argoproj.io/ignore-tags: "" + argocd-image-updater.argoproj.io/image-list-disabled-hack: "" + argocd-image-updater.argoproj.io/update-strategy: semver + argocd-image-updater.argoproj.io/write-back-method: git + argocd-image-updater.argoproj.io/write-back-target: kustomization + argocd-notif-onDeployed.slack-disabled: "" + argocd-notif-onHealthDegraded.slack-disabled: "" + argocd-notif-onSyncFailed.slack-disabled: "" + argocd-notif-onSyncRunning.slack-disabled: "" + argocd-notif-onSyncStatusUnknown.slack-disabled: "" + argocd-notif-onSyncSucceeded.slack-disabled: "" + argocd.argoproj.io/compare-options: ServerSideDiff=true + argocd.argoproj.io/manifest-generate-paths: .;/chart + creationTimestamp: "2024-03-04T21:30:33Z" + finalizers: + - resources-finalizer.argocd.argoproj.io + generation: 263 + labels: + cloud_provider: gcp + cluster_name: gke-alpha-01-europe-west1 + foo: bar + preview: "true" + project: sre + service_class: alpha + stack: gke-v2 + name: velero-test + namespace: argo-cd + ownerReferences: + - apiVersion: argoproj.io/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: ApplicationSet + name: velero + uid: 86cdfba4-8697-47b3-8489-71fab7f4a805 + resourceVersion: "722811357" + uid: 94978696-4fd4-40b3-a1de-38d9df9e9316 +spec: + destination: + name: gke-alpha-01-europe-west1 + namespace: test-lla + project: sre + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + syncPolicy: + retry: + backoff: + duration: 5s + factor: 2 + maxDuration: 3m + limit: 10 + syncOptions: + - CreateNamespace=true + - ApplyOutOfSyncOnly=true + - RespectIgnoreDifferences=false + - ServerSideApply=true + - Validate=true +status: + controllerNamespace: argo-cd + health: + status: Healthy + history: + - deployStartedAt: "2024-03-04T22:00:05Z" + deployedAt: "2024-03-04T22:00:06Z" + id: 14 + revision: ea8759964626a583667a2bfd08f334ec2070040a + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + - deployStartedAt: "2024-03-04T22:08:29Z" + deployedAt: "2024-03-04T22:08:30Z" + id: 15 + revision: ea8759964626a583667a2bfd08f334ec2070040a + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + - deployStartedAt: "2024-03-04T22:09:16Z" + deployedAt: "2024-03-04T22:09:16Z" + id: 16 + revision: ea8759964626a583667a2bfd08f334ec2070040a + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + - deployStartedAt: "2024-03-04T22:11:41Z" + deployedAt: "2024-03-04T22:11:41Z" + id: 17 + revision: ea8759964626a583667a2bfd08f334ec2070040a + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + - deployStartedAt: "2024-03-04T22:50:55Z" + deployedAt: "2024-03-04T22:50:55Z" + id: 18 + revision: ea8759964626a583667a2bfd08f334ec2070040a + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + - deployStartedAt: "2024-03-04T22:52:56Z" + deployedAt: "2024-03-04T22:52:56Z" + id: 19 + revision: ea8759964626a583667a2bfd08f334ec2070040a + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + - deployStartedAt: "2024-03-04T22:56:15Z" + deployedAt: "2024-03-04T22:56:15Z" + id: 20 + revision: ea8759964626a583667a2bfd08f334ec2070040a + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + - deployStartedAt: "2024-03-05T07:31:56Z" + deployedAt: "2024-03-05T07:31:57Z" + id: 21 + revision: ea8759964626a583667a2bfd08f334ec2070040a + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + - deployStartedAt: "2024-03-05T07:32:44Z" + deployedAt: "2024-03-05T07:32:44Z" + id: 22 + revision: ea8759964626a583667a2bfd08f334ec2070040a + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + - deployStartedAt: "2024-03-05T07:33:03Z" + deployedAt: "2024-03-05T07:33:04Z" + id: 23 + revision: ea8759964626a583667a2bfd08f334ec2070040a + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + operationState: + finishedAt: "2024-03-05T07:33:04Z" + message: successfully synced (all tasks run) + operation: + initiatedBy: + username: laurent.lavaud@mirakl.com + retry: + backoff: + duration: 5s + factor: 2 + maxDuration: 3m + limit: 10 + sync: + revision: ea8759964626a583667a2bfd08f334ec2070040a + syncOptions: + - ServerSideApply=true + syncStrategy: + hook: {} + phase: Succeeded + startedAt: "2024-03-05T07:33:03Z" + syncResult: + resources: + - group: "" + hookPhase: Running + kind: Service + message: service/test-lla serverside-applied + name: test-lla + namespace: test-lla + status: Synced + syncPhase: Sync + version: v1 + - group: apps + hookPhase: Running + kind: Deployment + message: deployment.apps/test-lla serverside-applied + name: test-lla + namespace: test-lla + status: Synced + syncPhase: Sync + version: v1 + revision: ea8759964626a583667a2bfd08f334ec2070040a + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + reconciledAt: "2024-03-05T07:33:04Z" + resources: + - health: + status: Healthy + kind: Service + name: test-lla + namespace: test-lla + status: Synced + version: v1 + - group: apps + health: + status: Healthy + kind: Deployment + name: test-lla + namespace: test-lla + status: Synced + version: v1 + sourceType: Plugin + summary: + images: + - nginx:latest + sync: + comparedTo: + destination: + name: gke-alpha-01-europe-west1 + namespace: test-lla + source: + path: instances/test + plugin: + env: + - name: RELEASE_NAME + value: test-lla + - name: CHART_REPOSITORY + value: oci://europe-west1-docker.pkg.dev/platform-89be/charts + - name: CHART_NAME + value: velero + - name: PREVIEW + value: "false" + - name: HELM_VALUES + value: | + global: + app: + cluster_name: gke-alpha-01-europe-west1 + service_class: alpha + cloud_provider: gcp + cluster_stack: gke-v2 + - name: HELM_ARGS + value: "" + name: cmp-helm-v2 + repoURL: https://github.com/mirakl/manifests-velero.git + targetRevision: test-lla + revision: rev1 + status: Synced diff --git a/docs/SUPPORT.md b/docs/SUPPORT.md index 48fb337a78954..e0adecf12d38a 100644 --- a/docs/SUPPORT.md +++ b/docs/SUPPORT.md @@ -1,6 +1,7 @@ # Support -1. Make sure you've read [understanding the basics](understand_the_basics.md) the [getting started guide](getting_started.md). -2. Looked for an answer in [the frequently asked questions](faq.md). -3. Ask a question in [the Argo CD Slack channel ⧉](https://argoproj.github.io/community/join-slack). -4. [Read issues, report a bug, or request a feature ⧉](https://github.com/argoproj/argo-cd/issues). +1. Make sure you've read [understanding the basics](understand_the_basics.md) and the [getting started guide](getting_started.md). +1. Looked for an answer in [the frequently asked questions](faq.md). +1. [Read existing issues ⧉](https://github.com/argoproj/argo-cd/issues). +1. Ask a question in [the Argo CD Slack channel ⧉](https://argoproj.github.io/community/join-slack). +1. [Report a bug, or request a feature ⧉](https://github.com/argoproj/argo-cd/issues/new/choose). diff --git a/docs/assets/api-management.png b/docs/assets/api-management.png deleted file mode 100644 index ae066f0a6a87d38e9812264ed0d5775eb75312fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14376 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 diff --git a/docs/assets/argocd-arch-authn-authz.jpg b/docs/assets/argocd-arch-authn-authz.jpg new file mode 100644 index 0000000000000000000000000000000000000000..323f95fce1d111e448ccae8a10ee55dd09496b95 GIT binary patch literal 275921 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­IhBm<9+%))MYB#5_MwL0oRgh7Bb>577 zhy`85ie*bEWe!+UH{dus9uPx`qAf_JWHH=>g+e2UMF222J(J!7>nnKMi=~6~D#>|i z)+EuOv>0Wi5kO$|vSM>2NR?}AkRJc~7yApV02csr$~+)EQnF94gKr`a;7yQ>|WUqgHY;`f8^jGc-LK*X9M>HrXbW3vCa=#f5`C1nOS3}$Am|Mjsn_=+#Qx2 znY$`agB>bduMOuq(@X+2AlWX;*_Q-?z7C#12%1$S7*V)b98BubNxRsJb8vcN&SqD8I!VsaxT)#~#b z{cE^Fk=*X~Lwj(V9%x#QboB5EjBKgx9fV z@e|lW=Pj?kWPN8LT`UiS&hZ^*=tKGsw>9Y8&20Ub_uH4-Q|p)dz|16UPp$ zX!*A;Bgec2a8i_O%i0X@0?ES&l>NP*M?NGi0s{zJdo^PK(Zpq8$~WS<#%-8$ohjG2 zwfvLk&^i+R>EHFo!UKK};ew>ycO@}@vt}+|Y!Zl2j9YR~d4tGeuqb$jfMGY@|1@@P z*nH^uSqP{f<eDwJ!6WQz%l_vOBuyGm@L17kt6vvsW;PX?4$$5-3+ATcn!Fv)oh~yzY6D&-U+lpij)jbX_G^~~c{4MN$r91jz zRFVh=Ej`m&V#pbTg0fZ&FbjV|X}{vM@bbBAT=iS9-Gl2wA2VS56vN`pr&3%$!I_%B zgN?mypY-UxU8)}K>Gk;nR_*x_bq6q~O6N35kjE>>%PfZ;aQZ~iLpU2LMHqrHIW9Jl zxitOq9rKoObT)>IadS`y+t-u@9KiUat_U3(Y5YIri7^}MmF$Z+CrMV;=xWI!c^4If zGdy|->@6^u!ZIQxK2#!85RcY6eAf%cr%OrvnhmY=Y=U9RDOGG?+xe008fvm_<^3NJ zM-c9rgL{SJnD%F(+r+1VLF50*H&b(hSkue$3TCpbSBG8R-!^^WD5081Erw3rUIn|t z4BQ(#hY#lkts&z3wAo1Ly#4eY#e;R$oL6Tu0tdOxAhVf2@4QH9|M#K#_pP)Y6AZN} zEo%oHP)h&4J)2F0N^R%QGOj{{0tu6e%>%P?P|CIN+q?7qm64Ezo&BDos>SLmT zwA}6Wm+*|z;5v@XaCOr34u;D-I?lS?NsTh zo-Tevj?#UX4&5Wa@}CGC!9+8}szMc8-!6_9CKr=VnrsJbTla;gg0NcoL3F_p3ATY- z97Nd9$TMtT=XnK}Sm6mZmE)T7u~SL9C(h@21~_ob*c}7nYs)!V-qVj$Y2a9vpItiM z*gCB#c{{FTeA$O&g-e5)e@FafmDlcC_{vEcvb#=8LlP@7X@wMSx6~4zo+cj_dLnix zv^iscQLLcqJ4tTOzBiM*5{rj`;@EV4WEN`p0obzptSQ-fWS)ZFN9Zd2N5U|$hC)(a zwnu`r^d}fNHPiKOzRInGI-OOoHUb{e#8h2m9O+d*`3l%f(C=avu_?E7`(0)N-2^kJ z7hENrmSN!nAf;$x_@1vg!I7M!H5m#rra-W1a>VRzwHGMH>% zBTIqay|M+Wo`I|OqHCb@4@xK2k-W5x8l@{IHLTYt2CEtipT*s^OCDVEId|z)@6R3? z*B1|=uE75}!ng$gIU4AF?@7VfjFlLW3m~-mA7m(4OaZAUl7E5%DTuK9)u17|7)6|V z2=pHvFZw+J(*+9nPf1xZri@8szhJ;Qz{=X4TOw29vu88wU>EB$(%34-+qNOT<81o0 z8%h0%V-v7_0;xTYWP7f@aW?B=A^Z|@fCZ(-QSy#4jyj$QnSna9;)^i@N;%kQHy_^( zW=60ZK?qi&0U|Inv~H3R5l#%o0EPbT%=h$~3XzDMK!bI9TjMyfe$9cf6WJ$3?K;Sjiw-h z;u<)Y+Q(=m^2xx~p$%*up}^Mh|4a_+<9_13E#tGGEd~i}cC!F4%ZpZQ%Q(R=?hqg0 zQd5N)HE82;?9@vHCe^|B`WZ!hdWV?Ar856aSv!`%VSs4pG-NG^*zQ+fH(pW#NsD^6 z|3euX{pEN*=qEHbeyk{iDXjt2{nr})x$aW55mX1jd`D`$h!ow{@Ud6LrQe$X5Cxf! za|*^FgX6l#oApls##5NZ-`_paYVF`~r8UqLx_U_< zVTn=c4VCf$*-9VqY595Tf#^JDVt#wuu&0ghW`maU__>n}pWh`oe{#-?m)m5xDqe2u zkJ_DcZy32Aunowi&nHMz|HN5+%Y41~xx{7DTo2;M)1s{vx-&ybx1?pi1f@9u&>!NJ zZ>Sr1lm<(a9vB*xexWZRBn&g3LUPi?xwEZ7**#kUO5K@5XPE#~LPGCm0|#pW_JB<* z>BhV{j<&u=r;k`G+%+l%lv3YumMN`w9AOS~j9BsVd!;}>1*QE+eCNzz<_2CrJzkjf zBiS1eVdDMWTgOn^EN!WL<`0ZiN^clG*Uh;|0th=9fP%a3OB-@!j6`L+IhV_&M9{yh zzhQn_)91j);|-a{lnZppiM0)w${b5Q4f^P=zAk9%s`!b$V@pKP=tm})Jgs%~!KbtQBtr>c_JkiMm5Rx=_*yTz&?yXje{Y*3+!zfCX;%qr z>=DfY*d@-Y}xY zy|_{23EY4^uef=`HV5l3391$@m2fWLYiI?0N{S@BkL4u(Hy$3_GX?|xi)H%WZ|4E0 zN+C@ykO{($((WIVw&4Zu^j6bi#iW|kAv)CglbjPQH(42N3tF+4wl|!j7Y*!Qtme_l zv&ULD&5wIle21PsbQXNaRH-d84=ZujeR%Q7lDlbfOAQhOCM)G&yw^iEs{49us^Wa6 z(QQdjSNBb~d{08J%>h)GWAse+dLV0X`Eka{SX+&(j#lC8;$c||pCa7E<}Q~NDw+(r zp|?=HaGD#+Y8(>#Dq#}v(NK+6lmjEs$js2b4Z)JA)*LhE_Q(E}^m~5&{mQjKvD3Oa zGd$!5*)eJ*T-hoaqK|%n8-aSZO>=SB3|-!gC7YJa-%gDh+TKN96Z81Aciy!T*Bkn( zdu=;rZ%%o9^h>Jc{I{M#GdAsLmKxLRRY03fF@bqJgR4buKnbsu&){J??i7mXh3*tCdE#W!%>y$Yey58G|cM_#^o`Ph=GW%U2 zzutrP(GPu&mL=`7z2%tyXs@EDOcoE(Ql zz^+Sp%arfNOx~Zd|Kfps6)hjbmGa?0!CnLuS?ZppSN2b+3=sLSjJ2Ndst2ZRasXLpsIt4>?~2N z)Kv_6Ex$du_O<+D)6yy>yNV@UveCFW4Po%8Q5bi(?r%{^hkU$zqwSNTcOJDcy12b3 z>2Q+FS&b25=(Ag`H5T$VIk(g&^IoM7<;EEtvKIZsbrS zI7Tn_{co7S?<9U!3|QSb%ZKPNdvl(`^1&#Rl3cX*?&JNIb9Q*#HCSRW$2ZzheGcM` z_{tW~eo+6E1Vqg52poF2$CF_^I35wA8l=N_=+AgoKqZgp?ql!>Z*{sLq zbSImAhODnz7-yMkZqn$Z15;&VZcjkosr4|8dqQnSZ$RiOK4TF6L(3I*mWy%8G(r+ZCgA;Xn{cB8#HNC=36IR+zSYNh>Flh~e#^*` z?76<=g_qnGp9yVhRs8gQcKrL7H!~cCD|cKDrEHd8Zse`@ddeKFip`A7yg5tbM~vLc z+Bdmdl&2-`eLT@x*IA7CJ>RvO%*f;t-rd>0eOA)OZvAPv9x${qC$k68Gzn^DDC8Fu zzu3B4{lQgx^2|Iu`OU^I_02CFz12)Bs{#u4zPoY3ap|bMDA{ret@x{Wen>@D@BCFh z%*rQ-X6i6?Q@I^xOR6pD>P|S6aa?)p9<95cMo0J0vvIPH7NHO1)mw$Nmw#JrwfOQ7 z)U&~umQ@m7s!_3XWTBWcSV+wb7xUo3%fR%%o-K@od(v9lv%TIaD=)+1w;df=5;tvW zJ~m9wDTKq?u}p`K3x6QTL}H7AZE}%_cBLV-pewTMn~Y-`_5C$F+?vZek-N3ktIJn) zL8}yIi{ZgiE8^B^Mo#F*8-{Yf`9rw$`7ZrXr(b^DR$XXKFnY7VuBf+TZ-TCIaejH9 zd$s{uORS>!J_(#-kXXv_Ef#J;bQuAaXU~%4Xt~Ee=+BO`(mI*^oCXatcf2>CPIC{& znQME>SN_>tADfJ?)sRzs12N^Z6Go=4e( zK8`pfTuGgOtX;CjJAG-#$ndh>>{XK6_It*NL-vjN;1#Ps%V6OnViM$QNrj)5tac*5 z;0+-Mj)|dyT^k?SZ7~HbK=rzw5lX6963(WK))Sk`Sc4_t^Lw*#)>MjHNGGI${cdLHPelE6sz+S!wpC~JiL$9zG@hI1?3k>ZYQhF z3KOP#w-qFo1yza3k9K&lapcDjras-e=ZM(4u1XunN76@oEId+rta7^cdnotx+9f_( z?YZ-Mq=LLX&ep#2QET=5$Yqx#F&}tGw^~0b;q*r{lc3SY9bNWIe^Yz}4?d@6npKT) zB8e?Ap4W#oYFRxpmz7YWsFwYiyhlB7Wu`}rM6wfR&u?~ zm5qMGw>2X+GJO=+xko|6x;7>qzS}S&WAF(z)8xCt#})uA^S1tO$S2*+By;+0L>zOG{Q-Zd4N0yJEF>%3_CbTAAfs z@4Q_5QxR8va8)#$(i7z@YpE3!aoT0VI^BJ;Pj2$i>6XzN{ala2OTMB9CP|`1a=Ixd zNhKvt12m8wP#}*>73wgv4H{^$cTbd5qxQ+zs~xvv4`yW;JfeN@2nHHf2z?u;%_I5JbmFjW$+UHo=f@~SL)*mLK;qn`o@$x+^KHb2!eXepa% zgz{X6Dv1t7akS{B>qt!IuE(AWoP}G;$fHyjv(1!yJc1AJ&3U|fXc#|Dd)kX7^JgY~+P%YXCaiP% z$!V+Is^-h}t{jT);$*@`Z)V%aO%z%Q-pN?{1s@GD-TA06HFMv5z2Tj^_gOUyy(6bt zZYLDnSW=-?G7Wi5x)5RgSeM|P=1OIK95`&0c-v*Dlnr7GaAz>du?{r?TigkqK5IL^ z6eJ3H^NIFL;&eEYhqQx{TWJRF+YAI<`HLT~ATy{QqKpVw**_`U@coS4O4(CJm>nwD zR>1d`jI79Tsm#N*SGFN(C2&ZY(v&!ZQvY>kXLr2DHpRCliGsqK za|eSpQL8Vj?6xs@D%nTFFJ-neV_j6xy>J@4Fh%v6Sl#}p%U+V=GA$RQ4YO8qUC{t~ zI~FV6OQa0Gdm&*AFPO_p9Z>I4=ahLTe6)2fBP4v;&>AbWX}Nb7XL%kPtXVo^2|xs> zr1Y@?epe{zH!Yb^x5LkJ+J?#N#To;&ZrC?!PJhK^Msn!_`GmxhbCnwJx46I#QMhez zWQ!mJ9{_$Wv#n%Ov~xJ#6y?AB#g^>Y$2bDWkRF`ZTm4ye6|U0oPgj*07)*fiX7Smup@E85YES2mT$^8 zjR)MY3wI9!OY4kTgl{s9dhyJPi6f<5$i3woUd%ui+uAeMvRk-&Ul3H(pTdjjeSVm& zK)70y9@?=~$i$di<(0!yD!jCPV{A!FSdQCPzH#gxw%XXdVJDEAEL6O5#VYr2UqUI% zza|OG2$Qe|KVLS=j`1Dx$eRGA0p0#SRCoG{c!ouw=3UyZ==~e5=xrTXLBw=}M+vT% z@A{lQ$Ew9zgEmcLUObz-YIwpI9aer5c6Tf6gpV@-liqb9k}tlS4_j-4~3VUB+aX^XRmp$f((+-6lAoLir0xI_!Jq&Z7 zWK-I939f8YSD27FqNyCTC^Be4u*tng#)s3h=bv_8Saln2^b}H2b1a`{H3eOQ%NFrOZ72x2GM;!hqRge0a_7ri`4t`+l-)uWczj zyr>IlGXaVu%XC=WTfn~)eF|~=4Ck_vlf;xC2><3~ zC)t?@>@|)CxT~zfkPYgOyeeR4gq2bSgsIP7ta!AGMnMveiX74XL?b_3Eql2d{*FUs zAT_GXHa?qR>AZWpAzgcuHo0B?h7Luzkl6uWeDY037f4Cn0x&ho3gwcL^SNTSl$tS* zPyu$(l80LjVoLW~8Vv*y3ViIT|!ixD5)1>gm!qZ90PrT3u=UjqqW|*!jWZrdv zomEV#@U^?Mb@tx3@zwu}y!Vc4D(m)#gQzGfQK~XPP(X?xqM(S7fRs?gP^F9z6;O&} z8yEpY4v0uoBTW#M&;vw`B8-YkL`0fMjVPniTP#3A2x*6R2j+S1{O)t--e0-z?~nK8 z9`g{avhp z5j;<$GGhda`wNA|EP;NGFT^+Y^6ohm%JN$~Oc3V%KG8^yVw^yWiRs@PCcunX8bBum zQ{~ahqIwn_@Bug2?}CiZl;PB25>KgCBdrd8+84r2#k>vJxD6xKDtw#q5*-wczhmKV zInfo?K$>ka4swfCTg_70S~--&VKTGd>6?k zb6&F0zmobZ%U5L;`-I}yV{evxJi5TX|fS%{YJs^wWN_2K+;0cr^`j2Dcp_eeki5G=VaUU4G)zMM)#`NwJ z4Hi7y_;~~visicMQJz^*42od8Ay8`WDW@Mi8A9ELOA#@4DKJj)S8dc5{O@1|;omn_ zG!BU#fTN23i{T$mb6raxWb%0yYkIxzllK2py?Km|j(z1e0}<(I+i9jX320uCZB^)h zI|gD@Z6{A%=}u*xKt=H(m&9Ryd=TT1XVRP8?=w~J#WkM_t$_tcb_y|rr_KCyS%GO zQQ>eorAVsfTCD7_8_@uDdb;K;M#>r6mWA3{5~|ZC(LJ@Kv@1MWkde4kU60B&N0Y~3 z!<*VjjsYgeOF#oUqbX|#7(UV=s43bij-kePXa^A=jmdbNNo*1wRLAUbk3%OPG5q*? zSF9_wZOcWPu(Tv?+%Da`{vh^%k4{r%Rmw_o@;WCr)iLoFtu9N~WHv7DJuQ!g&EzfJva1RtM?Y>hUgxI4i?%^73ehu#HOBBsz$Ujd z5!WX0YP9wea&rwH)zMddNW25lckmy&rwf9ljeG5`XZV^E zSEQ+{n185{C0fzsJC6B+ec!l^VgPze_sZngKT2>s|Cp52p2^8S+&vy(KQA?Ne8R;2 zofp^y)u|l7G5$@|3Be4dQRLJpnsXn9`C;QDyc9TCSUcTziRX4D^RjgO>#I8RM(L4g zKQlksH%AAZQOjQH4%U@j%H8EL%r;m}x`@rNoHxs4GUaFunR#o6$AN5Qx^h=$!31QD z3*lpd8COrUXRa`QE^{L7k3O%H$%d%-j_a=?Ixms5{jBEAa&Nd#2VNx3^B!`MY)3(o zaNlx1@<}lt!vCYr#K1^k2#W zMG_?c~l}r@cpcQ7B+>DYMxUm*_NqmdN&GvIJg;i zUr+bV+56asp1Ra68yC-PTsm`Li}wso(rf+b^gWZIj$%!(HmzZG9j}H-E-z#V-`Fm4 ziH9ptQx5>#se{W#DWqGHyhk8_MOZ-wxIBIXek@Daf?>pub8+mztL}ZV=2_p>df2RR z7s@ZD(GSwNo~#hT2;qXlb`{{SKsi>={#MUB=tykE>0?O^rJloc`_dzUvN(wRF4Mh$ z$P%tIuWBSU6xt3_igv!fN2oh0hfPMG>K|b`)oow{3{r8i)_Tg5tuP1d_BP5WT)tSj zrc#f$DexI#Ef?65VCJ79)B|ieDA$XvBeoLApE2)<-_V(HToVQ88LnTGyXXML+?MjGjNFpqLqVwlDx&XGk{ z_H&e&q0V{C>k0y1gm`@`=OuO(ERuG7T-ao9{GQHSDn z&Gh1a6LUtFhQ%rx*#< zBFKl*;QZF7zVApke3{J#lDHI`z<_U(GUHLe3x1Im@vXNkV5CSDtj;VbOXq)ge38=g`~`zRKa_<9_@;J{yRr(%Tjsq< zu8wuHIi_dPSSI4Wk;f97=J0c*Pl5}EBi`yB^v`gihy;+=Ao(%B->qNK0ci!7YTZPV z`PIO3_PTIDrAj~T6-l{Ma4HK5)7CSOqv~#@Eq~!@x5}%pPJ>?6;K7{Cm~`Lr!lU!{ zdnUe$DRV8^u&k!IM%T9{T|@!8U4a&=4A-(wtHnu{okh-~%?&DOU9Xx`#bQo$Knd@OM-QXR$&M-Nn0dyYN;WieApXDTz{~ z>*0C|oJgJ$E_J_35;O`)-ajJMRd9X2IuU=>2Yt^8Kx>DJEZkE^t|RYq=G@`4T9LhN zaalx*9-3tr&4pu8?Cfkb$c>zv^n`MCUCP48nn~Xnmx^6v#zh>`6FrDb`K-$C$RgBF z#%-e7OmvYj81fmYCmh8bCFBx$7!=1ypY|4H_h)XxQTUHWd^2W3)JWg9Uy4F!c05Ne zgWsQ1JN@zd9Am;y&hbTbs5mfq8(Ozf?daraku7>{R`es%%F+kToFmjfoE1%O||b0&d!p-hUN-$Mk!P zy=t2TmHGmsV6i{R1I2Nyb;b|?NV;MMQJXkGh^jn6Co3a{Ud}5Weio>JO?KZ>4f?R( zl@@InKbc`COICk{E1LUIP_k)%mM{I6uk-ZN*qH#|UH3xYRE1NUl$ClynTm{TX`=~X zB@({!c8T)(M}M2E?|5M^^Hi!Z@$^Y!A(^)huT7n63=PzqX~mBPg8`+A{ZuxIE?7X6 zYI7cab$lWx*ius9G3V5hRJ7H&!vqRx5M?XPK{Y+*Zwg-HmTTk5A!ywCh`LMZJJ*>q z*Q>-G*7a)ks*Fj0Rjev4Q=%)_^x7qO7;ZX`?}H{oM5id_{uGgr^kgRhqU4(A4XZtg zeMc5zJ;ZpToyNajUI79VJU$(%!Av4zm84j-Ki`a7EXAj!BM&bZ{TQcx&oHP`D-=pbK=Dk z$jbi;iaS#`4cwi2wO62hxJR(fS?)F*ch-5E<@BrE=xW0bAiEq|HiOfOgZ4KorHQnzPh0aJ+ugK0J^g0m7JC(0P{nZ|`xESlQ z9*BNVa$w#9%MLWr=DqUG0Oop9n^is+XUj3)X6^UcoQ;~Lm4 ziISV(l|_!XgC)P(qL=R|=rgQtV4Z`4r~(<>Be2mAAW)M4>N#dK_7XJbqGcUOEW4?O zfi~BR1RZGFGmi;BBpZ(z*JU<}Q%+0;cg&&wFaCw}_*kTI@61`$$Id^Hb9^7T*-%qM z!%pI{7i#~gW4i6KC-ih*SpFO-JGi#@rwoAs{=>N#a@|M#R&p(tpK`Z}K=0RG3)Z^N z5u}*c%Sv>(mafju^)R*okuV~MEvV5^W3b^6Y4|K4bDDb0yF9QVnBFYr)etS4vls0t zRF<1VRu7IkqKk#-H+vm~zHeh!9Emi46^iH`nYP3zIt<-^NlEcvVS+^DuJpLC$NJ0T zlhmUgVET7l-vRZf3F5QVf)YNd1gW*?a*+a#Dnq|@AlgL*`nYxlIUl8FAXneD|HTFJ znALohkbX3V2ENYctB1QkYETDUxerKyn6K zKbp+#P*jEbX>PK_qn)}4f>>e_M>8+njsJG`%`fkbOYDz5xaUfVl~_O|H`SHsnL@j1 z87{yQ@?I>iYT8M_&$uh*}`OvA1;8hchjTe3)4ma>A`p7r{;&F2xJof zVxF~V)M_llR%C{FYpB6u2U>;{b| zi~ZD_C=hny{x0&uQL@D3Xb*#AM^=>BofN9BK#uo6B1x6OeY8#iFJ3`H^M^%_`Yt|cf+|0POXi7>~P;Mx#^>u zS;GqWX^dNBwx=>C|4Q%N6XS%l6#fGwj;DAXy6NbRLjDh>5Bc~2m`D&ggxPEIJ!1=~ z-l)nhd6RJa_xCW{Tm6hFA?H*)KYCyAKbSY=J{CGr_E4=l$dL&kLx_PB=)7P;9})jn zViHu=MUV?bVMPp-x8a&ZH*9OP@d(uPJ9$hI&@%B8O_)OIIAOB&4>?dqIY*GG*V|#1GlOkR^)=GGCdpuH9>hpxKcgz;9=yUD)RSy0 z-H~TbT<7W@dr$1_F*y?@sCgJwJkhQvTgvjvz>(5NW_$z3fx+?-}CJus1(P>{Pdg$;CTgliFB#{u)Wu2>&H-r9Sn zHEJ3nJZ>j5lFU;&gyxG|<}Jttk(M+M($oEF!QF!+V?L_d@$$wlCNj6L#)Qsfd1|p< z`ye$AU98pgWEVMy_gJ_(o^e_|P*B&MM!V;;CgUzx+T2qSQm$@OFF+r3)X=kW4x91GwIQ}$o2y!tF`8RJ(`uYFq`Ng}L zPQCH@eqk+tBsvtsbLo4&>tarqdP!pMg z;>qo&8lUG%nNrr9((7?#HIOG_r}VLN%zh&F7yZc$q91k!SwuvzP-!b^yL{ox2u zlkPoqn;Il`z+v z@GPATfLTHT9yQ{&mq0n)$x(UQqMwWYi~d_|UAM-{e<6~(e(+ygo^|oB-~NUf{$II- zW1tyRKNB!CVOne0N9+bEuK=KdlvmvUtuOwl^ZuwQ__l|T>3JSiNqGtnD>ZydmWD`~o<-8?=)9*88ryOK*vmmP5=S$ZMe zh8?|ZWb`X^bsh>SAe~xhpqH6ZN|w@BsIV&-$u00N5NRor@w*k6m=XNXsI#k&n5Z?a znoIS@SD}Z(%MF6G{ghL#Mn8*@)yp+2SKnE}p9Cq`!A*k1?Wif9V}!*F%j@Vy9{DM4 zgV^`MBh}&>rv~!YYC_aRer+C>*J5A-S#Kp*mKJD6ULSeP$s|5ro@Y7a3)Ur0KDU7Q zr9rvfc9Wya&gf|5V2Hwi3ROjC0&F?#)Uy8&jU>Dx%`+juwT|#(?Tv`aep*HegY+I*2DCF)^qj{w?Sg)3}JF9~CrmAbFW-srmf|WM7MZ7lCbb*>0 zw_Hdc!3UY>j@~heI^h?y2`nA5viL5Kkq}*8G%})VGQbc$hP^mwmkt?yO+(JExO@Ms zfde~8o4#Y3_5Jz~demG9GZzO82+=*!NNx7-1*rl|jug&X2p7lnZE%^uj0C!HXur;M z3OV0Omo->7XxOl}(vlwTQl_XcVcOimPvUD<6aB{ zRGklPyaJX*wy21U{?f`*%$OlI$LJ7A z_i$DpfY+~o2f#Vo)T-fNF}@!;w$fq3-5nBhr0_br%t0S8l?pxO)=#xYcfNJhhU;3SDJ3;_LO6)==s@ zjl7xGd3cj_hP?6McKfp(JmYuOqASIE$pP7(TBXLhUa4(L3i!Nxj_e|hn>H6;2TuB; zP0FZ#tGCv8jy*-zv2IjcTkAf+(980apFVL%7ik-h;^&AsN_hXefTRfaHyTflBgWKA zy0v+JsV_V>&d1E=0fas9b;4WPST~T*Abl^P3AD;|x~f7}^)?qk(nO?sXpEY7w|^^e zEu`Y+$R9-~OKV2!GM(!#bx{U}LU<-CCSUK!)W%eocyuIYbE;W&tIevl980T1bOv3z zpbXKeGLzSgO9}|gaO)3oWGbJazFD7RQ%%|(8uRNHJBD*IFkVy4NfbN0Ib zUTMBDSTcZNBh~eQh^=0pEcDhxhc8b4AX)$g!@oheS-ya-yJ2qsotL`G+pt@sOO#!c z12$%owWSm9`QDInaVRG=uXJ?xajZd(hNVY@`AJ@NQQMlLdL7Dap_`Zh1$zc;@hZ3r z9i`^p&zxSnU3OXb#E_?1xL#ywEWF}U9_?0(NlrVHP$Ey|S&QZuqx_`r)#TChiNaFS zArbNY+pM%0A?=DtIjUS&rD6;pUK z$84`9NaZ`$O5D+{$D-z}Yv4a>;O^_&Y-ldtDZA2KS!a;8D~tJ;k6j;f^L>T2WQV1= zZ1Ykam-M4ieaLXs+_0L z@5FWLfT@`l;0~e!W=iCsLdd;1kLvl_h8vnhRpO+OeI4>u@w8haCZy!bR0f+dbHx&v zR2vfpWF|ieqJPS(u|TpVMM4ohlG+r*SvKP(<@)848F0gxZE%?lycBmR`1-bnPD`b( zShN*KQ|+_;iVkV1p6kTZCF+D{5eQ;Tg-bf|fQqek>ju2neu)T2ggCN z*hT?QKpfNuUeX=+XAN~y3uiGbt#BY<()LKaG~zZbN=YCzUj~Q?de-Q&6kA$Qo5=WD z0rF8WBleTSFidqM?fLFLCVDte2N9;fIZhG{`)JLbW-<(OT)zg;I-FCbB;b0%|4wfF zy+Q(>!K8Nd(tjs6O!j*TUV;fK4`N_s{J=cO5Bpz!D~p+W{8YaQy6M$lVZ1#Rd5zA} z2x%0FW-Z*H9<}x%Cb0E(Eb7}8PD31b;U;E@TIx}>ZPCc9N5j)c7z}^hhI$Z~0((Bj ze@NkVV1gMF#Jt~~cot)!}{R=m}*vhqc z)N&KwXBXB^9uxIzCUbz@gvjTrK+#fO*6 zTq17n+<#S<6GL#SkjXkLqG^e)E{}FC=1aM=Bl%`>)W)XDS6LNLmYxYT*f6*DOTW_1 zA#w0G#vxi6orNxb;fAkbu%mT3QUxbQVYnR6QvmZCM=Y%Z{MV+{S5{n)h|)YWG;NMu zNy>20WlUUeiWa8M)fbu?`S-X5qc+%`8R+n?h+v1a1&5(9YnOA{43IU$e{h%Z0*|ep zqdz2ZA_IPqxu1yKJ|;6JP1r{#Gd2qhtcU-w#25AS;V$9a?+$(@vjG*EN6~j+H0=-3!RNT>X+!aUr-EpM-sBKw$wuc3YV@9$;f8-i6Akst zL1OS1%+wQ+P_C{6_!T*;HSgdbeF76?A1?WtaE;(0O5GaeCYY(|m|?g;clA9re7NTO zk}}~YX_IdHgSsODhccKLGUkH(G#r6vy0hTmi}W7+V}9zSw|>S;%xR92pwZ*{1<&Jg zozmq~`yWgwVi@B;B7mGu^B|Liq{SZg(^imMw|$4G;oB`x{>$}uP&D)9@3i6DC&aP|-3oEU-M zls<;tGXz>0(@+?u?`T5F8(KU`puuBt2q^oaiq=jM{T)^0NW{14`lrH=ziBNX9vAsy z)7O~OLP=CT0?Kgnd_|Qu%86b9WQ>S zkPh5{3DCVv@*dVOD@?sIdwdtT8l6JPg zjYr=zChWh(CZ7J=j~I?OeCoL*N1SU@#6eAc#xrGC8QRK>kAlwRXNuZPfv~A40CF5E z8s$dy1tehl2jDQR82&Xdr2*d#DCKMuJ=ckI0i>V$AOi6dmL7E+G$V?Kc`V{0$1DZu zEMB-{IaQ@zQ7!xjRxszq_AQ@S9kZd;8Prrk6ARcpUwZ!;I8gKo=@>WQSE(Z^I}VXWmYg17_#;2zJmvFNAO+o> zYxV>*ABaNR<*01I4{(+cqvkX)p-k^Lg4@BJXGX!3+@KK`EAZTU*v8zwVO(1cFeJ$G z$4pVg`r#fpEAS=ar>MYF{E#M0@-FC^5!=U$mz2`M!0-j+&8GlD)`CVg#a}eVkUo0} z#xNg2p-V%z7q(sYp3Ln734aads$$8V1Hi6m7PwzL!Tdvc4-2S27&FBrpO>EwN5$Zo z1Jc!KzbcpS<*LOhU73{5ndl9DS-rKTr2W%+2=E|-MK;F1p|f-f@Kf|S!qOy-m$fUz z$m+lE%LMwzs(}fBmcWc#uvPOV?ZJlkS?H2dExjamMDh`xGX+to#ObP=CayA(q{=n7 za0$WkJ-?yoBh>5{2Dg$GWNk&OYoyTy~|?Sa~sW zkLf#)*)b~JVd!sU^oE8uQE828D4eE%szN!^NATiX znSZ%eEQQY9Sg+*nW!)9CCbfHegNI)-Uq zZ6Z%5UStoA;KVYcTn%DvJ8nuR2~+qlBF&H-T~HDRU@3b6G~!h@0ANghbnX8JAO;ZH z0z)2cbe6i+%lnXSisEcq(nkDl@T_7&7`(Z^n019bDDGF-_&@>|8BZO4`v7_ECpBo+8{PeCPe5 zj^~Q$S&%8z1JWGfnn*w$5RGB!575UG1ydN}LwyQx$78BYTb^vdx|O((^~RMIS!W2g zav`}8T` z#xBRT@{-Zc*iMTUw~AeDU}olr*OOVwA+Ks`9KI9$gR^m1rM_1qz1<^xLiIW+p7p#k zy^8NFb0Bj@;+`1#?f{zn&yF{oj?s&8Al`XG$JK^ncFo1N4t*VC%Ja^h4SwC7y3xyu zs_O4~e^8T=Z?;+ zJjV`;$i$1L{O1)jUP>vE&*w>Y$O?MykSJgHB{>>k3+60p>dI0>6102Jw6q9mnY38! ztt)3*Q`@2+lGe_KXQn7+hHbg4iE0Cl>7EWmW?X@EAW5$|GB2oAl+F;5#*lo9KZ>O% z_S2;$-M%m_5CMnU9p#NL!=I-RK0#ql(qOXv!7J2G%1C3ANp11PE-7hxk+Nq`Necfr zfs;ma`RVTl_<%v-S{;fVrn24%-%R^3 zXXjmN2_^@_%)9^3>R#*9=A|Tl3uT#aq^zIO8TeXffC{tLU}OrRxME{ruDdY1EMWaK zdu?&a?8H7%NeMoHMn&yp+w*Dys1@(7Y-=IOn#vd$`~n5EnQ!y5yyG8=($0w-_B4(M zt50++(pEH0=HR@51a+7PYfIUR>km|mw#a=kM*#K(Vh^BpF;oVYggBvKw2-_saNu(B zCGY91PJnWSwZQ!YSAdN2gYiJDvH`VgV$p@-iLB{h)|#8RTtRIdlQK5z&OtHb7e^h? zz@Esf(8w>q`^OFTLkd&00c7=?Fs;eMT+js7Yi|{~gTSwXJ*ZpLN9@5d=)xJPZkMOM+jHO=YymMKq5ft4ln%k5&#l8SXj6~V7MLiZ7n{M`7ZLVyu$cTF5+ z1(S@OR6gtJ-NW6wX8$5{ohNr4>OY(a{4a5I;0eEj_ajBIj7d{(|s0S?T=9|3tN z&!)h7cRpvSohOyTAJ!)ZzydKL7A1s6+z#g?viVv8&KY=j$U7!dT z-QVILf+?_#C{kQqpq~Ei#`kpUB8Q}*=}Vxr)$V^LHvaeC;QuV|VVEn1*4R5xKj!~n z!i0Dd@4wE8_}`s=@qe{6%*Y{-$>W9w0G}ei1Qx(zb7qU`xnPDSGA#gTubO}t2lJ)C zAstN*20?u*0nQWr!w1aOHi97k1c>yDf5wN+)%GrZ1Y!T)46Ilq+T09q)n-_ltN4{_st`Is;~_TSPVswq@(5$ey>h$ZsDsOet}Z9^!NNV*XOY#U7~QfX3!k02+4uTNnJ5hG4qlVikZRXz_Mhk+>H< z1)f9(UQ8iS$(~MT@Vtn0t@h7XYNuzk?gZs~2vc;=0xxbw6(_o!Sz+OceM6RN#ABCU z4HFeFfuapCwPfT60&qz!)yjlq$*`SvVd54jo7tb0wIZ5CjOI@kU$`o zt&!M(*lc9wdh~2uTEWA*>CW|oGwM4mU5=cIy`KNzWyipu>t#w{HZLINgX4eeZwHI3 zjkVp{ee&a@yokZI540Q>eCz*WG>i!0XN(K6-0+9cZdn*so~hS$grgmLg#y znY!D?xxcJ3zuwEL$44h(h3%UVo0UHgK9aL3+p+a!fQbS!>#&sTQHgfu_<`bwgWh99 zC-pW$&r}leW32eQJ|${5u$xMjtu6j|b{Dv1=Pz+@LQ-T3sIkL}-ZFbkcR(I_Whu40 zx13z-{ovbC_?zan-(9gTUXjO3ByNnXzQ(&J%Pyf!_**HP1%%{ISsbgQ_bZSR5vIR%90;EI9jQ9 zxy-T0(^l2?d{5V#wZ9*3PA)v0F6W}^zW#Z|P_=@kckhRHWuG_59STj#9q92g{(VC6 zp?77Ek4(u*hl8gBbH>UuzhJ@YpG~*_%3y|Em?0%dF7U;&g)$C63Q!j z4u!bc>sh6{ousTh7Myovo0{>JR973<00r(NeoeYYD%u095TTKC_nfthR+vJ1sjHvX zfs5HSb?L`~DVnc4HH&}bW^J)?)LW`iN@4Qn4W2DY&j%H3%Y17Ljuq+k9Ljb}PIeNL zXS>7d@|A6}sjg?%xVmb-seKb+$4tthn%Erh%B@KE{k-h`Hbvi*)Cw2P%&|F#Yr$EX zi55OGxgo_}8V0s530FMNRo<_{DiWR~r<69O6!&00Tk0j0l~yarNQA6sDFq*y_kRI%k&VyhxI6yz0D+aV+7gr96sZ zWBO>OkY?+XfD~Wr;aO2}KTGQ;kJTkxTig%$zXsQ?HcF77g*u+IeHpHYsH{oX`5c^k zqb!-W*LK;Bhv92n9fB4R5(z4=oL}a;1^e_bAbg1h3y8@np`5!|{K(%ZRlAYC?Ij<_3w)RO@H!+VCm0oQ`;60 z95NFHliySXj9`1gt|TT>CfSOV2jv{Fjb!nc66o$117YU^f=!+1J`<|9FAVpsoM>`rrHE)(7Y%*q{9Nr)IFv3t$;MV41`Q z9!wWDJT$-hQ$;WUUo+8mDOdAOlHt(jG$mmkQo3Lr?{q8iXIB3W;p0)d!M5^fG=BlX z{Ye-BHNt)i2vKTI=mMgoCJ1b!D+=N#0!`?t)DA)xqxT$-2Rnb%qYHJ*P0}6Z)5$P! zl4%g|tP5XDZa_kc^+zI^Kmv0Uoc~sF0>;t&Ta`)%31Zm{55QuPFlKyB@$^&9&o@`2 z77)vJk{$ZGl!qCTrpQ)!1AP+`!+!eUH4KRev-ipD^~DQ_x&_2n!yb%izd#p*huuCr z7#&LHTDMJL#tyd*lnv;pX3xncX$As1nRI2KLg_*Ze1A>Sw4s8!%pvL7EX~eh)5BGd z;`dEME$h$*7JUbI>NsTB9e~ryu4_BTj|P!paF~=I(Oy7wgL?i)$DPtiJs&WT*}kt< zgRwgPA6>J67!Rp&ya6s_MbS~y&>x~OuL8NrgbZH&(53xY&e_i!y(5DzMx8wTu>Dp0 z-J8?JXBr}N?wfh@Xy(0L#;c)UA+SM$D5f1)UQ)vEXZ!Kf5Wkg!1kT8U_~R$QOj~H= z_*aHpU^}lUyGFC;GMMbLELH|JzuUX=E9kJFh379)+-!Z0FCcES&y7m%xwSs&VPUGy zEkEp8`CBc#yXWo}F(0frNBnA^W7Cvtw*}9G8e##gn&mgRB!5=81==@wiOK*RGCS%W z_t{AN!+`bAL)8$^q80-#oc7TbWrXKWf7|BhBu)G5W|laEpLy}+^h`UiK0HmJckwK$ z+lJ)fh=g%``q%})5M0B@Le%s|)&50(lLGwauP};%9qDPuR^Vr5S06ew=BdAcXoP>I zWx!yzbo*ydj4%;~E>C;E4sZZ5nA_~AGIOKI7L`YkE#P=Ep3Q|y0DV61Rxkd3L8n09 z^ZPnE(^a6#?`y`ciD{LMTRoK?#X;XzlxbUTgJEsBfN(bPw>1SdmcheLj~j(3_LmtUBv2B9fja2B za!QDTD@BqZ|8!rfS~~<=}Zxx1hpBbTc8x~0B4LIR{RZ#lbw__jPR=qq5ZLG_2Z*`Ou~ z2-?ct14mOH7W&;KZ-JqK@0(`4C8YA07w5MvE7%2m9~0-kgLf$*_c?@u8-SRAk-YI+ zkeJ}vlPI_#8b1f|w0U^eIYSGlBW*jj+^Lills}e6reDM^3v&>G)aaxi2PYqApa)Yd z&cVOJXP)Eta4|x_Z%)p+0)E+Q93BCnxMGyM6g7$94Y(>);In+B2n5 zn?U7&RKDRTJ@(knVsC$0xUc9i7}e)rzikzq9$!FojQ%kUnluF@l_(Vk`wp`0t^O3k z`4B(hI|O=i$^1a%J^a-`3)e>LOh1Cl!jJAnDZF$xOt{yVH$dVHNkd8n0H+ z=U@{Pz`KLbQSjZ)&QY2EeNjbH-rft5fC47}$VyoU;E zm~Yht85fyjZVA4w*oSHn238Abpn|R7_NIUBgD;>5Ko5U4>;SSd`OyRyR5Rh-JYWT5 z?d_pwB7-lNbG*wefEy;MvD?LwR8b?#4M4Y$*+VHIpe86D{1dpHISxn|_w}X!QJaHU z3~TjRQp)9>(WmF%fK3cSrLJfE1RK$Ze;RiQfZmv_5{(qT#Npuw?(Kksa=D40ciZov z5BcUz*D|#RHMo}Q)7O|bc4=%klqozRcl1&!3_XU=wlknmdK35X*b2;fE*>bjiDv!^U#1#u2%N51QZOYBjzKK$IfL3At4e2O5lB&nnwcxt+2;ZkiRu)pI5XJ+0!yy z{g2kq8O(15d8pscgx@m19{woSx`uc-9f+>joVPp2j^`j#{0d%0mjAR;DRpQ{JJr+` z^~|_?*S;CzrZ&_;IqUK%Fea5Z$;nV)3|mZtM5)nZNfuC}1NvCLj$Fef%|~wt{a#Hb zdw&J{yqExHZ7s; znfmUudC$X^%bh;USE$g(j(kx#d1UwC$^>kTo>V|_Du2yJL>LhEZ5q7tdg5SV)BRTE z9f#E%S!cG&8YbS9zKYrmm|ures^r+%NIj4S)*=K$D%o z26zLfpPU2ST}B@^huWuO@Zc1Nb-_&|_k-Ksu^dxLZ90!XHI#QBaccEN>}Scfc_`ae+Vwjvo)7CQQ3>C*)^hy z-x18yMedZ>i*6iFX^T)-4%#Yz^;FU_%I*A}D@c=P#t~@8BTwUadcR)7t3ofK?3xv0 zXE5UxA698%^zmrAdTzza)8o&NNU2E&9v#B`7_)P3=0>6M24!*=X z`79th;{g*a3S2Pk^pw2t(}70wG?0W3gky#Mu6T1GFPR>3hn9scyGC2V8%j!t8f;|x zP9_7HGBo~dQ*@vQT~PU}07Kn_sd;;vUHK~?YBmdTe+7;2*;6Roo8Lq>1hQxz)drTc z>K6|3p!P02I}bC?tEI6pH$_E~md9iV)%l7Z6Iq6R85)SoT}XC&?PDO7!`8 zx^TZvA~B#X?8AviKxYNX_~vNifk$>WO*7qL>17t`uDXDDcRYrJEsWkx*#g#>dar&} zR_v%Ji&FaRIc}>~?t#|`S5Ob^KM$wwe0%m>#C;2&j>Pr_M1HB?4M`~m7bAbDW5ogj zk|WC=Bn{JgzM%GW#Y%uF1zwV(+^+35Tb0*e zH?mw&*CBna^SwLLVoud1pAhib9r5&sMgJf8523>Y>{IhGmuH&JZtDd(*k4}Pl7M=c zw4G?b92Uh}F|O3VfVc-(S1dTN!zxCXWe8&$GuvDmD^j`GouTVC;-jbvkchOeuMod8&+sj5}RnShCrj?WX zmtVPRn{-XPBr^Vk?bLoTW>!sKe;&nJ1Q8W{@d#y<`x)=HbD4G;~#L+O`~%>0;6BYZ(+jh=pA=HON5 z5X?NS-?8UXZd7=qQfwisJ+n90!dn7cA8DnNzH#GlXuV}Xn1f#VnLMNj5*CU>AuAAt z$D}pSJYO*g*kcyVQ_0Sc2|B-q3vtp2&NG;XFB|8dd!S@|PyjqFVJRcs>|IM(3Um?^ z8NCI}%fQ3l3y8dOg*B!?oWIq;cRT?t!T=}_upq>Xy!z8Ng;k_feE4)?x?rWsxyL{* zRT5nPfVIJZ(jAA1x8|W&Wj{rp$=|##@BF#vG*G0{5LjGi3O*NtS)==%wkC3W#4=`{a2DcM1|}{&5bph4lsEavsaoBRR*R zn+%lH=^^N~$=-%7T42Fg58NuHfp-^j)t`IqvsJq%RX4PCwDZTRdjVuvpZ2K|opqD% z4>r#$Hd_?$Yf+1EgLw_5<^debC&G>bLE~_QF=6R61L@2S}s0u||WX`+qZg7^B%)16q~Z`MtcjIF_it6ToW>D|&Op z6uct$b#@T|51SJt_aQ&8&i{_%LacY_Z&$K<(s`7PX@ssb7w2HE_4@37_nIT7oF$)O8j|Y869w32m2o1KMgWC2? zx1*>NEn31(iL7^TdtX31o;tJTC~~?(r9(ZzgT5Nav%nuigI%4wGF!9R{l;0tJBRw~istYlCFlXC7ya!zwAi+Br&NgpG?V%&+bPRR;oh{R6;!a-d`jXmU`3 zzK*jpM)2I~+nZZ-{?9PJaymrKAM|`u!18JVa-jJ5gALOU)|+BVM7Uun%0}bMFyPwU z#}C(RO}lmFm~te2RG?Goe}`f1-RE@dn~IZ$&ql@MtAbS z#S&iA3)`V&ozj(iPLUMXQEZ-EPGo+=ssOG_seq(I^vo9!U+A!F{}Nt?_@`l~E+C9$ zwtoUpcN9q1c=(sqOW2JBuv_i^Ua)~Gkh8TFpPBmd+$hS7_aGb6DkQ@Je}Xot$Er$# zmypP4^mw&mE^Ss`)$lV|cbPQ1d+#v{(68Xp^MT)z^R_)af8yHRorMlVHJ=>1?JvZs zj5Qr&5UMXZ&dfL-sM$9q`uJfjs)T%Ctky+CbGA=9 zobcY2ctSq*b=-Nt{13kPZ=#-^Y)~_u$^*%N00fT$>7+)7J^$If-<~Ud?6#C3X#<{Q8VvSzX-w+VV3Ey1!ua%O}&PJ^VRS$X*V>v`~RWtO~9dg z|Nil@g|@L}iA-MBs(+P5QW$ z(Pu^4y|8m`09vtfEprMX6IAYda}33LLw$ECoOT%J9y8e4rIoB6pW^!3!B%k)+&VIC z7#e36wRL(Xk|Frq^(M;WjbyP){cE8!jQVR=Ruzmo7FaQ7m72jYo+6mpg2O_WgmDy8 zHA3*^r-Nxb`qroT!EC5-Q^&+6brJVDwiMNu_Kk>zhRQ8#K1^^p5@KdZf`o#-IGkLO zIkEWw#^l=(0c|fGLTPIvlqSqSMX>rh7_Xj(_N#ah@Ll{xGWVJT#M&-46F|wx@2I4= zZe!!#gM0)}{JF2kGrPd${)}wPRa<&?4ut~M^vBmQ`HB(XF_un>6d@Ph+Zdbj7F*lw zE9dSvjmrP#Fm}c>gcq4@-@B^1r^F^~3`WziCluNj{IqfKy zLmujhIt2#hZX25~>(-p}fn3afZnzuEUz()nei%99*Y;w@6L^_9~ijGa~zkq?0HgZl@PtzV4QV z(7QJuVTz#bU;o`G8uX1|WIILR2)FAT_^}}TE5bMZk#X!6b>b~VVGt4%AD%IqPL`;_N62UBe8vrg7>y3j<9@?c zO0mq0Ea!VYcOWeh{21Vvb`)(6*1)dd#k+$~Xm0J}v(E)i$edrOg+cyp(s0g$x)Lgi4|S z{ezvt3MgC<*H-<+EdRXbf&uNzIZo1?{4I4QFD5}yh^P$4_-bGo9d5w_p((#QlfVC# z-SsmTO{+p>S2k%k)cX#UA?59EVZTUcYt`ouzkC<4HTDd*AZ=@dk0XD3$5z*)y*(w? zhub|Pbk=o${iORT0x2~ZTkIfFH)Gx7xkYLcKDT=eo#aq!heUp#6zYX+F6&@&XK zA++CdDBziielZaL7wMo>^rh`});C?tM0af&bD`hI924TBlS;+f*=X^Y^*z3;tXrc0 zf>sixz4iXgBJaCn;y4Y{zv3b;+^7DxGJqu5a_{HI5P&kV$?Nzp zQXo<1ZNS_i0X-!=!OUA=e!-ZQA$&uGFM_~G7xAeWbNB$qvA~|gx5p`K^5l9RHWv=k zaM6w6t)@~f{HG}I%B3&2Jj{B5V|LSNuL9G5I5(EtaAzBMZhMGglO%r*j*uW8A+VI?O^ZKu6=g5z45T zxi&JIZ4(-VYa%L3bfjNxS|aA7kwj8vvbH~|u@Mv`bC=u4=@|qSd;J`?5+I54iBMSx zWN)?RV!z{kGz*^no1&|uSy!#%R4S%l=ilFPwsU^@$8xJ(yVtC(3*fhHd*sf4h)?H z`b1Bo;FPz__DRSL3ymh>R3@hhYOiya!BqB0finP0Q2Se|Aux{h7^HB?oQ=PETKAi$ zwY1?nOt>P@y?xuAW-qsaL%Kdcv=Loh0+Hi_KP$r zz_WrdvyD#~lW~ON=p!0W4T6AwTs3O@kDKee?&aHog&Lz$Ih2v;H$8;q?_T}hy$40` zo98*<$iyfe=Eh_-13b_N6;Gz%azSv-BPe&pK|C7hP{m)1lbOFr`w?>{3uep)`__u` zMCOvAR50EN;wb%(Ru=u@OW0ST;%S~vRqI?q$qk8eVvtI5K8g;@MH!BI@zleu15w2j)#JFr4qJ6*6NgS59;(P~ZlxScW7&SLY*x4Q zyhRK7u+s{mF@BLYv}1%uod7zuTJlgB**nS|8W3^Ro(R<0v*!|kaM3T)vh(?1M!0Qw zA_|zLeJc2nyEp-;px=0bUu&BG-K_p^b-rMNG!30^5lNToa~Ul`{fSY!7iPYYIzJl0 ztB0JIvPj-N&yf=zr=cuT;CqH#m%jm9OE`l!z=WK zR{cr&wY$aUo6)(Hc5M63hYmUxA!dqb{e;=ex#Hxcp)83{S+go13?pJ%QT{Dp;pB*% z#m`ER(3bNP5IORXS9Uw0Jk6=&s@PQ73H%6~PaGWf^_i-MN{zX-kLtyvL(T zyS}^{*;lkd=cX{DUPwb!E<~+M*?peH;UF3Ah{^fEF9)`6jWxg03${x$o`{t<&g}NE zn<;Q{@802Sl+xx+rn7ZMaJkeKlKahGp}dZs-u3mp2{GGrZ0Y9?`Xp#&Xa*%NH)+?> zvg%Sq4~Bg8+8TU*_ttYtJ4~$4$;X4?Yx0~bwbOr*(xys*Y)oRGT$G<|O=#yK6iavR z`1mDhK$3~Oe%Fr}nF_gX4o}r{1z(+*0yZ7!fbBXp;C0}d^Pp!&arZbGGfKQbux3?l z$hLdcB($QEANM9`mGf*j&G4m`C9x?m4RBO;0N(lV!*|-D%21L`xS%bkhVFGL|K8AI zywg{X?%IF=z-4q~ijY7}hcv+r`!a`7-pY_>14x043mkMyb~{bMuNF@QGZ@O3?VYC{Bzk?OYY^ExIiFjPjn!_EGf2an{eE5RSW{=0^puH%A?t%KTE?%p`7nOYbXj zUPO@~ z7y}Qj{YfMfM(ROsIW(@`47!rvPZ+Y?Tg>q}uHzXCdKXCLs8-Zr=#EiWP^^-hWef4X zK!b2|-%qbxEwML&rX9&Ths3kC`nI(m9(-P$D?%%8mU3L_w&H?e=`WIEqBdc?!Un*6 zdEi^@($aBlYW%NLB? zZ`gTvey_Bil^WasXeT0B>byM87*Q8g*U3XwB;-O!^sQjU0LxVmD-;f zDJ_LSaNnd)2l*PvBoYo031Zc*Xw0hhmfHDB1aAz>md-^f?phq{3NHJuO zyvTgN3O^y2KLTNoW(TvE$L#j8b}a$iOLZO8Hw&y&}0%pn*86S z(Z=0D*(|0KMf|7mEp@Wt7fE^4H1d#7IU*s7HWtNS|x_h^XL^P|h| zg={O^Xj*nV(pbxYpN8t4i8JWu7^mn#>3RS&@ZV&5Z}^;X&Y~b|&6aT+t|mtRoK^i5 z#b|j1j);XSz?w-7Fh%^U)QQ!Rp{Fz7B5|F-pBOH=HO z^A{ABe_44pwx@8bu-{cj0kZOvMOBoXn{yK25z2QE+AXw)!Bf5>#e-81J55x)zx5n%cLeWrV$?bbL{5#)&ByJ}?`{-x zt)=O?I?h%O-zo_=XatrO&^R8)$Mj)>Ub;LwHwnSd{#zOQV_;IZ%4@I_fCn%o0DoOe zYBfuu4giSq2yPS*_Xp6qUB+>d^+2Ro)2bQi2jg3FyuiJ|ouR*#i>`7z<@$r_;uJPj`wpV(ienFEm19ViPXOVyNmm_#xGAhz&Px2&*P$fKoR zv*sWMUj&7R+MMs{Tr0SKr7+B|=~YpqDq6L5nToEi$dDP(F4@(&APY=dW6Ux5 zFgo;{Gs2%&lV=P{xy!|I%`eg!0RaYXabfAE&}KY0=GLU&2T2iJ@l{5ZFkHV~8O6%) ztYsYUp%aNGOX{LaeiYqo;18(ex3)W8XgrF)@-LWrzFIzI^}eEff$O7bmoMv=ybtaz z@2J4rru_HeRDBSc0e84CV5z~WdhA#~OaJD&Pbv!K*N&-{ui~wx&Tq(3HS=E3$!{}b z8Q6G+N6UZx5o-)?uyb;Id|6B8-Fi_8G0ZU&++$j;YD%sZ4*OrX;n%$jj?jK);{0DE zi12}-|MK|Bd&?=>HHv*Iy5^j9%a$NE8Vd@}yNLXexe|mg86rg~379K|VyJ5MC2*cw z_iC>KN&G4I1ct2jlU1_ii2V58k2o3$V4n2MZL(mXy5~RsO)f+BeIgk^q}Vdv!w@WNw$7MIq!h|m9Mh?;d@lzJEr0l;)QN+g zT=WHnQp^9u!D$1R_lWjQfKP4ny?kw$q|t2H|h9jV?cd0u0cRFBph$KIXd zGc=AQT3-z}KU%tS#}Cn^J(<-P4}{w-y=dSNI`4T$?FtKHC%bJk50r0@N?lafdgJ7E z@T_=&4~S|tc!uoowQT+M>G9ySr%lGihQg_csVXHQa#s1CZjzar{ zYri!4%r@_ZLswf#+ctk#W4eXq(rthD3)B8xb8p?b9UUAq2oEnfEf>{qbqaFcpv$}S z^3vV&Vsy%oVr@%@<$Wh*x!_{MlVi#oUGBx~0~8H65XZSNt&^4BY+B@%`ccVz&estqit5VH%%+2&~wfDbo-4l|e8nX!=7~ zPu83I$rN5H7(Tz*H5uQXr*yKvP~y5*?#|c9oM;pFyNAKagaHlx2S5NJ#63LrHgFK^ z_R!1xVH87py?40W8!&jrQQ74YtyQwGmC0z|p|^n&zuKb5DP2p+$h6|4Of~c3eS3!^ z))AAZY&o;}PdI1yzu?VWxxXyiO1$dS)%_kDxrKQ`mu}qOzRos2;R5;nH)YE&PY~Sr zU*uj71eEfV3Ti}$UIdk4B9mcqjUt-(DulVq==`Hzh+=)ll-=8@JWeVJvWC<71~Kp^ zbhaNdw!+S(-fw@@dflaFxzBHAgyOGev%8D@r1x& zQ~og&09=C~W8QN`x+s_|90ad&HdT`^v&V#yj&_~edm3}tuvXQhXnBXR!q(tD7_w=X z!8Su*$2_TW&2m8v5i=m-%1MSS%Euk$IuFil1hH?Q?JH;VJ5`L0xc}e~(it`6?@s6T9wpV|8hlUwc4Ob7 zyyD^fp?(7chv1s+9W_2Ghu6KoP>Vg1pEu;ZOOPHGu_nm`Ec)FEIOQ`9l3)~k?N1aajzHtVwH+%upKl={ z*u%dBJiGUkObC#T&p(NBC5hDJGPJ(lVsU|NeG=WieXmaQbHAo=HzHjbLBYif)BTT+ z&=RqaJ`$-*3=+^5OQszsxb@)QCACiSAQ> zYm}BfZnotWvIZ8XS9fTwKeI|JwZ=WWMtE-U;eq)HDc^-I zxTHqYPi@w{ywR&|N4F-s>HT$;cdPmD({O?N^H#k*u3zPn%IygE-_Nq!yx{c7Gi5*6 zgr3gA7!XTSh=f@oFg6H;Y4!Zw%X(QwH4NRSRu)^^60vt9w@A@3&&4)I@$ypvX+PG(i zg6)DWFqai-oHkq;l5R(984Q4H1-IhcU^95@2C#P+1I)*7-cr5O2ly!WjfAm|9u-7C zq1B)PY0+|kNMM?=&DQjuLR+f{9U_~Ww7SBs5`gTUg`jdgK&Drrui*caM?V_pDJbQe zUF^b7$#=%wf034efPfIS^WB4ZO)9l&>oH2>>bpzLI}Z0hZDsH|?~U*`Yv>gxD5)?7MG7 zu2^z+)#0@hRyAo`?aV*r%P2eT3wR0Gx0&+1aElUqwTIha5+ZlPba7zyc(q$R;;}E_ zfg<<9eSIBc%h?92GKbn&iTDxK;9->0I62cBuEPE;0u{!!RepAxPTeA6vo+WbTp36&()kGVlCx5HyNg&RqWSV={SZGOys z+mPcUR+_UZ*mIX{38@#W6cl<~zZ654U_udFQO?B|qds?&a(|IvR8iAd3K|R)Irt+> z9?aVQ2yk3L5{CWg{S*zZcB6RY`}nH{z(i5e@-y(Y!5{w-HTP^~z5AL5P(jGnF@X}& z4z@7m>wZVI5&R18BbQi%Flr3>UzU6%khDT-`_N)o)Qc=!OLHA3Xo zzGSwK#!n~l!)b9}0xp4}f?`0VwfTmOM$2gs#BYIL z{(21mKV+^TRz7S3d<5d5@Tz5s2qj>2ZY$hOGwX%H>CI7zz*WJz7YfR{#ZKk<+CjL! zHC4iw#Nv+*Ak!hLx6&hF+Wno5fOLm$>SFuXPb-+&o08o*9{1+;}0dpCHtuuR!=++O>H9$60!A>qH}WlMlq(_UJ?xx2d_*FRq+_i-8A|0JcP$O{Ix*HPLoB z&%5~&2xfjXvB~DA!QdA@ZKU3l{)OLc)2+H0uAU}J2_=#R@DNJJG!4Y>T+Ro`XZXOB zXS1nf)3_t@Eo)cu&W#CevH0IA=X-XY!YYyPW<8vIwhd#j$ay435(fIs&-ulOrwt5y zaUpf+Er@l)gpqwdA}faMQ&4mYQ65q$xxK{6%K@Ar^vVGC23d_z}=0T6ag zGUNttqFZ6yl3?bH!SJyyW?RSHQ3IPg zP|sdx?|0rTAn8)ShJ&(Dp4=}I#1|AiY&t!)Odp!Mj%L2qu>I0xnaE_H$zVEdsw0(o%`ZsYIF{^pg7W%@ZJ76ud ztdg2}?W*eGoG8IT{LY&O?KB-LnF`O`3?Vs9V_D&npZFbBsnpZ)Irsh|uYyr=#!=q- zTCg=H^1k-5c0KjHpXSmCg}tI$VM~f2(y@s2+EF9pb)$R0)!4kvFy&+fx1EwZ0L|M3 z4GxEe9t1+)q6|p@7V_1kc%r$CNB&t-C-XC8-9B=Tf?hS*kc5d!7ST+3^3s&tNrUSF zQ3#}By`FP*nsY^;LV0!YeeZ1Zp85llth5$oKrExnW_AvN()}@1!5O&`kbYzm%ZIA} z&&PYib2G7-2IVP(zetca9(Mml(yJ*+K}P;K#Ykz_cxA{cFL+sn_r;UU0J0HXJX5j= zS}5eN2g`sK8lc!Lp*R_#s^xO8wT+x8BVQ!sRPV3Qf1Y{2&0c-ZoNZ6;CnvwqSjPVKpVa!M7ioyT z3_0y7chT8gs-;NtV5!~nIBd-C2yike3GXG$4IVBTNI9S7?2;~A#Kfu#;%u`T2Ga4X z@72IN+_Ul%Jh1DSBUkR7MPUcM#qmD5HmOSIeBy~aTZ|&s%sc7jl`;D}C1kuUc)VUL z-HY{rEi_?@@O2(bQl)*bM(7_|+}-I`eb(}JyGdL7Z4pJdYn^PswzT2&Ll!3nqp8>L z56im-KAhb#nUqsBBTL%DW%2qhYYc5~)sG;&olAX;mI=zsowi)u2qF_QoQQV0)lBB! zxAg;6bSZ)z7nAf(G`#n|_TSiGR3CEt!Ay2I+SWd0+IX6OAoYT}7Z4?n9_kEYwx}uz zLn|n-bq;v)QL-ie1rV%2Ie>{C(YU6$(pib;ukAr@Oe%i;6ipfWxY6^rp5%Sj_@M^M zxV&cv+bMc{JGtaihOj8M)Wqq;b-M+<7$*eWLsu9n%Z37F&;DWz7gZ#*Awmku;(T|~iirdLT!x7r-UOF{fDY;bF4`}IEe03OoXAAp8}Gy-b)Sq?iMBYchDNP{)b zl%(L{{zeUn4X0cuzyw1Ri!LxlL@`z?sTsi|m1y{k^;hCIb>`5EUreTwlqmSI*0uV< zlSuf%)u5J*Dx0^q^3H)H9Ref!Yzm}$fJ?$Zq+Q=iy~8b3Rd3!UUB|h#AKSgnGpiMK z@2@cu7Ag;;i?RY`pYpO#ZWVEkzKAkDVpU;p$cblXTN{0xnegUb^3yl_yqG9%OyOt= zdmc9l&?EzX2-;;Mmn0+_Q6fED9e&TkK%d>>DTY?1y*!xdB8;21;1fm27Hy04yNbN@<;7Fml zbC52{<%Q6BeMR>z3VP%jWk#RB1^%^u(}ssm~W0ot?1Jq{RG~X+#Hj*p)8|YosS5Emw^qU?a&GV*(gq zA7#8_i@-n?{~&?~fqxbA%JaeUm#KrdMICVAxZ2$ zfK>iQi1^(<(|{z9l^P_nQl`4kwTGWIz!IZBQ|gB*H%y1lS0Y1c(F%T##s8U=%AD>^ zA%UCGip^qrQu1=co{9mLMcE<6SMOa{XOA4XcaRYt8M50aU#;P=*UM&((k{J>%Y`KZ z^>MWy959CixwU%}&co!42qTnUBTY!uuoK846TFtNa#>OSqwfSMAwIBug1bUPeVWJQ zRPZk%=sq9i_H9F7!4wpT-p$3oNKc_OHwJ?=XC6h9FqAVfwQa#SdfmRGSDHW@pr@UX z^LY{V{&3b&eLN@#me}?%ht?=MRcsGufSar2!_+qu6_rtt5x8RcTWDGuG37flT%apv zu4Tm(;oDi9*rA&WFtnq4&~IAzfSCP7G?as|?#1VpW&R@FyaMA|F>6s_$D0r zl|jK5n?=jtTI03u>FxV#H_7Rh4f4_k1<-ky%4bzqe&qKoC#SKFIILsf;ziaas$-`T z3R^!@H%uPqJ-@p#oxugS*wzW=3M&KfN0DEzU#08|xM~orD$M%kte4Sy>I#VP7iLC> z2B*Y#&+mzgMif()Cg}(IKYKnnx*djAH8n&2LMu0&wZ5ymvqt>~SHyu551k=mg2_}j zyD{e96Wvr4mNiHNl=toaL2zDbu#lh-is(aVnK*?4jS4DPm?H=#3zKc)8Y7s!{`%(8 ze0u%>M<IEs66`=@2Qo8(RuuhBE9i`uXvX z<484n9H%^F*5e3vihCL^>FN_<&0^%Y0b|w?(g-utn>@VG2jL%@wd)A!Z2455f3igx`#nx}|c3On+#cwkvYqjpd*&?lhh70BzP#;>B@+w|?upVoETH#SoevO8vC4PY3Oi;xfBJJ7@nZA(x!E|~sO0Ful~ zXszk=tAj{DLbYAviIO899S>a?Cb zg?#5xT8LupCx40cm|FN%@?@A&0(aU&bA;s83B zm3A{g5^OMS_~ll)rPtxdnm7Ui9(KPhxLt6$#27a*I%UjO3W^_BrfggTL`80g66H!!o(NFN>|Mj4vq<)vvxKJ zb7#BsA(LUjREaIaj{BVU>j(^Pgip(oYIdooY{Y%uT%!H0ai$03h>FH~n|U`~>v1L@ zQXW&{XyW>SD?ucq)`KTWo6N8Fw;ZO$VDVkdMO^B?c{8-=&jGphw8TQ5V*!nlVV$@r zGisxWuAwo?a(i#>oKib%2aG<6^43~o(m9j=?nKjqd&7dp84R2p;LfU2mVpgo;;$e-U%^tO97V=;5Nc;b6cP|u#2EU7YjE`NMR`{A z;Rmo-81#buTIqCe8@RIeR>s|d5jcBK|2YR4z>2FqGF1}SsDu3UH@+2T(GJ1qWGS)D7+FvAmGMiQT8O2U@^9Xqt4)C-TE`(X#1n-05S2UGD$eMIi z|1_TeiJ;~c``SVL56$QY@nFgbVcDMMQhA#;!L+vhIcU09J)AMLyxDRJ%a`nX2xg(n znah~02kqxSrHrttoiM@_A_1Ba$OflE9XhhO^3QmKQ?;Ehc`Xsl6aA}U{09>oTR&vFL_)Iz<$A|0YM*0e zMzKtu$VMwv@@L5?2!liL9r-x2-=9_rg&!EkOyJPXYzATGK3KsXFGuYlB~p^d90I8v z)&;ha|V z*J#9d;9^-5hz3hkuG=3x=a3tCCHP?Tre$uQydI$6XQEt6*8<350=oS9imB63f6*6a zI5oW<`hyCw%wMFuXU4E+z-enS2yMTxO8JdM1^OG261IhX>e$;51zusnNV^tet`ao6)J&10#;Uy05nvg#5rN9 ziN+eja7}BT&1)(HhWcupp3o$Z#59NpcsU5J``U`O+@R}ri_iUbm=gZ_u>K$5_TUj{@&hg_#WRQ}8Q|~bO;SivKBuK6_B2dn6=6*E?uyQxu zvhJ{agFdAF6htq2|m8^sdJU{qQxIv@Qm)V@L>1$gJ&gA5`+dqbmV$}{x zF8-s@WrkO8_{J6H&mG?x&*8fdf3j2lZryqKqV`HaIa6y7VR*NyMo*!yKm1;$8m^&n{BKVbqT>TX(eA50)N-I_frG=`oDtQdbB!uijJWQv zpe0HO?G3TbvO4ua-PY$`%xl3@F*_&NHm-s%<5q{PNcL@2pOLu1V0of|NWh0H6|Pqx z9W3|Gv)#`|0&GG9_LPEOuTl6?vJse?r&Sqw8n$*AtYR_Vr)4;lg`jP-yPuhyMK~+BhFAX=FWr z`x|a5z*$Xu=I=+(m}rV4xZ%dhb#O6S_w_TOAai|cL}x_bez*$Za&K9{dFH*++9;tV zhVWDWJ}G?7LHVbk`zj3b()U##6S2ylZ(%Ht$h=nR2X3 z3BO3Q+d!tn78YJofJ<3{IT~$*-)i-EWKmop+72L*J`CBBXD$bA`z-P2tne&OTb)4J z0Uk|%4Xykyuyq=Le+gX-3(D5D5(cCJyhEQRV6yO;zLmWQb}pHKJ6vO%^F5<0;H!~; ztTmd>I!?uEF4>-2vd64rs{fB~b(PH;Ezbt-Gu#|3GJPmf9G}Kw{|iqIaGwSIu^FiM zkoUJeD*gx)>T@C?vxrzd?-?ZT)%mv|+UtdgNPMbAxQkp8-Z0e|&<6|?H-x4lZ0S@c zq*FZxg<>GFitC^MT3;V^U5w!8|9m$7RM`e-1Q->dHnYP$oeMDo=8nyoB~YhNEU0}5 zBJ#iS7k^Ufnaieu)7D(k0g>@{kIj~zK84n`vGp-awCo9H1)-8373`ZdT1I?mHOhns z=x1wV!+idK@d)ERKX56AF}9HP^lxFmwt_Qnn_2%+CFzDGF3uo_A`Y|WSKltgb%k?7 zfve?{miSuWVB}xT09x2=^?A$x!YO#d-5nPutei3XPqYJm7sus(KYfLSO1!FdOcfWk z2JCS8LvV45yC!CzBq`GSIoYb@1--*TQygmxp*|jvGF`ih_v~H9r(}c|_>_>3%!>FesySFE6uLV7)T4$HKR0nZ@b` z%CK9?(Mxp>X_qytoe~wO#ir&uTNk>&STI^C^J$F<*ocoA5f6&L9f9*`ZJtJqkR3_? zps_=I$spebokYtuJW!6v6^l|Lo%ctCJ>11$%Gbbo#d@Y9{H1q)+Ou*{?0yc)lVuDs z@k8?DW*_8}&LjZls73GZhq#nUd!uwTrnP}qoK~WeYB9_ zh3CB6>jx>v-kF^yB(b?nK10fXthY`6)63qHG_BojyY!mk4P4Yl43KluDJL;4hB@dK*UTWmtBE0VUFr zc04-d03j%a3<4QaXaGk*d;gweX(c{uSDhKCFwrKE}!tG9vatdrK4Y7X%5J5mQ3UCamg z%*RG$cf8zdny)=6esjtYG=1zldKxEl62iY*Lxg=AL%G%pfkA7~PsQ{vILUAUYziSG za3^r@6TmA_~NR{z`CG>dlOsB@<-0NO@`O3 zX^zj47Yt}Uu+&}G#SnL|Ieok8$|AI#P7;6L2{!QK9X=g$nF|yD9=+Qdlzez`3ks&C`CAKKz2 zYtFa*ITaaLqaDolLdqOB>O!ogR?2V0lN6Si_$ijLx<&QHz@nAaXB0ueR_i=nBN99u zIjG+N86AID3b14K+E>|6>6G`&VFbsDNaYb-RP5OeYA4>-M8HLAeV;YwhcOud;FMeJ zIws;f;(lvjv{|ZeM6Uk>mQkKSj`e zkt~S=%YDm<24o44ZR52jh`obfC zH}~aY2+&p;Q>7r#CSqeYX1{(z2a)@US`r;Rkn3BSc&X1;#tZoo`?^6txERXeuy118 zyX2RS8;01NrxzhUl*8xa+PntLY~_e+K}x@ArD*-B^y&k{3Ju&0`=k|G_fC5i(Etin z8q*@=y~wb86IkTKZ^vJvyxadHx9$po=fwJXJWURBU4AuVd`cQ#qx!dy&Slbj0o{CG zh5~!Y6zn9k1FT2TPqbkq1;xmk!Dz#0+8d~jkfX?Fo;~W_|C}%z?_6<3V&u>yQ$GUX zV_fu-QCmRsg3IP)_nikp==VxHeW1x-uH%3C|D0=Z%>;kS;4TWIKmL*$jukQR@?o%l z#ZabsbJFU!6cHUnBloO_U=Ea8aBqrOT1>J)KCKyj_1OCWCub{DX1I-tX~@@GL;T#v zap&aAmBIu@d_k8`)4iccxZfZZu9e+h*dQ~P{EM{bW>-23PHJUj@KLSIAFK!Z4dC74 zLMC9@l6UF&AC)pfhXlnHA@ZT&8^A2ffj}@Z;C2@kR>7g%S@4ZxNnt2vyYm`z>Kz>S zDq#q%he@ykS6s>@6~X0XZdM)wg(mhXAZ!>5Y*-Po7I)JC*BWH(YF;;^5=QqCd&Woj zryFn;xK!0&0zU*MiFi645IGzU%X9=a8z3b-C80#m8Bt#DL$Py|UnFZ{0se3s8-0Uf z`^g+QTN+b}4?U#}Rq~6$e8OX_Z-Xfg{g0sJ@y<@%q5%e|-7WZZ2&spAOH=-45WwHB zU|OeLlrjR6&A-KWpK5YKL*Y5AZ4!?s@|~4H3vj&>{+qMO`LO89!N(=c548DR1Ao#F zZN9Vi0;~agxO=H=#HO^SBmc7(dl;!G%M4pcjPgo_Cvxs2I^uh=bQo=$R&1hVeq0<}+obMJI~Zd}S)`Cg96u$ZPCFFypP4j0fu&!zqoJ z0~1)zAT*8|-wW6v>g)#)>u!+JX|eu+V*cZWNw9JZnDcRr$zLyY#l_n#_9lWOFEg=W zIVzZeeg+fFgks5Sq-*7xkF3gf>Rv_7YjIKdP)zeeAa}|8SMks8&q5AMuQJ`HxOVKy z^$90M-1DFj5c?#Z-#fj#TOjAAC{yver# z3HZhVlhWZ}+Q{l!$$jsIGueiwu;ZCjnRTRg zgw#asR;h}V6KCUHPh6*LwY9lbyVZO}K_|X4il+6VY2!5}ex({lXSS3E3b&|}bBfks z$*18$NfIGVjiT3bb%th4zMUm<;WKL!e)2)Kj)ZCUN#$KjqT<7-1{LDYSz=z-+CHml zb!f)A55~z{=R$;E_16q>=iS<~%@05Hl5*aBOh8C|+q*!Az5~L;_xA=DS)^K)CkeI7 zF5H;;xRM?PByp!Uz;*^A+aAK}cq)t|6co7pjSzilU!{F%IhL*Gqb{lL#q8!WdwoD0 z%b301U>5x0$f`4oc*e|W;Y0W{kI(wInXC6Zg52)dDt*XBpo=9-&60x(lYXHF_09(1hV<5-n=r-A0D+`Mw1hc@nqI#m_Uj zm?ci^9U%v59J8K$Z0c)gx`ZZLWJfvfLM)#bw6-~W07I1(0WWkvf;oahMCvLx|;J0& zI8y6-1kJPcn#7Bih8b{oUKLmVKD#<4GtvM0k_#%}3f?C(0b;RIV~C_u-m_`l)$9x7 z%6dei&grkP#U=8BYevx0F>|f<(ib3WHe1xBXd^FC4$~D7!y4G;G3_AyrJg7nCNvZ2_$aYeIcUC4-~u(K^|90Z4CwfpQ`iXKsh zPD;6eA~m>m$aRn^;y=h&;BJdk7UY{32EhIhb3!yYD32N6GYUK7OMz_xy8oHw>S$Wg zyV$+{Xnjo2{6{ZFoWzOpbUf_7@E}8?gO$|+z=p+V066N~ILyeO<~JMViPPY?F|V9d zu!hiUstP750_uoVu+ab6JMCxJ2XU3f6#GQKXN5;L5BW2}oGKys^REnCGPsxgVnC4x z$_k1j%4g=3(U@|{$3qg#DMTsP-`xVOaHB_*{EIW;4$8r7a06uze^BXG%1Ajtr*F{X zTmU>}pQB+4*4R8ii!iFiDAy9Yc;TiQ0Ki~+aZC*I2*1CvxU9ErCZ_Fe_IkMg<(q|@ zSP+zQ_P|lWnsW{DZzNFx%5P?kp=Fwj`I4y*|}3=+wM9IEJ1dt(%|aT zwiZ!e<{ULJE%%VlHo(Ao1eplNgxwLz9(Wt5UFdx+wf#jq73;JmH`SUuiW3f3UVIPkPhLC5m1TiT1^Ho-KABf*yn=x%L9D> zLJ9gsl3n|JQ-Fmu=$T2JX#6tfmrYUDG(2Y;P_P5Yv(#v~_0(y{ybs4$C)T?Z{P$i! zA@pIH42l`$W@3u#8lcm8CMFM@eksJVb= zEcf!=kSfiV?QLfbzn#?$7hy{}YU`T4nEYPqa4P3sUGi3o(&!S*_G_qi)M>W!VSfWT zb#nu>ccwiWD08Tg9GoLnxJ%W&c*}Kn373PK;eo8!H8~f8W|$?d+}Rjo}&GY*ctvwJ9YAS%*;^HNv!nYM7m=ceu%wJm(TD$)X387sFgsWg#tpfwY})$+*C<~c zq*d}(t!AxK;V`rI$tN@UOBM{0!6R{^sTgpG_l$S-FtotYhQxhN9a;z47Ro_jp3E_n z>+8^ZjGymn&6q%2t6m;uKPfe~D7zfP8ZPlXamN4k5_5I%XQyBua|uk6_LWeF>N-s( z=T9k;d_3Klj^gR)AhBxa+j@{;jGnj1@X{rVAWw=tqd%|9P^0&B>Y5k5!?Px9S9E!u zxWLf=a%g8MBP}3y`0XXz$zu{14e}LSNUS~C`h6ZRp0hYxqw|6p8kG!c4R8Y@n6=y@ z(=g+^5n1#iw^zsc^R5|eE)({6uVXe3TgwA0lA#7^wdpCp-3R*6-`j_Bigt)4kU0{o zAdUi2g%FF0;eLKJG0u_LVlwyju?6U{#;pG^wc^luG3dr6|MHtMa~Wlq4-it9GJ7 z;gU)sDn&&`t9GKI$Y6+UBa$Mrn~BigHAv!lkQmUusHAPRR0|EV9H)4UOtm;e9A~(%&w$*&Ywjt|?Gl6Q znP4V+1n4#3r3vwLD-|j{!Z}GLz~J5* zu;hg!Plb#ri2URwRBmwOj3qcGEX7&>?UjKHb=ZRIrL>y5^5JU6KK7_IHVk0}js zF&i6AikfVa?|9fH^4ltf7x{iBLk1pBaRPI82m|C9n)g8Zom0E!r91JDK2Bji1m0;! zV8QN?Fb!6Oc!FJB*r^o)P^BaH%uUX_`@QlmzIaJ5HiK5YwNk}~?8V=IqCqZxw0Roa zPJ0T0@7`h+FwNv~`A62)h06A;K#BB)n3EvbfX@VH@OsiT$%Ll!(bY}aKw>awAc+G; z;Go>5LHybY){8rc*g!+V1}R4UHT-c$7L_}n81dEAeobtY zk9Z)UL~+lw{j-g8LpfrI(n3IhOn0}tt+I~QtLNqH3DUvAM7pt95(E=~-(LR}>B>i5 zjhtsPsycV`C{~CsyS?WYQBJTr|CW5q%}zPu>ABAP@Z+|}thawqSsHvoKxL;YL)@*H z{Stuy2@N96i|YRn&$hp{WN!sTz4aEbf%jAxAa-+7^IvYWdxPA()6L4cbN8%qDu5JL zLf5ZWI1a{m9uN@E)h7Y(QJ~uD2@2V!$^$j46|@LVdtP`lr+e_I06-Y(J_I)2+I(DIFgABE zth4OidDD<-w%Ww)xO6^=6ub{cGKqu&i|vlnr%Wu(onXH-f4#B6oX0D)J4%;->#9_E z4o*jZV4jqA?8q_LyF#!fS0hleQrVFo2dGW)1#U*@-+}8; zEKC&z$^gsaS@2xessu6!Em96GVj1IaH@^>yr9 zg^QhhIAr|itAr`oC=bpJErgHiX(4ZRGrm$F-aE4?4G`K70dFEmb`Sp1z(_98pd(P#mntjMkxc?npLi#lk zd!QA4tkpZ2pAe+8LI-G|d6WuFgG^%Q5_oyj z;NX@-VjrH^M--l`>AntdKO zjrNZ8jUrUrQ0do_x)Ct)k+cJx_oI}HGl;N*thqL{r2GwCT{FrzmJL1zI@VNU8`8jc z^48=4Rt8MdKoP9DZHX(ln@gO$$cr;m`8x4U>&zGWsveoPlmY`J8>7CTL$!c$3kj=*~XL4P(Y7%r9$7@ zIrL&1kSBP|mfn}SBYpgohhrx0ZnhKje@M9PC(?>78pBfven^kW%xJp&^6 zh*wtkut9nPO?8{BNnqszrWTgB zbz@U`SMslP3mqYP7Yabav)ME82rQv~j#i&FuyM-+1^&_WokIk8=wT(&AV637?E2Gw zx`vg>m%uCM=hI?{_vh}yaj3CA+$PT>ctn~hBw5!e-h0Z zpsw8fwO5dqRf{9Qp$O|h1g@~54``68RJ_o5ak;e~x`xP0D1Qa^DsO>-X#or*I|E#V zg^_VN6UheeCu)*}$BA8$(~ebM$&H$y7H8BFaMMIlDbc!52NZwnwn$J)IC*yn-hgO1 zHK0D_h!YT%kg#KJxe0U`^(u*iiDn2}aUDpSKAG4JgFli2swUC^o>#Rn33d?AhRvz0 zE>ijdBP?qLu!P|4+7@06n)pO*eb-dr>o2Lo19Ojx)K4z~Oks*D5$ObC;c)5DJ_&1o zXDYV(9hl1Md@hJL>MOwmfw0Dv0VfRfM43Y>Ci1;ursTbC-g&-T1x2U;Aiw3KK^CLV zfOX1qlU4}>(yxz#xvgk?|7A>BKrF#r_*Ze1?hyI)7MTU=2$%RBHP}G^Ffo&izuaJH zhy{!3;n341`1ojL+JA8O+yI_@czF!Cg~(StTL-BI!rZgL6H!heSqiN^R;~i03&J(9 zmdwW1gOk|_LSrWoJ_Zq(>@7AMSrm!w_u>xZ217FiY8H98tTiY4R^wqxO1{s4_}RPf z$?`Z#2`J@@S;E4ySjg|SNBCjZ1gFGiGCZjzgsdg_0iCweu2)nVDsYW}`rZ&GOw}*- zV0bY_E7%Xa{s{uj!aR z#L{DD`d;w!(D6P<_m@h%P>sYP3r8BGOFKGY7VbhP6Sd{Z$04Qx*n$|c@Y*@eOkfc) z>9h!Ee84&-?M8~Tggc?0c9;R$h|)?WWho$D8`T}w2{54OM4hMrbW`nSca-N(g?J4& zuzef0KYjO15B}(9J{X zi|V|AOVF+KlUkY=+pkp2ZoQJSw0PX2qkFG!i=DfC5AOQz(y4JfpN!cI_qUU2Far?}hBFLx@la6ckIAU-1P!uC_bdU^Pg04k@#hgP z{rTm8`^(N{jhd4nT$(ZinP|=I0}ul-5Yb^_#r17l+qwp+Yd_h4tBu}rXiS|6=Ue^v z^Q1MqZhh-r{W&G#_KwK6j<-(UHyCn^Hle;=ljkgauxByhYIWgTxdV6X_3FpD5G6TT)a`dsKaG^J7A%GWs+3B7Alf@y!7KHnV)T;@@0)0&$ z{5*v6!yn?e;ucDB>|DdnEKoG%m_G+T!)@EujV=36>^!KmFMX^@V&785Cx-J|PBjWi zs7u%g3pH~ujq}#6IP*iXI9u z9+@YKael^w%Gpi>bWX_5SPs+$ANn~^v5RutEN?4xg0%s-4j2jU8U6szgcAPxI0(8W z5FLtOr9HVmdUb)AXWJNUwnH_zj^7QB~VTt*S zWG9$;7a?gJyjWNagXZp*h$*~bj=OKC8i$^41AWNK2ZI2AD!Cp}f$3X4=%Ds6mzyNK zy<3Zm2$qRLUtTJhRKa3aK)PMpNA$rzkb4`VV4Wq%$NfcdCwHz5Q-=^~)PqgWENNb# zu%u|dOWDOI3vY=4TUln;d;uE%u_hMOVjJ$d zaZN#J>Y$h|(4{i^qMALj5dr!0ntu9x5Ld?;s5VK1FiJ=*=ngTwI?M53fXV^Nw}x+L zRsD?yu)qmJTN!N?;Z^LgltSOgy&e8#m&^n+zl0oK$;ntnglAV8`8kCHW{6vAn&{q5%E!N)nyLybq=t+Lx0sKhZZF$HSh+)iJ39yF z626~aFrS9ig4yNK4q~$k1yhEuFKYpCQT^o-4hE)q+oz1F$q)jq%H(Y5*hGi9-K*gK zAS6Il(3=SXj(Erdq@h~*^f8G_{|ZjvITk$L2BL8n6^((G8UOeYg_Wiw->VDjX*sZ7 zR|3-F#JL-?_BZ7m{@#fF2={WOX~;rHjRP)ZSn9=@r;7PXsL8;e>w&eyO8|}S0Aw4y zvvvmbXosuGIK9QfBVdb!{wIjv1qeK{xjnUsMOr-YUq1Fu2EKTsfvs?w|4$z#$`76+ z0*7m#b=bq0c{h^1@l9MRJpUcx-xQqi;eT`ttVAd17G-`3yqTvSt_6d833eRTcO~=% zpke!qVB-d6qljR?83TX-bufdL|9u%CWVVQ2?=18V)@%jW>Cd(2Y>EBIIm#z*j-C(@ z_5WxJ`4mRz&hB>shbv`i2}oMS+QknHZXo=(H#UJ;yW7}0k`qgY=+!%5y45}2(XAB0=1sdNa)&0G&mEby4u?~1N-7{@TM>AL=zn{oDi zPp|9WirvxcCe**$hZ}G_Z@YWUvBi~3@cQ1j_AMaPXO6%5dfj!0sOtypM~`zltvt6} z+yB#j+L+dq@YczMde57+)34l!(09i#nR-vd_$}cpvg-vcZizu?QuTHV?v+rp%gJj; zj{1lcZJg-X3O+ai91?GkU3ktr6!`9~J}C<3yH1K68t-All?q&Qp=81o1m)0=AQ`L+ zlRIzsro&{{D-gnU<_*|PL;m?mP3s=?*YvHne^G4c36`Vbm|T-$#qAx!m*C2Q#)^W9 zRHkZ_+INQy<|w?nX1~vI(buIr9kl&4HYUF{k{5Ci7d~mrP2F>(CzPk274pVCca<~V zsd#8-THVf1_M0(F;}nG0o==|C839d#Duw2kECATwJ=PbZGJE2AiO9bP#3Ojla*q{5 zz)jY|PP{O)R|CYiee!6SLtuR%`U!zthRNf0${#KcE;x5_xu^f%6)HjQe@3%!pKA|$ z?IYiE>Fz4cwYV=)_X4|Ss6~F;8S(9GO@7i!q|Mc#&S?I`d?8)et9)BO?-8?hoT5$Z zylEEh`H#}qk_s<;$OrU_HSw2HeT4gN66VgFY6S;G84VgB5H#4H;MECe_ZCzU1-lfv zR`+JXY5fub15Cpj=o}e3hl7foM|x{z5D+%Mcam=G>E1G+UFeSJ3pr#x2o?k3P$HNul|E7%7gzAmFn=xute44_Ze(Bx^yw3a4Fn9%4 zK)wPi*yDxHI!Z?}8WZ5n;nDS{xoGDLTR|X>-^>ba05Z)nU`{=`aNLdGo!IaWCaD1l8<|=`@p~%+@b#`0HDy_+&b8(WA4S0}t@JCC4R^5(ld@Z{ z>RsS1lh2LTEGAW3Q~q4%P@xci(Pxp-b&}`DBdcZ?n}i09(p__Zdl|m&8CTU=_n4>PZw{aeN`)duML#O+sbVMA!^1;;HhZyD{yy$E{oF=7%;e zq&CUpytc2-8fk0Zg)H6gXM8O?l(JeG*4GYfj0aDyO-SVs`LWfod*D0~1{ECn5vZ&X z1q&IDwlWCx*!&v&fm{Xe+ZpE(zGbLg%w9XkX8Mx(2kVJ?V^7gF%$9tanUKIfwCsK3 zAO3>5yFT6>3p>2>pVui8kCg<6SshcL_PP7`AD}gz;lxfJGIinuzY*^ET_9h*h1^A~ z!})!g5Nlo&8a{U1!<7jve5GLGIWoO(0c}}t{rjiSqpF6GBA3&ij$?rHz71YENO^$_ zddK<-@iK1C+fzch>jO(8Q=r=6IX#atrHCb z->ifYC@WzqSf0(Zu+cjj8G_R^yZ5V@f3{G&g-JAUgs#t3PxqmZ9(?>XF3t677k|@; zuaEs#1cjs#`@4j9bd6r`^pMthx4Zt~twT8HY6#gDP?cG=QbEFArb$Y2Z8 z%2=K`b0}>D*GR1enL%ONYu(!%t>*>UPrM5ZL+S+TvC?c8z2=X|Rw=!F>(V67Myg@R zQd8E5EOawWcA&@~cLvuH5$rALLbzrkBQuNAA{LSHnmZ%FZAF)Hz<6^|@e{&+a*4=* ztm`FU?UgM-kwMh`wMc*7LqnRVzG7wI0faA)(dhv8xmjqCP=u5NlUq{7^?}5Bt|ST5 zK7*Jm!x&b2Z>G$>rjcS7ge<(qF@#O(xo2{T5O*6i1uWiu9*jnxSj*r!Sm5+$TL(Zj zLx>Kd5wA;^9}=r{cmz9x=p||tRvlkPW5#o_muAG0G3B==+XbBSTF|y;PJH8Xc$!N4 zIJ!5?oB!Z;K_{+ZohUvBLI_}@Z10d^{{aI;aO(0y#S2-UOEqV>w%GR^&%1x_^to1=bxNMUoceNWWNQDhz1eSeTFJS{Si(NDyl2?W5J zDcK1klmm)3|Ml(}er_vlO_(@$d@%b?$Z4YRMl6IvBtU;Db>dI)f3Sn{3S9>MetzwF ze&<1Wk<1`T7$vGZ5frKd>c*k*)rXa$!LG32Po21(;Pqa^`POK)WbWV?Uq5}^Pzi$L zPSmBVT(F1{#3j*6fB4zs0%JRYGm3bubJHx2>@s=qR}=YO<%sY`=crHp_1tuyF;D-& ze_FrXtD{oy!Ws|{hmK}6`XHJg%0tkh?Q8G=GBR zsddIC3#Z9#t0HXlh4Ad&H#6uEK#dTM*YN{#fP#4J`SLV0@Gr0&1##;+LiJH`XAqqQ zts{cnI%!O9=Api(CxAS2n4*Ix{n2eHQE}r-$J0}RTlSmhKxUUUaQI&{oCK*;VfUOJ zWgE8pnjQ;U1i{$dz*4h;%PFF^O~IAHNXP>IoCJ(+d^0R_^GuCO=8nVG4IGUCvj{^% zc=06bi-8-6{ZpXz1voh~qCgjQYG4I%GT;KFaOqGk-oF!dmiTyK;p)?7sv?E~kMG>= z6F=^2A#(-<-wIJ$Y()Hpx`h5p#AhET_N|f)Ov0vvK!h9OhbP9G;QJBYW@W}7=Zi7vOMe8egXRaSAgKGi_VivS{tDpVcRHVLfIO~5J)Smn?E@ez!|kOR z^Smg|4E%fo5KKK)(*e^4AGSl>4dDTQL;a$PAe0F#gpLs-N$imbHx+a3#(Wjkwyqlgy##0FwusN&nJLk+MpCc;jpVoyf(7%t|*|f z6BbiI6$wDTJHB?`g0UfO9MEzP;CVE}+8hO@2yfglNC#jCFl^(8Uf4U(Rm2a=oiHC8 zrsyJ~8R38dn3VwL-28Pt!q;+N2*}zh2}6A8Tm1~?(yQkeqLVyLEaMI*v z!p%FtOB|em7s`zz3eG^Z0)u~w8BzlqFJy_>Koz)0Sv1$?@XGozHlh#kaD<`2v(pIe zXl9-6f8il{(aK`v}ca#9HzDXBGOH~v9 z@B8y?Cc;ZI6Ykf5RkO2axsu{5f$E&zb?~ zM)xE6@$*>+VS|XkdYw2@1^VI?^o6WM!Ryc;;6hJEV=;K0b-;c`l8flPOK<A z?OeLGs=6jUgB{fuw3wYYg}|m5blw*_lVX^4EQdp~P9j@ZIVE;Xz0Enr4$ON1O_9;2 z=H*Sy(?~l-(JjKqq(#Qib=xVUgg&I6tD>7C5^_4HRx-6~gNlL(bWz`eUHNIJ(vFFc z0WJ0WkaRqbPz>0l7{2)xb35xQ$Ap}A5sVd%#VoJ>yqnUVp)HZ1rP!%TLypO5C4pUh z|JINhG`9-4sdFmLzrsly+On?08(_^jp{Uf&r|7OzeU5~WFuSEwr zsk@FI&D#=?%m8v%=$_<+3f6 zYuZ^u*J`sP^3b-5l7S^-qq=|>h2?ECS^I?D;9SZs!=;znXnHhv^r;Xy6N2eWo>2=|d$77}eY5dCCV~jAqZV3MRcj~~V zCkFGax)pA<-tGR9<`3o6`I1@%~=w~MdZNDEUn(PHAOP#6wL zi;{$usZ0kP(vK}B)JAHZjV5?f1I~^k_ppyrC@ukqn88ZC7_(9P>gxY-Yut1p&e5=b zGpn9q)!g~Ax_Gc*@!U?qU?0t|Nie5rMHbW3$%^`9@WAnL2NqjxR@6Cgm7d3`J>y;; zGe6C^&Uy1^tq>!bMn%chH-;fIS{4Lg_YTas;e^8feM_N=QG`|PivRXeO&FvH5Bv{_q(*MqcEV!m*8pkhrIJ1}=EWqA){(MGLCF&v07JQYMX- zdHve0y7qMx{^!=Px>WUT@!}SxO*3iZhO1D;X)^M<4=*8PWdwWY@lp@h1!^Xj%JggP z7-```jmN|NP24Tbr^+?g(}PndFE4aI>DN1)Edx=#$yeDvV6drK{Eb_vs3kgFbv`mH ztfvi@onCQkHf>&of#L-vuh4R{4Dqq+hE02n_2cS(!VrWY!C52NT>jDwn;+k#-m_%8bg7WFNb;c6DXDMrUs#7AC)go1$2 z=PlCw_*QRtI3nwvU|BeW&|3!iltsgLNsEaSs@@TrGWCc_6A1&vpFhGEU)8KSySwpb zk4KzMr+UP`93}zAd#>8@HaGq<+KM5&f0%H$?=H6}x47KAr#ulZ)eX^?vb9&3{2QOY z3({cx-1M*K+;_vGrDXcaf=Y-@bt@#bfXX;B|A+n{yrzQ{Uph-p@=rNGIAuzO5#rAu ztDV~|sP;!XlX5zvEvS0a1&<>3dvnF>%^P{Ojlo-PgOtM z9awyS`7^58oG*${W!Y4(E;yDwxa9%bu{vbYu*7-RGc4M*kI$nZ6^iyrD=Eq?txAciSwZKu;;Hbeol zx!(T|E|15UyRS1x@T})@tUZqr|E1-alzr`E%(t3n<(T&{HM+U}pBz$Fs}6oTzN_JK zaUQSBj>Ld_(IfkzI3YU4zU2~sZT#09Zl;t(Rl!{#299t1%d9s;V(q%q0(w?OS z*e!OEoZVt3tX$!%KU8_eAb6CJ4Wl4d`A3MGhvc?&t66+u1xav8Xjfk@O^Xu3HN&zc zz0@J88R$gU0S%x#?E%{n08v_h5i7JPK>})D#W{Vu)LPMLmnmRNzn!K>3+j(ziK^?T zyksCcF6EjE@1lp9q5LEms^L?r8awgFbL`qWDMZXh2nhtYYYa8+OD_SrYFO)yYK^`1D;POS!ZqvAZK^ZTt^BOk`9hueuLIB$Ypl~612K}NarU&GbX2w&5FKtQji z+YjehQ9JKnsRh#~{(?dNBInyQn&WL;)UNdyJ@yWFL;s5oZtQj36X0`2E7J(P4bRSo zmv)q1I;|QN5BKcw@mh|Gt!fsPs*+BL>du76W|BQeyHdPMfj}C#DGPUT=4#_qr`{MsOTenuiZdRzH?4=_D)ej z+9)K-F15>^77p%3J5bRpMG1|O+b~S^Io)&_>pXb&u0yuGM2#O)J@U7l(%gl!TH%di zD7Jp{cc<3#uld_T;R=83ELm$p-uT1ms5jxzXmH~&Y{a)z5cXd`-RLugzx#0C#$PXx zF-(WE+aJX(H?wJ1ld6Xw6VY+k&5B14K&EZ)JMoR9LCGvsoPm8iW8ZLU4nZ>X({spj zSgVeQz!T$laHI|WL_|rU?p2;WfC-h1lPa5|0VDO23+S)VS1_hZv#;>!r~qDM|F~pD7S`!phc4-%=%GpRCIg*zZ^xm=ur28X~N%sPW=7u zpwY3BH=3pqcbS1E@5Qh(y5Ng`Y$)L7$3tn(Hp`w{M%rOmez3Ye)_|(M?6A%rc50;F zO$?{&YEHNKG{1KxR`^N@3DoYhU^*LN;1RyNC^u&I>~J-!k6PgxI=v~{U*lD3X@o0O z&EH|8n~#%tA?xm)nw%}~$45PhWUE?KNZiE8RGTPCNYE?p)GTR!z9!824}Tu|7OGUl z^qZddc|A>Kx|rKQ8w|lhPw;1WogRqYP{TPLXLY-#mqX3nX+6vetgcznOmZ)<68a~9 zdJ7Jx#_Eif)O&fl;&w4hMN4ozUQB)|%Ebv2saEI@8ohH7%;zEJMP5b#XQ0)sb%^XlAcjfWzJ2070l)GH~}* zgy3HX*yn{uh~2CE3B!KY5AN7#JIU!DW(r!_e1u?93CviOaasnM5766j8^^;YkMUtn`6I(Ct!hV7 zclutRq~_~3Q^Y_vZ4&)3?51FR)7^;{XhR8}R)(UaoM4q;9*mDspAqrrzEO4q?|ek` ze2>#4Fw6Jjj`eH|L`=KC>HVcTq7n=|pBZkJQcmcP1p|-5;e}G_51f)6kd=-Ssp@Tg zuyqTX;(k;U6K0=?=k3pDNysSr6;d~%C7Q5t|M-9q5ZKhtkgPWRy z(-|^D9md`$=9`Y^CHjZhKTFjeirAyvE_-R-;R7Xqdl@*6fa1z4?}vRjAR)Qln!Vs!{?o9sS|Wt&UaMKWiU@t4pTt~YMUPZ53IoLEaVzb4UE#YX`i-=IjH&D3 zr{*GeFLz}A!X?^DR&N$sofT^?$2S{>>WutoW;e>Mcnz!GWhRT5k4_670=`9i@M>@R ztz17Eq5t?2i^37Oc9HjjiF94AsEd8ElR@7;S)uxUQnUqiLL!ZzY*3&mk7_}exxr68 z<~5>j=c9&=aw6gk)x8}2xOIio=0UHKjw1VdHu%1pj)R&-!6FN$^P+n{*|>0Tj7#2i zinv4cZPz{i^SF!j&>f#uc#1Yl4XrPIdJgcShfU3e_Lw8(Fo4qlCXQUr{P@>%tI#br z^2=w}DA*cDUj#6rtxfwxsGUn;Q`L-I)Xq(w=T^eSP@}!5uzI13sJi^HwHJTnMp!>0 z%IF@6VRF2W2%qj}U2ZUQ59Qfz-l^;DnD_NP_1Ximg>W_58lzLQSWU2t;1Xj~;4m&P zkRP$|=Gz%1@10$8nKfS)pfWQ22W$g(j@w>s{KxoKQxx1%wKe$MK5-A_`@?j9gb4we zKtB(?F=}^-QFU$4a-VjTS_iT3*|ll{Z}Fw;;c9b*v#Xa=C#=`7D0wxTL5A>p)PdTW z2Z8r59$%j%HV~*DK#wS4EZk`}o90)dzup>^2G0df(>f)=fl(9O#UO|PxoRl-6+XQO z46R~x&J@!ys@@OK-iQyI+*UQqBPhqiq3Zd-qTFe2C%+$NjG)Aw@$zgUQ6Q~VIPfj7 z4KJ$o-@Zo;VO(zz%$}9{MluGxfHN)Dw{t1{I3l!Gi4xN9B3p)ts^^+Eb)tbn z^iR%3t>3pp>*2>RkLWFUktJ}eM*-NiE)+$D%H%B~oLthvPEq`vxT&{rbnl2F3|A^f zC(oY6E}4C?w;K}7DeRprx2^rqw%*kIYXwaLD5?#AJgS3m zVya51eN1)1aC-ec41iLRnsM!S0Nkq|ynEQ@yZi2TF&!+Zd()}<-xe-e`)g;bOjxa! z)l{|VzQLo+rAYrZv5@@>7%C?AQfRp;;m}_t!veI|S_|DEtRU)04@Fvr$H+m`-H&8) zB*hqc^V`jif+>7>hqVx#krn@PtWkCvzC}_`?l84X zC44=*%t?w|+uEO*U3y89sGNOGtUsh`LpD)xohniWKYT01;IWGPs8zeC14De4@Ii@P zwv+?jNt&RI#YRPvGSUJ+B-BTfB#zi`U=HFfBmC?Y#n`mk^Td7qgL~@)fmxa)L4w`O zVS8=wBC4=*!|)jFH%*A3wR_CilfjTe{!_w^)k*05l|3%0OooBH4LI`aCg&wGDEFlz zc73*4?FxJvR!zA8pz9)CSKdpJfcaDPvZppVqvs`sE*~-JcW5irCT)@bfg%`Vq_ih7 zBM9wPguY2vx)?8D2q1;^HWsk;m*^K*>;<|IJlV2;TAWRo!`c6n6tSvxK-Z!=BNVeU zKYzLWd0b-6MK&G%o*$dXEFaITGGpfF-J49zr!3=-8a`oN*-yV%V-(R#xmB*MRT0Ww&(a^FbFq_k><1JhN3}KWtZj728ZIt^cD(*ErGO?Riff%26>FYP6YOv(?^#J#98jbG`q$~)JEM(8Ucgti=M z{lRFKX)J>}sd^$n;Jb?8Crv}5;#t^l6dnPccc@$t@T*Fb<}{?~{|Y7dWCoxEJ&H0C z|I8Jq(Hv8)J+QI1`AxB+El`|LAlt6e*s3W}!+a*`;gu(ra*BqbDbg{wOpPLJ+P%mf zj+#pJZn=?(t&`~bhzac{Wg8m1un2&eEe3K2vryrOD$B;6zmi)HOF3?Mb2|yd*SUT} zvLePScA=eF@Y{%oY_Z>^x%?5Czjp8>?=z6Z24L9Gbwx$S3MskXl`t8w-OVTM(L0BY z3FO502E07*`q7-lQ(e85bw_@?DY1|pDRFln3mR9PxuFrjP`^`d{ySiX^SXQg2>F$c z>q9Rvi}A`ZGVisBUuPtv`RBZI!=$k63n~puv_R@bVsRqP9A(lwJMFB>$s)O*+deGl zmi~D~d;C32o)647ZGQeVFl*UQQs}ZEM5e={I**M^-RVLz&p+6g?x3082r`BE~|$ZN>s*a!NM&wSWEEF%=_|9sH!B z^0p0F(FzIm0C8d)S*EXf{8x|k51krpE~(1qQQ zf=?34IVY`W)2RgpKPkAIF(yY_jw!I@lx$-_Ob+cCrkGW^$oFv_u#?G@0Q#J?4?-Cr zr-AfcH)Z{WIS`b?IAj$}aj-tm$a;RyXH3gl>v?HN@HZmLB6nF!s6mfqcna%={7@y0 z&&F2+6iue?OQkbrfXTU!MH1{*&G`v@^t46dqQT74Uve_C?#eboFDdPDA_xlTdX>%s z-6V3s_1QGy=`gg|>WT|@mgxUPex_j3nAM6}u=x!WdY9}u(OI_1OIT?;3e`tOR3%NG zky^Vsms(_5gv5}J9eX1POhK+}oBgE{VdM(OEk|}Gq3m1ch>TLopM>c%SI#98ENbJc zi3T9I2f%m`Zxrp*g=+qMfMuG#)O`IbjaCjjh7)$JlWEfq)_r1pT5M5sSe1H03b$45 zrW^XRX@t%toLTcO6`~9^;`rZZZq{N1)|BI)%Vpm06|?g?h=S=3cFfYFemqce1UL+3 zvQOO6!>wY&TsUP*n8vEs9ikyW7+EWfi59KyjPb4cCb6y19r^B-=qs< z3CqN6ckAgeYL`3g)&d?@kO-KTTGkNA{~6|-+SpspTfg`&kkL1mgzooDT7rv&g6Qw1 zYO5M4E!=@}l8^ALz=iZf-%$V>F;>L5NooB-S&&jiOI~;bFY@6P7p$QMaa4iTf1LoZ z^%pE?y{6}}K#icTBh%Adb+4p;>ZVv|$kI4;F8Ro@guR&*RYT&P1A!1$plFsK(t?m%;$a8X(zC#C=^VZ6&;aW<#8tEpIBSWjR@(&L|UDI z{%|gv6Jn)D1Be7z(9>{KSYrL2aq)! zOcJXPyej>pJyk8JR#x8BjAyKfC2&*R_E|p_U{y&VV5T!nBLNs2Xu*p~DDr zY~Idzc8$(AiB|(v)GTlOSlgInG9r;zqwO?eh^cF3PV)>M{xm z<ZflSnF8PGGr*o2$U3--c@e8M9L8JQp07m z7!Eh<2fV2mot5@Tlu+I3_G`a*t(l)pD00rj9n!$H-c6So##wGu_?3g(&m5IP-t`P! zHY5F8z{fNSCK9L&SaiFyf6S3yl14BNfkVG?x= z4C(6l6MN8u>t)pPkE|lqjy|fi;Gw$jQ)SVi_Gi1yX`#0qQA&d0gpZU}%65TQKP(Yz zv=n6Srb1fdNcc}Vzns4JL=|BDV5o?-PR_toi~)vNofJ`~y(0ft_P%H#<(ZDtvT~}l zD)<*>0AC0=!K(WwZlCERDQRbo7BE8^LPZ4bjjbA%0+=|#{uJ4U!@5R#*^4G6%)KjH zr6zraRlWOh8SIEKRhBaCVSE znWrcd9(md+Wlw2>dsYl4{J1dwU3)(9!|aBn=ct4rPY zhMTQ_^bDcq>nJf_jgjGP3$YIX&()kV8U{g}QVNnb)E}Dg?@&FH78_2SAk40^kX3{z zt58*8GLT7Yk{0W}%95p+0Wt*|Wh5SW6?QuyKOVchbkQR%cV=p3$*2V)x8mHwd zcASdtdo^E_`63E-7nBy2CKS=WiH`A)|Al<;B$CT7#DjkIvzx~=Tk(gePZLs;#|{;_ ztb15X+wJttPo{AHSO(w}U@vGL_j1n{(n7ZU(qyERlf^ zJU9ILn#OuL42#4D3=UW2y6}f#Rl{ykSH*lcOnIY*X5V)~f0;<{9KG^KLt#;qULfKe z_I<~bSskMJI{m>aQRekDXW0&j(#i_M%5htP)DS^p7x!OyNQE~$DNSRGpH%H8(^Y=v zp*12AQrF16ZsNNiQx^&|^07=gPdhDHT8q-v!(9E!-C4Hp$KY9t|IUZky-pl^9oJi>beHAC?cZ>$^=UqRxuH!+u4wC}%`^^r)3h z|8^Df-CYG@Mp<1qIac{cra{)=DrE#xOf{;6pMYMsWo!*jZp5#J#W~HcjZUWjdbM_i zb(WsED~i)d8rnbh4yWp@^CMs=&kV=}kv3iVff;gsHmk#4L!_18PsZ(sQ`0AAequEM zzqvnZ4$DD#Pc2zC!P3MZWKu+TVfJoW$8Ey*b-b!h{rG7bgVIX|cXSyC!pRhyCa ze_}NB`j=sWo>M9Fw7@ZyY1y-ful?6R5|vjl*=2sg;ot66RWY5e@ax4C8GNcw$vcW< zEH%w2M<4)b6<-c9gL0#OpvFI)d61D&u5w}2%=6`Dv&y{lxDT%XW0H4(kx3)eM1f6Y z*kmj-%b4H(A{-W6332E$c>VLwXZD}Q8LJqdzB3VgZ@-m>!6oDU%JqZmSD#9fX_$be z?VWyyIDY!q?Gae9#)|HgU zEKU|a&X$vqjJW1f8k$M|_3dB184Luighef31~u~B>9lwB_f^8}on)uV+`RS<`p0IW zeS9BJNU1TzCw4UBw^lXsCO|2VU>j~Zt)I{GW(f8PZ&0A2kf9Dk10LsveGwz88(Daj zPc#3->H5y6%>B;qfm(#ZAx#YdzeM>x|059q;Wl0Ft?G>H=Qq+A_L4eSJ%)O$prM1o zF-)`yfmcH61Uf5=%xg@ez2pb~DG)HY4pb&q3%%M4A#Mnxo%A<1FHeG6xQzlw1iuXN zKN@k8@v_6lzNDjte;J@{4FSB67Lm4b2pOjeFPR*5%}((TRgM$rbeS{D%FoygUEC>Q}H zlAW?_lby<$Py`6LfyaY-4Dk$oz??7wI{XpvFS?$~$>Kf|<3y9jJczb|)<7|SrOy|5 zcL`w&?gu_(q_PIKCJ`h8X&VPS{mM8y{1Dd?B7PY!3;LNBvXa@SZ_DBf-|!woFNKxx zbHov5?IW&h-o!b@74-7+0$_hY^L0&M z7=6B*cMxtSp5ScOGc-i-EH~D;OplA;=Dd{sPc@&3OuNuh3p+cU4Sg_D@F=Mdr{NVb zb;L>5%~NgglvSslwIj2ogyU^XhZo03e@QYBj*jjeF%wE9%MCG(_pTH^X~)umZOZC=?MLr6kp#hKZmc`l2EH!%)B^p-A4VmfA|_aV+__K-(PTOEhojZcy68dC;fDY-Gj_ zW{_hbv#%bwWN6;}`TS}8FH847<|pgzf8;+YHuut#_D`K1`@Z)EFL^)m@OXShe~;T+ z{^M&nH9oO!6B*2FtUwU&&JK%r-yZMo)kDcb@iH_B;6xl&iQoITR!@-mNx&HX#Gz>K z9@zM$`%s8?R77j87fBe;n%AruTli7?bu@>3aJ%!HBPX4Md^3h%U>`s}puY z-5$GAgRsIcjM<0pf<}%eLqpr+9foR%;F>_q=&|g5bw`s8G1YmGrX6{gG}|oWwSMH4 zuKdl6i?Im-3pwet!wmYOY_HbnCz!Qd|DK)H!f(I7NKMf4;e`fb8&7v8U%i}?tz7fz z^X$t*iSN5AyGpb6C*xt?-g>ywEj+W&yZ&%y2(jiL*1Wj*Xy@ZRRo z+(ji$jNOY`I(zc<7Cet*Jj2baVc30tbhW{+zTwOG(ad-2EA*oqAFTFLpNVuQ6h?HN zxH^+}iE?9mJu9LP?{a<-tYqmwLL|?r<_j^5Fjh3f-gCOz33XS?+gnNTg+^rid2?D- z50-r7BPg4+`WnR7zUD>k{fci;K6*Q@Ui-OfEb05!@O6Pz+irPye(R(r`aaMb>Nb0X zJ3wqYuB&|&ZhVIFl<3%o913cZSN7yTAB-CxJfhG#BIy7p@A7ljiz6keTb08D{O89U z`0#Rl;;W7h=F|u!!=CB{_x!`T4q;cW{1bBkIV5-hTRME`(U|ZhsYY0&G5b|;dD^FB zE#4xH>UqleQ!kLgtaY)pQ!UM^&5oZbMUO5Hfa8k4!Rhs?6Y0>TmD=HylCWTNOEz_D zVj=l$nfY4^Jl;j+-u(`$xz}QTvTE}>23|<7w;uPDtKDqGqb}F}uG}9iVaL)CX3>)U zS;l&AUMUPk7rb9)Y~$A2XS<h0d9)< z!d1H8gU9;?(}{V)ck9eZ=bdU^Z71Jqu3{OV{}5uvuKG@ndJ#eb9i_3+tB4~!zOd9j z#EAyGA9e|Y@DrF!Ixub0%Gu$42Ki%0OrKJu*nIug$L{bdUnKkayY?T#?|NX8mK2qz&|Fpi=UeMs{ryl@7VZyb|z zDHwaGdH%llDO0s}U-{$yT)YOOVeGjtW6E9`Fl=||r+GJzJD_o;Yt;3v;X|3H$FH)l zS0FeN?N;DEy*VJ9^dY7B$UkA%Sdrc*ZNedtio+=koZsKe=n-ljcYi$k&CVAmLk!8# z0qU#g=CKdf1@UQ(@Cv&x!k`URL44S7p5*r^=4DyPy!9KWTvr@17;s@jwT7?2>+`lM z?F`&d`>HGx(m9{~2FhWX@7wj}#}hQ0C%#zGuY5LGZ)ZnlNFL&P7$KGnF}}lajIA6t z6x8jYnjP?<&;4L!n z{#0bceNNv2!ACFPk(VnS!_)jUNYf^sy`1Q&yf0wFhv=uaw?6!#JNnAA5|d`5kD4bg z^*#EiF!Bob^OXw*V`&{YJ={nyhsXK` zvhG&Fs~*{;Iw$YWPL-Xbf~RWU8ggqQ9SUv|{nQQrI97e_U$XTP?75ZyfknQxExZ@m z1eRJDCRi*W4>CXTC-@HGMM(I=RpIq~&nmrqkl)1nv@Qb{AL%Xp=?~iP4BO=sgxioUP&$q`1(ApJ1zOwp>oGsl++T`qgZ=WHlb7%Pv@G1QCO>Kv+ z_7Y#l)>tpH_gbQB!;enLvPN$DRjseAGRp*c9i(tCIMYT1HNI?S_~$hB49Vqp!S_)( zJ)CWNg;M_?oPC;obY~#GxU#YmBzy;vZy8zW69kNB-bVENU^-J6aKW@bX2n%nhkgR{ z6~JO5F|0EgunMLvbXsc7n^sz@Udl^-{}E{h&DD>P<@85YQN?NR{?W&iDDY;GFrC`3 z!A8Py>E3wFHq-e}o97F+eXhO0nEg4JQIVmtWzzR|)X35!J#C;L5I*u0_Rtyt$n!nognCx(;{{zsYjMhFI;LibEp7x!sITm!sWjAPxd3?g7jg++qOP18X+`KXY zRFD(w%2vbQ1TUO!+{Z-N(0r{Uyx4)n?5rab_2CiKD9G|B&u!e7bqd`NE%IacjWpqv zi2~9w*o$AeqhK@fPomsvrOvjdFn*Lgxs(#A(nWhU!riM~f#)P7*P{>fq#&z8GQq*E z=aB@{{Dph=J?re#Zr_(RFq$6}W=H`29EA8!sBh=+HQnX~Eem?g3|bzdjIz2x709A+ z>wCi`mehen_UwS;yIvSvGZx;sIOA#b=c}ExYjsroG?WF;UZ&?K_-Q^-xN!-`kJnoH z0BH9dWQO<<;yp0)A-pc|2i27}NaU>EVL+s3S}XXrbhM8`G@HyiE$ z;0__5vg&C)mP=f_ZHNK?q|6iAW-Ctd^%=8mCQkF+l)$6+qI0Mp7hWCK{yUixO-mW>>WE5)_pvMMDnt``;SdnB|0q(Nh0|BYg{O?Bl3^DsA3#B{pZbhVcIJ z>GSPvmUVOXzzDy_+1ByX#6b1_Z{N%Ok6^ z%^$wh=Xj`1x3E3#6Tmaxr{s~AD~KF7&-#KTKkd07ih*q5(cdT8h7<)|O)bdMSN`tz zNvMu!HuO!Dl{)(&_=sNgG|1!@wtZ|5(G52c7 zu7DOA8F@|?ZZgzg9@y;G$LBG(IZ`&X*Y@#YpR`*48LUK6XGE3V_zf9B(QL|~xd3L%K-AvTeqN1g(N<>svGj0_v7TVg13K%RZT9mLaLNXvK z@~}ju1uaXosMv}`MP+9|)PO7zQ2|*J2oUxpBtX{jy93(#EZ_J3@B4q(fBhlXFf*As z=RW7$`?>FPEU&hpgH+W5{0ZdFYR~3;p3ZVTx=thd*TP%)utNW==HC!ng@$Rz_;h=n zvpA!aK-z@pqWd1Uxb9)5QeD5w>``T>4c+d1rFihL83*(l!BrL9@c=B21uGr{dnTAI zDz~FHVcyi6Unv6&|A?6(#lp93{8ZCaX>5oYpzY+=O1}!(GlXB;s9iN z6}AD{ud=&^%&013azk>S_qC`=!dk!bH+AQZsU$#l(t*u_A{tggBHG9imdW>`7KjBit*XilV+zD-c02>x@IFqDEQ z@E8M(1Oi|F+<@aD=p{&jM=B4cVYL^-pq7N>&7Pi~hZEvwpWkOd(ojL2@V2>E-2-^X z^@oE)^fw(wrZQ&Ims5?*{_&aRpd#H{5+Y|ten~CrL#8sEI#xNXzuUeJHOM;7aZ#Me z?%iEb$=J`Ob5fqCF^Wz-v|31du{pwUBtWt#!pk^DDzPq}OJv_Uw^)oNKC zXJR_v>pm60yHLHC-Jy1X1E>1BRP!PN%IJD*4vZs=cak!z2b@frwRLFt@>p#@Iq_#{ zi6r;wL<8EO0pKNBzN^&LVrN;iK?^_kqH*)#j1aTk64{rX>T)I2*v$*wzicBVFjjN_Wd4M#198&4@DOoU+k0}j8qhlr)Q^=#-FWs@?1znSisZSiA8#A< zSkfC2jF9c*U1=P3;;=19_#f+`Xral7SEnMZ)by5Lk!PMb9_&0|!_0bK4-xDpVDtE@ zo#n4&^Dcg7%6zNy$*tb=Kb%m;F@ERW%UQvXccS;&>{uW>#fep;c;P6VqSTZ zR)3mKOOci8upR*ZbChg+%T*Jn9V~$sm#mccqPPZ@X+DvM7s zbkdqUnOOAQs`^Gy=**NSPz9U)R%eB=InpEl9z}7RNj7@OC`C`UB6;T#QKQ&)AoC{h zFOe4G_Z_~tpM;iK#PEfqD>N)S+}U@w#%~$<8HT~C<~Q7Chx@VcU|6kVf0~HE5e#lV zEFo}&kQOcX&U6y>oBniQq~U6OZ``HA)D@LOr{c1#Gmg6Zu{78L>(cm@2zjr`;*_(h zE@Vv&5M%Yy@n5=dS|?@d6oeqzTF0(#qyNAYu)v6OHy%@Cn$<;z7uGRW%CHMsi%$n zRVlE6RW_QZsvr&*V&pk>Wg7nCDg;q|_w>G_^-X|>d zJ$QTZcV1E47yN+JWU2nupT~q#XMD7+Hq;|>p~Fmz{d<;;R6|}(SIKxKzVE$Wz9^d2dg1F2TI8E zjLyswpD&akMG(RLPS4Kc_MBwx9#)!%zms7+L+2%HD0&0q5r3=Vf9d4^a^i!Y$J;fJ zxGGe+rSn*ecJv(wgX$ak948a%bMpsj#OH{*Zr6}%;nZMF4CvzZ;YK5x*mES98AS2l zWty#JKM*e;wAFIUK4KJRZV4vP>nzjq`rgm=e(cZJMQ$I+ zx=|uF2lXxvaE{NCWsTp{sj<1crE=S7gHFMz5to!iUdjEsnM=D%s6Xj$=7I#Pvjb}$ zZx8a-@(c3*pJgFE+XM=i*PGPX9Xb(=#pi9v(k0WyGkWS(S1K>Kh#q!MS648SeVo1{ zoi0V?`SfVj_N*%?>uLPBI^|7!L?auv{11jmoshmaLY57XGMLE<5UcpCZ zQBnhP9t^e2;kF*$0}dI5hIgTZFRV@n@_ex5=vmTUaeA~w@r*}}=2KWWL1yt>9sAu~ z>!61rr=ktkDehib_dw>=9m%S>&)x7?jHw+(;K(6RK4;>a-0QGs{KDx@nk)H)d0&hqWk7*BbrhYB?8YgRAO*eRcYihy1%WNi8PQ-jOX z4Hnrb3i7Bd=}!q(KiR~>2z39V(ny()KGDnjBmF_#xDu703;wUR2>5U(fwO(;ol^%r zbbQy3FKGV`%Y$yk6JSS3nr)%hO{}#3RFrU7dghmSvq9nbc^7JtzLQ=w^y4h$VzyYV z_1YR1MGuhsxZq=7($LwnZ$GH0_)<$o5ZJggV=B>w=C&?F0u?F(;R&KmNV6im@-}8f`CFJ!ML|?Sm&Vq}b<2^zKcGz7ikVLF6-% z#dblL59u5f4DVR8-6zOgr&PZsYOBK>$FHK(E1e7o{ZEZ7o@;JgzS2Fr$38M$gFsLF zZS+&=jYkE?kms%9d_c?epuGs%%irJ7C(c`AkpHJxWPlNpN27b(^Ih zqklQZdGRXwgQ}VX-%|qOM!1)BO={H-@jj*3o9Y#KG`auq$neD_q(9w)ca6SGnX{g$ z+TWx{70T}~guS~_iKf`zIy?ZwBUx4(>;jA#6b{`Olptz-8wr|1>Yvo5J~)29{Z?nM zP=4-Qtg6@*bKL17w!aV;37eRY0twV{xfNDN$&zUI(kGtNJ=LwdqX)gZE*LpE4NbVq zZ|58-2=I=|*@Z zvSc!Q@_m&E5o~;yS%cJ{R12C)pNx_3(&Ik3SlSFGASMTXJlX>6U+RoI^A~D$6)n*i z1v02=1JS_iziftgCm)e;l8FNv`MHk!$_=ms8#cz)+@jman%lqE()^qxp?pV83Z$4u!y09JQ4ev&wz{B<;SX zy!Al|_!AsegrKCBCy#k4i?kE!iG?sIVk|R}2{N2H8u!7&6ZJBCOA&d>_jarysq&G% zmSat7`F|fTXyWBrnczF;hoFuhJUcOr7c`{ z-&4jJ=vZVrYXWeQI#(~pSh8sBVxR#vWfUb)YlUMNFx#?oUAuk0H8ekA#i(EROW@$Q zs(Bur@``@>!`VjRYM?L0IP^+gq6)$`k_&7OfFFDUB5kM0#`c{!hf7>&1^!Ai(VZ6c zo)56>JisQ2M=N*y<17MVQ0P|_=p?ua4PVwNFT?02SGmkJoKZ6Fy51|}2S zcwk}{`ESHBzK|<+Y-D6a51TH~x&&XyQzO3{4;VkQ3C%}RVR?cl6p=VwaC-s68Y5l)!0EZ}wG zpb?w{lMnnDp*3cg)tU>N2?5_wIopQXF;G0&!n%$KG&xIY4H8TqzgGQa%ZX$LRaL#> zoqYiV{M`;?P23>%)4PjY z&FvfPcItZ&dC#pWj43K#3r|@Esol$+A<9=4ye|ERj!66y@jLE{$a3#%0foioKG05PPld8!UUaOUy`~ml;<#? z+VECqQaSVPIw`?AVG9f`nmkeb6pkZ0kLOCg2P{x^>>c$; z$sBTnp=Q@IPz}Gyadz(+;J#+u2BF`99^-dZOcYi6ULf-bpha{RBW@`8s3kuzZ#JRC ziia&UprK`)E_CW|etzy4n*}s3JG5cR`EZTdc%HUtJS`DV`o^xuYr`b82pQT7Wdw{Z z)q0WTsk56HJe)>am0rOXZ;9|=gnS2(pc8;aEw=XTit!Mrhzqi_U?p-)SebX5*5^-osIf)owB^YzB z+_R95sL*3V2X_nsvzk%H@6*bWtWqL4%_;^-Z`SuZj|70c8+u1rAE3y?mctJBHB(=p zOBJX~JI3O|oR508Rvn>c`v&+93(fQ!DanHMTx}Q`ZGoN>kAx@VPLo2E4Xu>ux0l}N z-)%K5;E+eylbq7mHeu)=`Q6n~VTXN9!N-7D3of6W;cmc9Qex2)ErU%PN40w>$7u56 z-Wl?Z0qK1Cr0mlMtm$8f_VqaL-+{)wZhdK^A>OZDxd>nVRwqzw9OR#=rw7gJYujYR z-aM6$T*BEM1Kk=Jx6_}Vj_b1E)+c!~Gqp%^Lg?^;;i^UBRe`9yw|FaHhz!4D;Buw# z5o)2KaV1(S0ADoj7{Oi39$Ki!nsgDQVcQ!=mIO*Fxs z2Ov^K8#mUMF!703*9nleSa67IKU!u-Bfdkf8-Y#}f~-#fO*fwPc&_?)m&Bgb{&pe6fH6j#rCK%3R zdyW9b{!ewc$Z#Y*cFpvS4NnQ)gtfSCQpub^zm9_oSH7I*q??HpA5G}8>~VG`{h^+l zy{4~)C_Shn`|Qo|W?#w}e#;6?NIP%zQZxNUZkLh7s=mhB_Umn5aqE$_Kyous8te>K zIR9@E=>?vmY88#rGQr!?_oiy-01WoX7KLF|i(w5+>-e|R9 z+b%kOo>)LeD;gSR~9pN%$5z|=$r}o09_tMO@fs_B;f*2*Vy)IZ{iuYz!kD+7gIQ` z2dOu?BE#ZXR`kCs_xsSp@~4fw{1p}vmjvTHT%t*NtMgf}TEErT&eQMDh~;Zf1@tYm zPOSXxiaIOri<-o_h>y>Ak;;pkZkpt)VLlsrgfw5B4^=xdY)lM57w$vVFx)?ljOZ#dlr|=b)OM;H81kCH@Xra*IiqEYh?FjcN-SWXC z=x!6f(K?AWtJUe+pr(2-Ck-Cgn~H-S!CuhOp#pEi?gQK5usV#8m6NSsGNCMo$`_9a z5bcY`+J?f53*RuN?@eLBJOFJFQRG$0r=MDg%1c;}+uTZERv<(hVXGC#C~RJCo!hgZ zXn0GIWetYMX+l^{sO))_O%-x3bcyyA4$E&+wP>A~_Jg4S9>1A6oec9Ns5^uN9LfN? z69l6s#3;bFKH8+-@RhDC*BaF%Y-U&YFgo{YRvrH@KL-)RS4jrD6yOy|x#T;+)7PzE z(8TkkCyZJB5C1J_q8|Kz@%luy@rz&UQ0!lJ`76Iv#SN45OFRGRU%%?sF#BI`{_`Uf zSB1$IE*lkNlbXb?zBCfvSvcwaqt2b$rg|zQvr=!RI7y{+A(eVr*xlIJ6Q+oWXj2y{ z)eemzqw++GJ7#gYI;*F@yU||F3u}BG2AgDAq2_pAJN(Q{4;f9QQ0;18!6ThR@H(T2 z&8|@kIkmj%QX6@x3S&R^)9_qxG1#f1LZ!OZ*}N39fT|+CK_!$e;77^s%*FFg@6anB;$jsEDVFBx$qLfNK*QsK$;%>P(L+|B;DN3b( z6~eOOeb~8={reQy^*Y?kY*5K{?VW5fvmF{72hp{U{csxet&o)(D}>>Z_SX4uiw4l| zM&${J)oW{PgVc!XDW#LoYLk3$F1to5tWRs>`AN~i2#4AchNmZtP39nj;bWqTsjbrp z7o$|jMKWcIU|5JkMIocnT~W&9X!CB&BdmozVbVUBY**qoawba%C_*Yx$Pq zLo!l;U{sOerVL0;SJiF6wW&f5B;F`2FDebxZS!&9PP_u?%Vi<3)@iYq^s8ibkE| zS1&D&*cZ)ol6aYr3`!aIJmrP(lp#CT#$!;P+Z$36L0{FJS<-J{&tRPgXCYP$E#io5bL=SOVfP5Sc>>Q~0ubsd$C+>X)LBecHY+bZ0#ey1W{*em<#O19n!|a4 z-|CYXEwc+rtSqBw1Y?EPA{d!mF-hB&?g~j7L;9y6w-aYWckioUP&~uNaxu8qqKZ z2C!e_RBC$qr0B^bI@<<4qKy27r8vin`k=lnaT>#l_prT1_a7ym}?zpHB@($Ir~%sK-w>%!tuqzyumt zSmhJ~k`voSq`Bn^9P!Q+R zFNOVd*6)p#eH>nS=<}PCeIyw!w43s+1}*)`IC8>l&^Q?6PNw`jgDc z5gEg<@bDf|ja1Iee#*XP*fTI#m0#X$A3%(eLc#Z(H?2O;jE=SDHQ@&#?-E&XWN{rS zZc5(*bY+4CgB)c<0&Tmj)uZROvV$%fq=u^&wBt|4vAh4(#oQU&X)jdh_hj0ELFP12}6&HuCts!b#_iFmi>_PuvyXG32=hSKStLdEy8fYH3PwIY4 z3TP$E%X~I6=GF0j470 zmfwx!%E6IcPf5EX%XG{w8%a=H8Gh*l_rhTfdmIoJxsQV*`lk;Z4(cx%ItLe26;RIV znuHb=^?aQZdWSkY+6=;)8-y|gC5~u!ku2{Y85@@yaO;NVv`dM?*y2ye^4z}ZN)Mb@ zV>wPS{|T~)WQDW=geVJ^NOQ%-6lp=GB6a~cV6TYIGfjYJWX+p?ooCHD)KoC|l3l6)Fp+--- zxOek%Qg2z$pF5|Pyjkk^WijPK)yKEwXeM?IQKP-pJ^_B*?4P%)*`*qYx4p{v-r(s( z%Tor)S>RlT>Y;}&V9$)B9gRqal{0TRi!Eak4=HTmHVnh?(t6>GkTemBe+RNH&BQPz z4>hPenQN?U-M&XRCN22~;_dsCDq-A}h!8DC-$2wG^F#-eI2%^x4Ut<`C$6pwFMJ~C z-2UTb-A{+7cqi`*FY>L|c+BjJ>FWD({aqc!lE)qK>%04#@kb2PTM z-Sg?^q6;s%T}UtyZIM{_aP4nH4A%wo}jKs0@k#If}h0&eYge zft6@R!wo;LsGxH`tr4{<2GT3)BR*PJX_#1jXSFfqq10PJzEDzrQPMGBoAGo*RO(NK zLcu?irpnpq>nD}NUf-_jX-jYF;3b@`Q{;Mw`m8azyrphy-WaL9sjOrwX13z~2JG`1 za{GW)6KYxEQn2$R#lijPU2g1Xk{?P6M8FPFtLYsj7Gn<|p029cu<=nhmo)n!H%fuh}k z?Ln#=@zz2s>ec0bNSKLjp>yi{NFwSTNkdkMtzp82fOP3}^-*P0x9oTzH_m9F+cWLt z1*ec;8;_x=!ooLtsb4f^eBUktdb`JOH((dto}cd?rE{PwTVJ%d8twGd6yE_w}9C zQ_rauy04l$zCVA{F`x37FEu=l##plyVoSlD$ip=xX7bvF#wJ;-4VQa&2u9v<#b!nY zgsY{p;#5QtZN38dP3rNFboQ9)qT$R^{JU8(%JLoKBMe&+_};e8u%R4gva9>uS-6=4 z>$x5Hh+Zh|*<>$%bL01Wi`xumZtdLty17y>8=lryE^pM!ov}-aj;*5HC!P7Oi20ft zM(-X^Y5(gVve20K3GeD?Ms)ZCc0|zUoPmI|>Mef4qCI)hw+=YJIO!>V^V@-zFsq&) zTta`fSkg=^eQ&nR4vBirQGCFZY^3tP8oIFOPo}+IQDKZ-tKZ z&5y@)hFztdILBF>@QifKW}*7o-af{`NRfU)Qr45_ z(}ut2++Rb?W7YHOO|Da(sa6hNWf@I@f9!ju+N;TbL$_^7XS~+vZCpBjKbVIUz#LLk zvx52>XdT|QA!;QG3SFtS^ApaChCaXw*h$+NYLLarfvRn+WDtl=^5C$%T+!PeTSi0 z`w3xYdKY&_#M3V2vgoNorPUm3d&}b|<DF;B~&Ab3^F>(!6$ig&wr5N)Ou7}|q6gU0uRam$ste5=TSDAAu z#k2aH@4nUXbv``mYI#z*B~U!y#0IEUl{m$1r*{UZ#nJ5;U>E0rdl-B$!% z-V%sI4Gt#Oyf`+bNg3K9%C|ZJb)e(4u~O@Aw7YH37Oo&IUu7CRt@-=2f#WJBe&-~G zWRH^0^l%Hj+PNjHFsc5j>TBl%yw(QDwG$IgYJk zciloC5&Ao-tVrrk8x~0c!&DUJ1 z-%nq%05l`3tdTQS{hjuw{(!E{s9F+ORd*g8Pk*bksEYno?}eQ#@ebk2{wxvrZ?1+& z7rvgiMQ)kOyxBX?V6AF?+SI!>znna>?y2TS>?@D&EY+0^v!l;mExsfA3Ri`2e21S7 z#y{_&9#q}^t^CrgEFnE>n0HK-_l7Z~jI*BF$V|1mn5#2ewhA2VTO3!(pmo!Yz}m#8 ziyQgBwDgh)9XdCs^v(&TGlp1BL_`~MS+W-%~g zJ64&%y3Uy^Q8Q*=9`B3bQAy4qQx-9k*ql-i+Hu#jIgPZ=uxNF){zg2pmqBITl3kiz zB)7&cq8~-FpR`m)fG&Lj?V>6c;9vdA+1Htcl*WI%`~Jyg83w2_$vm#uLrzx5z14vj zmr88puhXbCev0>xP8Po{<(bL>bLynO)iDyuofz9sb6F`Oc{q$@VP{XJV&Zhw?gn^Vmuvt?A=*~KL;nrh_LAr9YS6U=+^*FtcW#TpqobFs)htzzNn7AnY=T8{!JlvSa9KbeT@Y74DPY;uh?Bkc`|X5D*Yu*n#Z5 z)=KJ4>Xm=_ySb~W%o-*hUI}H1U{uNvvv9wDqbrf#ZdkbTX6VwBp1nw`-g+FB1D95T z(V>k(Ax%{Yi)b)GJut6BF~8*mx1o-a-1;}X04(aQ83K5{RLS~}Remq8R|pjq!e?I4 zs8T&31R<>qzl@NdT9L|ZGN1%)W1CUMXlR&)(54+Ixsl)EimK?S(wwowHN-X;xzzFt z7L*Vg23XTmFm~r0h0bo2OvHEyx>(n2NjUm?Ma9VKjMS({-*_cck0nvaf4W9J{yD0` z-$WWKOaIvV%x%%k(+50#Z4Ek#F0C1(uOfbS_M%;e@s7Br*UQRCd#zTUzvGV=~33PAf*ks7L=!hRIa8T!qUN9 zx3t3rt3s7Yi#+a)n5{AWlWy*N$CZ$2+?pSqaL274@&1mxvFrG0deNDZ&+B}s8~r~b zFN(HTi>fKKmsOt}c-`zj2{+iz0R)$4fyccweL^gn>^uqO%bP~>$3~Y79v?~!d-$e= zbhsozd9}gWr9aup^V0O=wmFg|BGSyqtOHs1kFa*7%bCON$1Ad2^iqYVs5R6D{Jr}? zB0TArDqY-xZK>CvX;u@b-P&_||AU`jvH6qDEa%&t1Uk5qo{!iK5NJGYlx5esCKCDhUP1sMFELkx1C=ko#N9V>xGPUQ#BcaeD=yG3apv%LNJP}Q)5DJ{`g%x?#4_@e zirR`+^=_EFmJ}9m|1?kl-7NEyTV7IZL?z@G5ttfSG6w2M6yK?Go1qXirO{a*(i&De zm~YG2@8-}mzvXzr1c8G*Jsmi{OtXEH$7O%3b@?;@sM$i)pPqEqHF<8LS5u;Ly6cJ3 z1vMcCbq94vNs&dNbocDI-k7&Ks~@(YY9IE3>1t~(zV%oqOH*2UWBJ*O0f*A$X>)w< zEp*_Yv}ckXAWG=ZMI7P5jCN|R)=)7Df<+2yJNL0U)D(!k>YoQX++=UH9N!dBUq2eT zP{&?(ok``^Ar|ey!)HuXqs2`ymX2=%rpiy~7)`lZjbwcas@qGLjx-w>wyfkWsOLW3 zr2Gz0v))$S=-B}F+6Z21QHktOYL*Vmt&{GLwbjL4B}Ms+6k{YcnueunLkYFRDB;I& z^igO0i@5%^->p*q=Rfk@FZA#1zA7gy&UEf%oMCNS?_@a>rc`@^FLrTLDQCG?b9;{~ z7yotAd&!z-DqrXD(u^_VtX*&fA8|ugcee&EF()f04Dgc-JJb0i(n7~U$cjhQTe{M{_M zTF>fqw?ciW`bw&MBH#`IaHLnn#Ep1symeah^KKC&bz-!D^x(XnNOt`uT1~jsDG-4FO3Cvq)o%n?U(lfm1NtMj>jG{pW zQ{H5UxHyK97t{?P@ht}JOPmScyNvBM9hf!zR%d>ch_>gUVtz39sx05BVx$PQ{V@bP z3gW8|RRtTvg!?Rq;rJRrqQ)$aQe^m8;jcs21xze4G2dQ7LDU!woUV_w7+cgye3C$N z@$BETuiWjBw3=xX+;B1}XV0$g`r$Hg`SJrP*Um26Fv0027#?}+#iPLW-Z8&h-#ZvI zJ0}il8R7d|lWg%nUfs;OoL@Z}R|*P7!ft5$XXNk7q#|30DhG>ZqQuzpI4ai4= zbWF+A#%)Y|B?8W4*7nYKm*$l7ht|v7QrboT7SsNBRL}cRov(hOxa$Mm5Q~ znGr?9=L1*!mm}8kQj6YApyIs0j#Kg(drnEbszU5{%F=b{yV=)oOYl9};yqU-ebVu&rbt9mmyDZs&S4urq z*jO7v0Sl50sC89t@qBRLDXB)e=_CRu?Sk&DO!pkf?FZc-prnM_tGwqM0sikhm1d*j0#dXJ7RDUD(^fsZwMXSWyOkVmV4EjrJSXU~ za8zbCCXvu8;(=Cpcnsh0VtR>VF+vtc5~R?pVgzz{j_^9IkI%gQIL|1{fb@&ejp<8c zwtW*qQkORQGRU{{3GKoHe=-L7rh?M5cq?aYw=u1f_Z$t;hQMrr#G&~m)&R{!gF0ir zboE1zt$5xTql(nACPHSKG8OCb3SL<_?t4V-C9%s~ddnCcvF0t+fXv`$r+d_^=*IyN z<+A^n5%N#zA%9;qkz&_5XZMaFPdt?0Qs2R{38hg~?<61qwC%=bY2I_v1!JbBNj=55 z-6bVg>l4ZmDR_^AVDwY_m;-WZcQQ&^llmMu=Q-B>X|Nd3|K?MZ9`wkH+qr9_F5@t) zm9!*3~4(n!=s~B|cEr z{WPQtRX%5ShUj9r)T>E4k9p>z&ug@!PWDy?LMF|pLE0!;ad;nfc{Bh$1#wG*1H zz>GIG$Gm7C)bZd-cMO6Dguj}}}neE#w4YjO1nRRgYH*DVk(`MA3kQOFq1p@f4oi1BzmG5h>XSz|Nr zW4rsxrrx%p)K=)lDA}RNBnm0`vFqD>fb0{U+=4~ z+hxSEM!iRKI_yz zYVXnoG|5I((dg0@%K8n7jMP^eg(iynkit#d>Qnga5z`vl#j1ouOLTk} zGIW(}j9)wKozlN5w}*($7K2}CU7;SvSn9WQvrUvGgBwr})E*3~Gxa&-^h767j6~qo zQHz}p0CcmI%|jE=QJYX&dF8!^@u$+Xrh-W0&F+-dVf5sN;^Rj>LOT>?LxjL|kA#M( zDGWUsGaD5v)D{|3GypiY8XZ8|?e%r9(R)JN-O&LtvllCZ$(ch)+@TwZdq2X=m&^gY z7u%syoT;)mx)i8g-oTQY7&bEZbsQ1PNY9*(PHdo22YC?FW>6y)RYVyRG(5I zH(U&XR-zV^ZCR3z#0ksC@C#T84h^+fe*g13zfpWJTfeiXvX!;3p#U;!T3VDVB|I07 z7A|JEJOqah0VPiKz~#n{){w&3s?ovGY~k(F%HE3PfaF@Qqf+%=+M@Jrd!5VB5j%1( zn){?8%Q-O|)vWAWl&`SWLX|xU)hmsmDzt2lj`7LJc(7&pRuyfe098TW=PSoqpX~Bl zjg4ZRJS9&;)6nF3c%B*rh${}3#_fWDUB1mskyJSy7pW#F{$(btfgyf`$Eh$yI>UI^ z+AcTmwz1%1edXq}5u1I23#>MH88P&LfZ8<3SZKag2c+h?yf6+UngfE*QdUi($-dmr zV#4XK*C9mKqdkq+8|@7o~&RM&5QvM^gBrX_^J&sN{!f6njK{wHb8=^4+R6^<*WD$s=l@ z+}E&(A-GJI#}GS=VEHx;XDnv0c~U1oG#BupC;SqOjzP?uy^TMaNrUOs6i)HRHx8*2 zX1-=^hXV?aWWp@G8^vuw)M+PqTQEi9(* zdYTnaGX#Pz91ydehmPm{%VqzqLA-km^;aPUbXIFAcbMumPQI&3Uc+D)hiO>l?X#5R z;Ze=!T-9d{H@Aq1-!v_jiF;FK3SWVgPEV1snmYoyF?QhR@rp132cMceV3r4T)Y~uW zijtc+j1;mq;TUeljYmWN2u9{3Y8@T@aTOAMe}RnJ=6UcyOpQObGeGYwDz>nX;hS4^ z7p3Q=3ZE$6=Vj&QSOO+Y42H=r+ljDa;1_Lwqh`)Nug-lk&?6qxu5qYb{+mF|%lXa7U5D6HmWwghqF}n+zG=NNbYNo_ar!p>`(Az8sOjO zIIxbNP`vyxHivjF3Yv?0iKnJ?UF@Qey65QeaStl#p#o%%9!^8^-35#8G$Mf3{;0PkoV&wadT=y;z)e}=|A2irohOJ0ABt5 zhj~j0uM+&&nccCss7-WYIjI31+ql;BG8Xucf$kQGt^&NvgFEl|?YpknkSHS#1=!&& z%hnUCh56p&-)_#qG)^EcbGgNRU=Z))t>~Vhvv>VVplrLqy8})rSet1T)LaHQQCOFw z*L@nS*lSACDFb*{J)N|r;^ZAc>_ZT2hD3ssR+;t$h~tOsFk1aI;^B_$4-fqnyez$_)ScSbO^ z9`%Y&F-U_xz20MQm;&Ve2>%lzsJ@xi)Ni#c6oQ_UV|2unn~}Hox`tA=~(DU$=r~FBc=^jAkd!Da3 zt78gNiO0TKFAL7S|FboP0MmzfDwsKoCqGg7c6SHfh4O=}g(iZqjf-IzSb~p5xp$x@ z4D5;ybYBDj)AKYo_Dgqv7Aq4}vCbRQRM+3?{DO_7;NQb^IaT5KR_6<}o2>qSa(Bn3 z6-sbS4&=)hqQQIWBh>U5m`UihpWyT2S=3wJ|MW3q;|_;4#4!i|j>fD-z(2y-t0xt- zeH$@+OI&%7M&RK^c!4fb&m53OA7O>0V-irMjQ*#`su{ZD`qg{?Os5C5Zf-j{)4@g@ zargP}-qHlS&Gj*74lLuh+8!?MZipTlO`G<-W7PF|dGo<&w^#Z3f&gXrjboj}&hoQ! zS_{#{^V`0ct2b+9g|ruy8)FJ!07$d2RR)W*tPZ&Vjvg(Q0eYFuiX4j|UQ1&B5{%%d zqV+wKuAMj9(Z6kQO~6Q zKpJyLXU7X>kDVShbHf#He@ByR-uQ8uZ z-|G1DnYIm`)O+y(!mjZjL*`r&ZD7QwR~lTBEHxR_EiMo0swO$GSc|u`eleO_|T%S@JWhXexUWZW0V)Yq=ss8wM=F1<9V zPH3(kJ>0<=G~}~POaF8`G;4Z9`ngci>s`zlnichrEcK|*1UD7*#W_^>jCF;Al4{yl zmAuS*g{`mJgm*?j27=l|R4>K+Hl3I2Zx>ux7z~A}UU;hMSYrt<))q2rhb3Hm!!R z)KrBkH!Ys1hIA%-nK7spPSr|eP%1hJYDIG4$bkb|;oXmE2v-OicU|W*Hl=xkyMzbp z=;-ur;;GSNje}PBZnXRVe6V;tcgU#iV-`ZyWX${;OfEuVo4WFhx8( z7??KjL|{Y8DZBVyXVe4-AqfY)53-}N=L)UoK-nQNwLvnBE46UDxoP=X}UlKLW?vJEGSqgp$RA?gpxp#bw|{- zcKP?-_nh!tHjG7Z^SU&{BZ{6bF+-lW}Zl{^pECbstDjb z!L~MJBHJ~-u zn{~CCQ8ZrV?TleX;Br_1ZTB!u5u&EJXlgB^n+4x>KKebL^l5*4>%orI|Mc52uT(qt zYZ{Y-*lx^jEzofvg4uG+0Z=eIKeyF9F&TR6`~Qniw^T0NuU;X=o5LRxmUeblS269h z|Fx(1qaOOyu$>}w4(R_!YpbAIAb#_g3*|2_+5hN^X8eC>>4z~~UjQNqq$rSg-bVWt zQsi)N(+qCMjfUoHsAvx&rS7BWYAn&N7VZ}*b<09qMg7Sf-6FqM($hm*>!hbc;DTkhKyuah@Shi|9cDQ*AKWw#GJQVv zVk)qG0jrCIy)&N(*TbQtnjMrDAuJrNHAD_q%vv=-R1rb9sC%UPz0t)Wv7?*B-j{Q< zy1a$q1Oo90PX#@-C(XO}=SlYL2@U`GQ`=#~-+!LOz`MY}yN9-r3X(`lFvyA?|8OV` z5m(#uU26>=?RzF&c zi@Gw50jJ85rk^Un?*pKg)1Hu;0sD)t#gLCL7^EegqRxvEe7ER{JY}ofVdiWL6Y8K1 zreqsr=1h^O2Hqv>)9AMGt-N0B;M;?w7Mmv%27cdu=Iuy>%p#Bz7QQ9B1&50$A{{`U zivMaqNVka#Zv(kIP!>q?p&)rGhPoEs7KVW=u;iI7Obf_UYOeq}WIR`B>H^DKl?!)=(NSBK<|+NL)!!@J-T|KlYq|%Nl)(%2it)Dd_ngj zQRuFrAI%7WA|`<5;MtY5Hi)Et{r)3i3~WS(=x2pvFTCE@=Qw)hH9NLU3k7|&uw)II6eNjdno?|;^DHESnv|0Jxs69y zQrB|Nia$eKG?xFQWz5cTqd041L{9tV=A%#mp|x%(dXUNrOzC zxRnY+p$_&4t7isMkVV1XTvADwPfSPu3~x-FL}^IMmxUYB{NIxbU)*fJbZm-!;^Mvz z_KC1jU;jNe&%W^+2RLP1I^u=yyHe+iTV85sSGe^-MP0s-)FM$@NO2V4hHFhb!Ntgf zcS5QrT%0m?&wP31sKNjCNF=*F+3|y!R8iRbvnsq6Tdt#Wiek2J{Wo$}e;egIny~b= zn!(MWs!m!VT&?GAm28^O?k=wMg~S>*HMs-zdP*xvqgQZ!S)IE#@!M?}m8Gdj9rQub z&kC93$^-Fc+0tDV$<<~aJWhP)nyrlEu|;BpQ&!Sy^AULwa^cF_mN5j}<5{sHpOZ3b zH?zay1fHuNXJtHFFUQDuwvvMv^cL!NL^SPEw(8!z`DUDB$Uyq%Pc<Xon5vS%(b zsLRKV+BPxAKk~@it%oaL0cQ$oX_#=n%?BPt?(JT}CGI{;JZTRzBhIh#%4jPNU4D72 z-(Y!8Um)>ehN5LF*r;H!gm-*VxmF6C@8TV?;HRe5g%rCOUUM;(d$NZInQ!%b-6!u7 z!gA>;RJfhX&zk=xsVOcn-*}RFgk1YRRv@`jIj-Zm#7F!yQ3Vv@TD9%*oh^&!%(E0H z-dKk$Nu^f-(!Hu*nGO#!by?obmJ&($a>FG$^+qWJ)4|DuBZhnp2AB4FnwiX(7;EIa z+{u<~Y&di8S^Z13XO~_|InDU2SIUvAjFl>o!Uu%;UL3z;zR%+>hq}m(GcV(ASeCmV zxAJ0$U@j+@TE39VMIYQ8lS(I7_Ly*#i1;uZ@*=0>TUu2P+AgRR5CHt~SwdKytE4O*=!vj3hy#bDn#fBez6JWAui6<=C&3ec+#5@UuOATVi zKH29J&1-AVraT+ama(Z;j#mi!$h1f9HmN0Br7)LZ)+3dw7F1;#_(9J-?Wr0$Z?@!o zddHo7v=NLyYd%3sH1d4d`%mtXHqQ##uZ<+WzoTf0*)J)=X>_Y-G2K0T8h!1`G0Uw3 z?x)!>iDw<8J<G+l7x2)8M@$6Ja*=Dlg?sc8x7uL5g=I@C~6J|-ZOc_WOcm+$T_t59ccg(k_q7d77Qyp{_gK_HUSGou zQt7$i+(-NRG>NA)uU$WWtk~RPK6;AzI?-0zpD+yNcc&lQ2xVn+v}AP{HF{sQO!;VY zBIXi%ZXT_2ucg*9_A`fU^P6X)oO|aoMTP{K0$B}w8;qj8l=k1=YjsO4eu`WTYf+Q{ zeq67<3n2q>v%4#!qm-A5mrXZlzbVSUcrn7Cv^3{-GQ0vTq&o&l_7= zC3;FB6nYcq&*}-Vj67n_YL4VRZ}EwJ1ZI6o_L`i)-PUn`V^8N$*B2RVTHY}unNl@2 z>3WQhVPzaRz!EGg#(@eZKlb`t;uLd~;*M$o%guBY4V(4{j2R+%02&ibV{dSO3aVJv zUv)?Cz=`T-o(9eh=BP=0{_!j`_X4~V&r#?M%%C6t^|AVymY?m^UH*W9Z_7fI0Kimv z{apiabPD{8FhpwnyYE4L0P6urTa*VbWH1Lw0pNVrYY<7hM?WUv4Y6zyQP>frGj|JfQ00lRiz< zj@%aN`u}JDpm9DE;2~@h-h;k5dF#WM|1E!$Hb#oXO8@99Q1H4 zkA8bZ)C~gOjrsZq)l5&yOKcQ{Xsq)!ukp&1gANp%At~!^FWQBa{=)(1xha9A1aH7g zoaC#)C<8_mdeuBanKU8Z;u%^;>iqLd&=QzqhbD;+|A47XuKoXy|A+MeeOwN`%Z;0_ zvS@@xSlH)xe-t>n0|JD}z*m*xNBS?1KabNN@BNEU|7-jIQ;x)7=6s?nv)=~S(4rK= zONh=QaiF@Q8$5}2q8Egb>+2+i+AeTm^kspI-KGA04CG3*%Wb_DX3GwP)8N>#)J~;o zD&`r?)chGR@8=%5B*r3jkZ{VK08(#3t>SCwC5k9Q!Ttf${M#$r+xnyGtP5SS|1+rj zKOp~q4b%VA7Y-WUQkync9n3r%j-+Aro#DUt*$tDwCUGQ5P-uQY<+`*YMJPEy*<1Uu z60Y#_QwZR-^7&rR7n`J~U2c;HZ>h;B+;?4|CHlYKdd$NOOOThycotNiX%6}+piUe) zim5m?)fz(>+_K97DZ#f>*=CXs;RlJ(c8mv&(D3j6{Q+woLQ;(zKz_^Ezx($G>@!dw zgD8&<;{dK`x|vELs=WRQW|au=uEdGl^F>)T@SCk}ix>h9yHIrhyT z8C~qkaas)D%d9z)4{Z|~!iI7YJmh!o9@PtvK6L|o2*CqJN**jdZworx*0P!diSZSn zq7d(URw8`7eQlIzCGX`Gptkc#qlVwTRj%2Bt;hMcz?yQmBF9lNHJfYfQXkaYykiH# z?9jmU1#THm=;W(+;ahic@N2{j=IENeL1+BU%Gy;V_}tc35+R@wsE}`nqTW@ zi!0d5yNU&4xhz7*OqqaKT^yR4crUup;i(I+hR70XEO|TT9gStB14(h(VF&lf93q?+ zfggGA#qaWPpGx!b{Jvvf4wM%y-ni$?l)*C^4Cc444*A2zDx-ATm51JEn}HW~$VD$t zvqtaqyGB=>H@W_Bgu3zfiw?c(ibQLfBZ_z4$?)ms_KbHlybkgS6kskOXFJy_ici>iq6DXX#$Jf!aep*v5fW^G)w zecinzLeaT5Yfb}vfa3)p+D9opc-cKVa!=c)zu}T~6+x4xslxVENUElh|K(M4suPLx-zr9Lnl5zoPPGjP!GiFu2u8f0 zFGrZ8Ah%QE)Iup%Lh-XOM@kkyd-v8&l%scuq~v zPAd{@RWOa2)`zyQ2I)Lm3nbeP+yphhY*EG!hAzz7j?^L8D7IvHcZgaV4|UPAC3TjH z+6emSvthym>6u+HLOPrQK}zDCt!UNlIpttXDYnJA&o%nenyRUP`Im)DMNWxM`VW)@ z*wH&GC_yO$2<`{ut~q7?QTT~Yyz9Rsl?t@|me6a{VCwuf6I_GNtPKwv1Qu#;NI-_Iw>*FN2@Pm9$D znNQu&(E1p%yE9N}0eGZSrY3B%sSqiwFse7_3rZaOiThLYl`qe2VIL6OSEn}LrYX^9 z(Td0u>T)OH-wx>2usO0jA5`c>T|a2V?EFdxO!v{CN&G}jS`$sy*kZ__ znD#9^!D!`m7h*2lK9=7F2}^4(s0W*Xwlqpmi0(tY=iRG!>PH!_%b)_xVeVTM1h-Z0 z;9y$HFqwp4EQCm;32xd^DvB(@FR#y(G-Fd1j-lkxL*~DK1i$7(tMo146c`u=7lCgT z?Tyr{u0Ql#1wTLg_)$=cgSx?^?AH8Omz7HL%;MJSD=fbRgcxwON;8rbSPETlcH~en zX_TK(hjtryl97~Kz}W3+!z7W|>d{a*NsQT6unO`!uZ@k~uEvp4YG3)vIX;Sl4$Fu1G1H@u4M> z&DKdV?wtw|caNwCdiC8Wr8ujjZC>Rs%SR|5GZ}d{%A+a0-!rHc0mKC^-ihg>?@he) zYTtY-Qs?%66tPEc$eRgZW)+?u3JUl6Qyioe zy(@&j?<)+wui(GL4L4W%iUuvj0nB@v=I~SC+^yC$G)LsYJbr8uc+n-48zy;N{;=9d z1`f}I_iltaR8I(bv3pWN>W>f9)_e-1IPqoVG!VR`@HMT$eY9QG-no~d?;j6>X#t%l zkXmo<5(A2CCP;@YvH?+De+Y=Kf!cW8={sFSLO2PxCpBE{?J`RGwDC0Z^{u4L^Npq5 z@9fc|n}~0&pIS*EW;>Fd%s}>WG7zFqyuYdXx);ADX^>9bd?EaoX5!X#&lVOeuh4fs z%TuHxEk%}%GE=6ors38(WaDDzzJ;W+DDP)CU|~B|d}Br@fWTaeCTcYCko%$GAk0;2 zb5`W;G-Sc&1)AfJUph#W1Bad&(&J{CJ+cFKPz_&1i>;wCzBy)sLAc{Ll@*=KBHIl*r}J9sNztO);l7 zPUT6PW|)Xd6ftj%`ec0ljO$2dkKYgq{@q5<)T^Z9x>+$V8-r^OC?&+r%)~LDZLJ}8 z1N%~r;P8ZlS>yHmPi+I)wjp>(T2qP;|A4WlH-Cr~A9H}#%0sGLcj}pyb!}$zDT@-< zsdx*VS=1Ye+6q?rbk7aq8On7Z%|U{N7sS+(z4KgjL5jTcpfNT*Y-rS8sj$w*;c&*= zr4;@%J}4}I8ovMmx%MJ9SpU{<2A?a-*n;*<#+Qo9evzqCz*-tD#pNnzykm~@B-*vb zX(IM-myNerAgLjMuNr&QoJPrz&>PFaeub`rF6jl4K*vVh2h7gpV;J(QAq8;1^9^%UFZ73Zk|um?4yitqnv}%NEdcPqsKnvKP*<8 zou^=@TkP0C{M-uwn-ELD)1G;`i2mOR#muVNhqnFkHeLq&-Kx86sc+@ANqkZEj#P%p_9=)WEK5cHd^gzkAv9?SkTGiN!ac!$rf zhw9c)F?RLWx)`7WCH8agc(!gO!UFBC%ZJrbAHdN#1dPD;74w9$Q75nthu;jzG56kn z{7iv`FtrU9HYmQ$SGGVgvwyZRSdD&p|Kl%?S7g5QNGA~Hu`o=>+s-3n;cEMmKJei1 zVQ>G_dh;ZxMOhYEAiksa*yX*O4JRT%Wr;#KkgSJMa{RE?S^r%mnTc8hYG&LGbWkal z3hsNgPr>wRJUj9L-V@I^c_2e-#M0PjY9M~qV8sWzU~hepH7je)5$$=K)!wI5Fin$& z&3tXJdGX5LcWq~meN$a_(KD@tJMA(RV>|~iu;S4}9nIu~ZhI~>=+-&PJ3jg7}76L96NZ%w4u|2s!ofM(0-USE;-vlBB z0Vd3+y-H=+&GaR}b&Y}I+ThyO1h;3}18R6ZePO09vHe41I}Nn9Zg z^OZKg|s!SZ1kOHEu30upGcbLU>0=s(JMXAMk(P%a4 zAc(B*#|jEr&;XZmw-2_bmhH>ZP?X$Gg>>Y##YP}cqNJ+yvf|{?nP(h)qxJH3!eAp>yL|GQ^v~ziyFM;ONgwZVCpu<8;7wJNPqX)s{je#x{=_0_j0tQ z=wKz8zi{dL-AD>cY>pnS_2*gLTj~Rv(gI5ndP~?Q_2R{ zmYLhDN77gJN5BkCLz?O}%s?1`4k_n8u%l$KNy2I_KWkk7dV;>Vw(KttZs>!DLUT$? z2v!2+ZPW}6ie3B6a{_H8OGXqNxZ_V{q-FPd@Esma?8g0e(YSw?%N8~ZBO1LNk0N_q zOJ>0A3RE%76j}7ES;SOPl4yu$?+_WHWL%EpdPKY8CDRvn%!F6ech1Q~N2YOneu=l5 zE0?F7)FjQ8akq!`j`C3hW<}fp>s978Y^;qca}yuwAxt$&3PC(qeU$^L!z~ykK=2+d zLM(~UZ{tGeFvVyo!V>~$epkoqGx)->>U{ttJ(<^s<70Z27~nK!IR_BZifbO3xJ(5} zf_(^*yOV&@b=B|pX93OdF;D77n?Zi%o=`_vd=Z$EE^QRlM_SIq8DLqSm9Mm3E&$N> zJ!)Pbg+$WwC-x1=zs3xU0*Ne8`ds6GZ5gl*Ww0ghG@z$abBk*I%ewFICg0OK-vW`( z{FK2uAcL_Zjso#=1mQs8{TizS&zZW8eK8D*J^K_cLaEsJ)O_pj;ewhN znqYmenMN$_q8T8(5Ulv0(vZ3K=1$t?vgVTygXoV5htPqO6U}f0sQ{+tH4_B_P%mnj z>ROLBV~O&4Z*D0fzr@{rbx5I1tE#5#Y2`E|EoTYo8HXY)$%E#GLD> zxl(u%uXG58>P93$zT0j9h>g1TDTsIcseRhR!49RZr@QWwCiG{)!0uCb+6bLjKud>V zo?Vs1?UW;VKn9pOr_2$a__Fl0Hrho7m}D&rIH;O3H93m{+#XTr&>Fw3Z@RGQmAx)U z&-XJ3>t6kmw1#V{5HYznt`KUNj45`rw}?)6;>XD05H-mXZR4^F#5Lx zevk&br8b=@+udN*mP>5%TH(GUNrR(yIPWuwi~DU8(H$&v#;&R7K*PI_H|*-+%rv=xZVkyLQ7+i|v`( zJC43k#U48qiLW7jjcfR5R37G{mcI$ZUAt=`1s&$|&W(>dx*J;%=)`{QF?(q|f zPVZKLg=G1<9XIXBKlFV%NGFYMkol33`=VP=)QJeld9LK;@jvq@%-`I7s~y|MOn?iSSG!N1(%xX$1mrfdJVN^ zr&>m>>a|!<9iam@rB+MYrR9;FvR-io!fTN*kNmIYAbp9IsBH2Y{e|Hg=jYVgVd=UR z+mpNacwOu86FG6fcGk_g4OXi`G@U}MbG5a)v!LlmQ*npilIuEnuNFfeok)qFR=8lROp(sNWIV0YGq+K`inM5<8?e(saRP?Rjx)$2T$=D(d+!^DA1G3k_9v^ERhHpmxogm9}4I1 z?*(;q!A4lq*4hF3L&xUC1-1kQqkXn36?d60TH^Bl zi3%WrkxN(m(T!ks@_5)$Bvmm6h|hN?xAAT5$P=Ky3HDu1p zE6wReQu)_6C;yJW_>)dU{szoxz>o2tSkWuFT$71Aw^Q73WG#2zKrJdhsw_4;jtQWr z>jJK4%{%l3*O*VGM~d+XzAxgeox3Ww|Ge@-xS4`X`}m@MM1GQ#aWML0jna4B{8boZ zBNO#3$AJyiew@$okWTjfY?1Lj`ng_*43th*in1It_-4+$7kaU>dc3t z?vpmr+`+8~HkvofKaeedUhL0<=-<~{k(6-cEY6MY9Z-kBs_`~AC@u>(Waz%4e?YTa z#bjdv>{xvkDY7X}C*^X(ZPyVW?$Sw-G%HX_3P%RxwEiCN7dkA_y2li7kFSIh)4EhD z;UTn^N5-tqO`d{!d<0J_3q{1&i-{nVfj2|NHy-G$%)1oh#`85tK6lO=^!m>s0kU9d zHl_PL?ADuLX-PNq-nZWvVap#tFtUHm9s;D}MC|f?g139257wi&zWi$-yW`osLq)-d z(Ol(!1mHjJ54u37V6|eVCXnQRfagCx6{;Kyso(H8kq0z2wctIS@dkEB)ZfI?#)yx5 zsKO+f%Y0~$t;CsLn`h8=Sp|;PO&7@7^z{(Tmw#HOK5c-4>|)2#z!m2b&T(HUTs&&# z7ksqZMIFL7lteu|V7a(?{rp9~S0@A!ui|fwS;?GJ0uvh0hj)I7_+=rZ`P-%^oJ(2b|jvZGTj(u|ensv^C0R^b0+&hZ1cD5lm z7oNw6|Cf8MAx<;{l^J<5n9XTp6#|W=3W*W-OY`Rc490L4INH{*= zhs$Lc4{b+L@|h|Yc3o?c&o|hMrd5J9-s8s_|BYuc1T+3mo%fL_3}uUfFqH#Cg_q<8 zCo07hwo#%3Mz|q#t^xo^f+Mi6j6HUsK|rfGfNDq@B!u)EUYgqNmkMs~fq#vP{7zG- z9N8Zz<9pBs((AP1(_@Z0^Xa!`!QFmz{{z+{It}?s*$w`e4thpj<;Oo2dVPjXZ<=87 z$!n-L93k7tlTGuvbr=`5bAPv`U9em!&yiY$A2zA)cRzI}$qR6gfm(6l1Mwpdhjfj+ z6Ior4+hF${jEmA>FkTavMkt+~OWr-~6p_au+2LZU_K@{lS(gHVJVph<{w8z-c8f%wq>H z81p>bDcx-<**W|Ntcn8Iub%*0w{PHV&W;Y~$l4G;(y+)d_!zKX9!r0iQOjD2}dY(JVa&PSjnC2 zRxWZKx;bPlW+SW?zu^JK;n(<^K%n0~it|OM z5re<+VZorzp61o=f&_w6z}j_J?qErtEvwlZTwD8bcLg-AiQ8cri<8e(N9 z5RyUc>w&hY%$|eO+u&} z`&a>6&v(leLk=mRprtwQg%7m83?Y>l-bs_cXxjyj5lG}#pTESQbe~5w(aSCxJw{q7 zVfM6u10duO{HJ&C@?PF=KlIx9NuuO0kR-g>=F{3XTL7oY+GgauSf;poX6fLO^0|lF znbH8l)|!vv=3>>v`CuNfvsKGHT|0IMitGA{#98ok$Tzw3vUW06)@ZuMGY!Y~edN8N z$nu$$wFe(ewTQOk9sln0GL5bm=yG*aw4ZcxPL+v> z2EgF0wL?wzaY-nRI-6RbDPdvj_i8pLPe9hf4)`8x~|6O&e=^Um`yLGCJY zId)Q#T4nkoLhxk>B!cXq4IO~5e|S6>t}vwj<%06~1<-I4>-_?DhuLiapZ-{s@G@=3E;|0R zMnaWoT6;ECk){zS{!PtuSWr*7YRz^&LL*XFA7prSks6#tjgllOZ?cXUq3vBvMBnik z2&LxA7IyKx`(XOcKJ!%q4wL?|KJ5PGcc$Ko6WubGD>@8n=Es8ZPAWpn{BcVqd}Plz z=8@s{7o{@Iqnff6YYlaRPh-{|7k8W_J3^?fAI9Muu;@NkhXU2jG0}i!1Ud*0{5i#F zZaMm4QtH5cwcYWn^s<(f% z24DF5zdygc{f#NdrUOJsp{G``rxi(uFtcYdE8cx+xM8j{FB`6(F9F);57^BV-3gYN zP}DS%YR0cCgXBJbF@ZFC19%T|08y9r0~FU^HsSwe(lk?glNJ_!c&OSiHqg@FqPq%tyWojcy2v)n0tLB6!8lGmGd`B7k%hZ!eBqE>TaKs3l-#&z zu3>sx`=X?zq8bdY>Ee>8BKfA!;gXSKs4veA2(P=4f=IR%fM?BPzrNY73VFq#vKe0M z!&M4G<<$td>JW~i(=^|H$d`ioTOXJ4a1sz3?gnWfY4DK!(7_CKdwTJeZV;Tw+ki{`**EjR9a2vqZ!1a>NBPS}M&t!iDB--LmE7Mo@t zRz1$_UJ9*St2m-yk2;_hST#SR*7co%Y!NoTIrwRg2qy~SMIxuqDSZv~s@K z#B@R1X{6R;t{C49OY5iWxqsiKc=e&F94t)_7?$L>|2D7yB&O4$??tZRM#-%b{K!wImr8O+qt ze&?aYSUfBoGdtaG5xyF6S9-gG=>QYiMg1%c$wo2j?Q?n%%?vXK2K|aFkf=)ZP z|7C-JZ2o`yj`$7$PCGeo0_fM?7Xvz(wI8@EsNYn2ghqassuzL($f4b}lmK;uX`~Bd zPlD4*lLSDjER2bwS`A#%yM1cwt!vpDrx*7efR=&Frw#LidRz^j$w}fsPyPX`qz7PJr}}M=J-=_@ zfE5e~L4xUJ{vuaBHAzA!-DaN(hE>4vx+QT+Sr5cJ~SamK{%CJ#KQh zgmW;+j|b%7#r&mR%zsbd5KXAu-I&eov_K5g+c^orY30BR{uor5qf@W?YMaA4^mv~+ ztJ5zcEJq|^ZQ+L-x_0E?eUr3du-yLd{oh$^Y%Y$J7%yRQHO%%RsBlI1)rS703BM8D zuBSSr^V6rI9er{vZ%=N;L{w(I|Mzs9_N4@#U>7$rsw*(XNHOV6uFu-@R}(|N=mO)g z9!^vI4kZmzfJ)kApdH4}(ar!;Fmu3lzD9=|^y-Z_`)h|{)y`SMX{cR4^$RGJldACI zOa%Fry6fBA@K^{*D+iX353LRaPkFNjTTGJaovH4USkEI9elg+_>Ls*&J9&<2T5o0@`3$l`9R|HJjLZEj?-KV6T*K>8_K#}>3dC$@7%5caCP`*z`^?YQoMnY zz_$A$S+}hHD;1@S`3z>EB3ScnHt0rhcR@c-0kLQskO+^2rJLevb|#0w#qx|mpl@JH zv#xdESq#V!JrFnFzM(w0A5JU#!J=(oW==Y#pqhdEIO!0;(+oNICVu3 zzGmDD+<==HFbEQ$UN%?+_R=L@9a>cAb5+=`t8Rb1(O$FE;8F?nQqDNYI!T#w(|JMz zAn)~qmk_y_Va`LGPu~X0XCN_bplSXv1&AsRR@0v<<=yX6xj$4&W;0@&6HJ+cKVa|R zwKQ=W-{{P7Jm3uJ!EPWnkK!j82!7$m#CM|lkqAK(qv3zql$S*Rg~NMt&P^3u{A&Pt z7@hbwZVrCrh>HWeFR;V14R4)Y+l)L1L;}8T1+m;Hr2o0UX*IsyXJfke(zs@?04$*Wi=s= z6@G(!clLy|0wgVN0Y`gFrPJ<9^_m}smNS6K_~3D}^e4jz18Wt`S_y%I)ncAlK5Lag-WZs`}DJG<=e)-EP!Fu(3AkMH}yz^9ZtB9(mir?lv7763H0uCWUi*a|OrxUR?O zQKa%B`;cfo&97tB}gvc@-XJ9~Y}0AOrX zN7M*q^qMlV+2q5b3t>cp?YUV&1CPW682=--`JE>*BtasZO2n3RMXn?1lCC)^P23b*uez_JrtPX8s0BM)lW zUvt^I`~j2v$_T2KZU=9RAOamO*U9!++BJci2yWpBK(5ezvBTWvCD3y)D0kHq+m-dK z#x{oZ3vaMsD0@McRvieYgh;*sNTF!E9>uOPsOTC>uig&^IrZ!4Zpe-Tb^QJOYfLmK zq(y_O?;yT%a?WK%qbyq8xzNgv5mk=Pq`54%)qVdpL}P<>xLPx#xN*%3zQ=EAh?2@@ z7qI+KbLVv7&zNS-EeYEtY|&Htb%G`pI#(nqwPFv$!Zh?bjrVGV; zY!>K2B|oSnC;*T>>?2<+qkYMxUT@4uJC7O`qY(*J=vagwGTtYvGk8<~wd4^zwMy)^ z=D6sMNWIbIq9Nlaa%4+L&_iz7JXAFzAvUe?>A-0z~h7_CvRCWoF)__tq1 zjqKXfoAWGf{|B}Mq)*l7WhziB>Bf(}1;=Gxp7`p;H$E)%J=`#;j$=GYGa^yVE`5og zv6-Vewj9G}TPo-#7kyo;`Awcf?=uGZUV?K5?;NYm&~06DmhZuKuBX?Flm+-~ScTaD zoH0@VYfc)UTEnhaJJ7djTjCjW!SrHXfm#^(1LpUGc&FRw&9+a{61#&fp zrdLLUWI&l95)fI;dx)FLMhJRKgCc%To^g|7g$s8t^KFCKUwQIn%HBg1=VPmN-`@BV z4(`6v-RByx5k{|N9G$>ra8Z^gZ+WF#KU7ipm)c~IG8XN6> z@Np&_@Rn>jlg?%jlY=cOIa9urfOX6l7Jh{7$NmQlq|4V+Q30cU*Tnj#ZF1q82G-w* zzlP0u0hoQ@N(F}tqf;!A?t^>fds77@T}MyIs_b}d=TGN!$^>X-;hLVAGR89KP3McU zY0vF;wyx4(&*Xouz-*=f{}L}&!~gIbQ{gd!2{XUkiM2@-td6yoMG7>}S&KWCl*c%7pz zau=|JOg4YOY{R=M0e>vQ0@jjsoxc!cMQ^HVS6GDkQoh7~>E2NQJC8p4Ie9?$ z8wa)SA!BA*b3hFcinWV-V}fphGW(%d@T(#b2SePBppZ240w%LB&%al0!4T>BN zs;-Cu^XV&~2^JSKP&#^m53UpLXCZ{tP>AXzKa?UW+d;=9Q zX3ZWGg#_GCAi1_eLk*#JwyKu@oV996g37(t8Z?TS>S8+os(g>D-I=Ob(Yyh#&#Fk8 z%~xbBEsE6Y1Nit3po-V$@qT_^)<&-H;Q(m}#T}RGh3$>d3~RsN=*B3La8*T5K^eKr zoI+zca}^Xi#1VP1{7eHT#sZeVON;-EZ2nY?V4@v#NCC^`*AC?fc|V0Psh9MkYVNQa zSa$3Dh+yw9aDL>_u~;ZY5me66cjI0qLc>5J0a%3+TJQK(SX_lkW$gXU$s$5JxUcw5 ze0CMqkhK98Ch;M++);)(tz)9vxQva112x2vXv|d<=TlZ29aj&>GC8J5nCyL+n*TW1 z;=7>MBS{96d!}P|Y@+q+iH@dB!465Ex0&LKxIWy)3cs4LylGUv?=ZX18TaGT*(OIr z-wjrLu^M47yn%#hh_wmi{0s=3gntK>etyJmvz{0O1V2R4SIS&_mrfap>5lMNDWRoA zW{g;PM7L@n3zjBpu5=*O60Ppx8bR53l|o7j^1E3$FOq4elrf-xUdH#Olq4*y=yHR` zd3W4--wL%{fyo+e8#j|-rJ}0Q3u2Tuwcy;d%n$na_;(}?>^%G3sB^)_o47RRT0R?8stjR*??nbY{)dG-XG!krYRjD9RDUL<&#+$SSP5p9A~k9^C;0Y zWqa{_&uzOvAT;W#{UmPQY`KsstJ#Yo9I2%7* zbNYP*{v9;=DyY-PM7fzXOnC|pHvC0I2we#MsG1aBx#U;LK z-w0=ixO*iWA0AFGS18HrA$1hI|X8t2X)8qew$l&S)GMrsl(vxYrL<_XS>Sp zmYMf&oU?zzr+18WVbut81U4bg%!@zzEDoMVNlxgqSy3^K5gCEz(voGT;%Vv?XevVR zb6WUye#`~kA}6kmx@Xa{vB(-|z;jyP{$vgX-B<@<26 zb&jUNvqk|npABf;vmJN|%PH@9q1K$j{JO*T#ZDR^VAxgm-v9f_mRkp0g~VSvmj2{M zLvYdoqaWbXMRI~qCka%Y3w z^!zlXLC!X5{^FQ*nd!^|U7wItBs$kOV(oKBwgBoQ- z%PnX4{L*7^aR+%{CFmQ0GG-tE28XZU2Qp2xKcYPHU~DX=?r!AWJI5$U8qcp$JCfJ* zoJ!Aw>>00Z9=&yP|I30M^bnvACbb=d)a!ZLnyy&j)DJmY?6J#hRdp?diyc7U*sI+u zp}8UW`ot$ImwP$p{CcIV+4u~lyiDfKd=p^w9q1!a1b3-HjUdE@Xf4aIsC_@pZYr*SzXd;d&Ba zcX&^`loYuIw($J>QuoeB34r_>9{x2O5BkDMI!y2-G<>aOA=L5Oo46tMUx4PqP<}Ho z*O0W7pVqMYUu+4IcK2_KlOgU1r@)XwScc7f+k^=E3jo@)bzmGwXvxc&`j06iFql8{ zQ5+s0Ac#CL!qCAB#}^|!!O^cW#CuKy9xLG&4Vld!sf|<8efF}AZg@tZpY7pVJWjUK6qOc3zT~r~Q~bgK_SgT+JTJSbo)+*#@75p`MF+6dzhnfg#V{2H z(@-!IrNr&bfre>4{V9;VjJTLh1@g9T%3{0QJF&}n=E>8%!$W0^3^0F)jJN~K4yyc| z#}DynJ8fTb9>Cgv#k|67*I2}8$e?c4kyqVn|73b<&S5f|Ub2exzV`TRFp0e#Z5{!< z@*uQX>G+8y+GXqBsCcL&`%g!=&h-~R)b%775lS6I>i}p2Ge^I=Cx0qy@?6dxJ-c)^ zzStM>c;78+;mxlgm{D!})hgLWo}MgFL3`D^A;AJW7M5>L1z#PSq+J0M^}4NHU)}M~ z+`V04cVawcJ9kzh&hhkJCD$%6>|llzSmkn}}c8hSo5B zlVk6h0w5b9r*}bYH;LAzyoZvVqw`G@D8ZqXXXNa-^Fo(0DmF@VfIe85xLnGn4U3v3 zxPwe7K>sv@BmoTNH!$!0Ks+Qw`bePNb7qN*?r;Abn5fIYX+C?#Cfb6mTpCt|c}kh2 z6;t8($#;-Q3UYzWBLpTj%-3(K)SnDM1?A%4fw%WlOuDv3M%rW65hb@c zzdHBSC74cG8M|EHCTyK^VWh9iNpl&qVT#wkkBWaWJYOXLvmwg($G3EzOE|>PaZ(*w z&O+ZwnK`zwtJ{=-Txlgq8SyuR?%wzo*w1ex1XB5q%~_NNp|{+u{UhZes%#yI{-k@a z0At}xG|{!-Q0dv??BwyqW@p;^8Mu8jvkTuNV1>mu(sq3}{=Pg_43naD{Q+AA!5~Vu-2eYJR!%M~wUxF!{TR!N!IH zgPP2XAk;A<4fuss>AU0jyp77ZAb@VB9aTNsw02oc>pS($-HU4{pinY~B6VK>(fy=a z|FHK5F`bY3+41pdpw}}t0%vB~Kl)0!hoH&J)(;HD?$kE$M&k|vLlc;i&VNBC6D;UV z7K_4gjwbQ?6IFc%YUa>J>nVkcjaf)R!xRIE%2thJ=-fFF`2w%}^=qrVD3Ve&qvA2= znytu|#_?9jBtNb`c3qc(AVNxuUrz>At%u1;5% zblb$8*l=G3Dz@AjGxsMoOMY<2>+TzT=yQS?{|UpyLtK?G%O(MATAa+8aA9PQF=rPM z)W@LYU;z33Yub(TA<}wAnlqnf_ zfD!X7r&u7apT*HI@0`RCmr&^AZ@sFAMJ;UN;vd9#!EqS^?XS0KYMvFm-NSi>g2epV=Yf68}2!@Xx{VdmKWmJPnp&r}!^sFXO0NK)5l8xR)7nH!y|=XLqH zelLPb#r$yp$!QH(PojSV7SYU7y*8uL%$MkM$Zc*gNe26asK|ny$w2@DjH(_8X!B?h zT#Tx<+2SAFtuGiD7VwDZ=6$}fGOT8elRPEm;nDSC_u>nb5>*)aE&;@G*tGm#?7ewB zl>PTVKDKO?okYeO;f{)^WU?q(Yl*tVx)$ z&2Y_i|IX;{zVG+;eZN1y@8|RV=lh-JF`4bU<~pzQI_G)LdA`m|*d>Xan4#+)^J0}~ zI{rjSH!meLFmbHxT&yg2Wzm}th6491y4^Rf^dKI@H$66-iqznnKWBr}ra4+33s=3I z#>bItvPWK`E^I6B8WJAVQvwz~UBx1Q#8VC4g)q6`)pkM~y(}79r^=%!0p)grJ2y1>%)RRZUPbfJ_PwO`?vvNQk~rR`?GWT%D&~$5 zp(d4m>WPPr!@B^Tfu(!oO~P?ZUAZIhR9HIUJf{~oSopqgsIw>(UxUU!H%Cu6{ZXiW z*ICsuv;VXxfw@{fdF@pCzM%oq#Eq5g1UyzTQF~0XOc~#5aB&NLOD0EPFi80AR^(5e zimf1;d_92I3GD{M*w^pvj9fzJ<9@@DSX$JtF~H((o+1AT0VP1Uzt&o?S!UnKOUcCs?gk#eUIGi=C%&RN6Rd`@5t#5)>mZRRS8nE8 zHf{E#D;e*f^81!5o#2?zm`JpEF?znxg;P)T;q65^ipTM=lE{Su9I7N7S8|a6kli&XiyuzOQg?HL z^{>n(;+7Y4p8?p&w3_OD^Scj{0O@h#!HXFxvGfN*s(Or2#}pU=-vs?!Iv+H1M}hgu zbg>Gy?^8YAlR?rySSCaxVFx|RE^2TcTwB_-_43{wINVkOD$VjlTd_RgAKghaxz4)9 zXw?tzlPtO}q(4#92div`Qv^7W)VB?iEy4G%84pSdAoDHrB@*4QlYewM$)cLix)cK* z{R3zI5mcy~`+q9gaKrEMA1L!Dtbq1ee|!pnN$r;n3wNq+;QPn^dJr%m3%_eq8Nv-K z6oNh~)cX0J%nlEW9r+AWVI|NpwMODR$^th)WiF)>_K6%)G5FfQc#@QeJ{=@}j(DJ> z8tY_TveF_=0c6`O}Wx*9` z$yZzfH{!8fU5Rea1Y#>Q25Oy@%mdpKc_pvqg}mf&R*6h_MenUkFBWiV3V&v|CcUns z%Fm)TJXYZIyLks&e3};8DaD6cNuWp?ftwnFTzI7T!IY=`jKyfJ#fGX`u|V^n@n3Ik zV;71f>%=Zwb-Guiqu;A8WSAdQE+Ml-!xWf;?)nnfHFp1o6MM;1Po8b zUo62F=~=GHm?62{le!4wM!h* zDazPXJMT>HWc7R>(qQalb$$QO{<^OaemVQQrI{1Hj#d3`BGQsrP2&IUU$&rP@nCEl zQ*y~nrNhPdW!tVm*!J(vu38sDHGk{9GVS00!t??|j1OlW!)uD5xdYx3*S~wSc-|_p z&jp5N5AliY{QLsI6P12drvY4f6?fu%*arx^L-h8J< zf8JMGn8r-2fzi^=iDmm-cYu4!9vLQ)Za0iSW4FTVprGPgtS6tPSW4{C6K25)yk(qVDxxmg!g`IJ=^|S@f>RDmjlP@yQ zOE*fpb8I(*5-6mynCw&-YFLX9ItitJFoXRAK&tgWC#eq*IhMJJKs!-Ck^u%3au@n1 zV{h1xInF|-tG}psb@rJjF$qGT-bWq}d10PWqR(Rxs!^V7@+c zU3_0uuvMy@i2d?k`e6=G;UuHLQ~D1tW}VBMLrOQ#bi}PCVU|nibIG>Hg_yIc&Wg#| z$j*etq%9v}gBX(fG}xC&bN-P7{L_VK$#R;Tg*;ecZ^uqdDOjhZpdl;n^D}RZAV|D7 z>8IW@@-;Vf(CYvRQcl?my^BBVTWqnWh zfr1j~U7x4;e2z-1t{Oj~Hg2~z7qk%i7(IN_XY);xd1L8k#dSp6 zR;7)%<+lmHFp3PMZdQ%pQa)rrnJp?qN-F!e5FV~~Oj+x-92;dCh?R>Rt`V(W9;^E5 z$~uMNlP^vRUW##gA7pQ)a4jQS&2YEuS3&xx4Ie8Lsr^oO6g&8B}`q>z1ET(8hvgh`siWVc7DfEuBw~r1FIuX#soANC(ZII+6Fn$X5 zPx=4VasPDJ9WC51N@AdX=T*ES!M}$)`{x@!9sXawbT%VW8%AU>Y>7!xKZP(hzUr^n zgkPzlSg{8|uR>!Ypvu7yO4|UNHWHwd_F~OY*-nu4o8NH1F409UwhI{Eqc%eU_*g+P zc1qj_(ILUr)`i!5EaZ@&BxOaxQoP^J^iE|EP(1=Bvfe8RMj|G?C zIPT|H4!&lkgmNBJTcT{ae@0=){=vt6*;=LyKUNu`o({`Hf5TzRsGiH}w=~z~nvI1l z6Lp=+)AoKUuhmjRsY62c`{Yin+c)(_ApG9B8#m)x>KqnFIIojZbu%j}vAdNL8ZVJV zs-1=*rfOEm9ckXLZhb&9Cm=UPUK!EK|HQ7sv5=>C!uBTrV%ty|)e*OT{8_NT$;@=V z%&1{FH9hWgeapl7WiNeUC!ilaZS0v=u$~i5xxaXhern)ITC=^J3S36}+@67i`2WSy7*L^p$4IcNjVqT*R$%}UXl&0R4HiD;|5`W^DOI+-fhRtva67X3zqK;)mk^u8xvR$&F=j6~+Od zmpB%T`}*67<3Fm4&wE*0voQ#dFwoMk2+&LxjHkx8)NZ>^sXmDTWl|ESlN1)tg}C(l;7hnx1|?OyPZo@qtT~%|Em6X1 zsPN1F4LI(c-&+_sfTNg975IW&+}&qYoy4z$KA*f(U!50g`l7X`N?fHFqA;3w&+N!B z7P$m5|H%yc+qvkDIS|vTA=Nd5h=UXC)>UVrVfdQC@&XO&W=HQbPHJT-=)KhUJzPy& z!k?}9AFJIzRcPTG zoB)k!FI!6c3BZ62oPYjqLQ0<=N1oYe8c74|K^0 z9$0sgnDi?QM1TLTq-AA%uBQFs8Y~|@a{R6o{X|_-P({p;Ke6fdqF}WD6U%c0_sWhE z>Gi1a?H0&~D1U{DbgcK=r>I#Bh)aOk?QZazD=47V`1aWXGA6lYtxG#&TV~MB^KE%{ z&2sWy*Q2F3Fi8#*upBB;Gibku^KXOQzcj&881x}}kK&Wr&RK^udy zygGphj{AI~1hhgT(p%s28?sP3Q#jAEf2KO2!{pbtth`AhQ()TX^)dyjb{7G!eMcjJ z12lMKl909_z`)$|G39JXNYcu06nbT(&hx}IkMTvBRHl6dH48}bF62WXC%KPW#$Q!l zxHPPZZ!^oKMK2pyK*pP~yZ=a<{D(UgsxPVbg>Q_oF-ys7W`Wz^J1(<8k50|p2x>r) zwn8n~hFMv=%F0oa5UwMkRoQUZ{WD-#ahQQDpsWtGe90`nlqGi6;%Fn)skx^O*=2%!s#FUnXD9=ik?ehJ18Gq^)FXsCgVu2{nt zpcSj2G?#fp0!HSxxxOhfo>j3N^DN~IVLJYE)Pz`0>w8MofZ0P2qVqrLkQj+U9!8%6 z-vd&C9NkQlG>khRi(*WRr+0V?1`X_xEDywgt!DR;(3FFy++Eoud``--=>hE_#Cf-=}`Q?I>x{ z_0Y<6yLp2fUaJuLHY!2k$UR+4Ez%UAFazLjAr>t5Ryf%5emDeaKo4O+dkP>3^$;-u z8b+YtPN2Nb@he*e2z4gjCFgl9ktATl!ndLA2x*!4Bpk*H`tuR<}K5_aW63;*`0DS z(&0p3>h;(S>7=c8D^{}UhrOu5v#-6y{CGc*-M;x3(s334cd=1}@uMDv8&;;w+wn>V zVPcB7om^(!m+tp%Dp~2bzt)$Y?v~cFJZab>Jb` zV%|w%ER0=tuzA{y{|BT_UCY7WRDUXX!$2)}>H}XwKGyAGcQ;`4+=jK1bK1)LO@{e5 zD_8E)nK<2K#@S32vo^lhqGai6a!sJp`%f)po`n^`XiQfbtKq6I%eRNcjIZTRq8^mX zpn2mVOjS?X7H{Z~Ci4v1=SP$BGBG0b)tSnDv>l>M2$LNXjHWt(sVT8~6W~ZfHm32F#XFO&hK>^iY=juB=E>8i}QMi{ZSLZ+}}*&C|R$=P-TojIu^B zB+xh&Ic^nvS-V=|?7rM$4gO8$?&#f|PBj-cLKi?r&#yfW*MP62!w=bspq~)rCw%xz z40o*XmGe^&M@;oiyPcyGO^w1Cu4QXIq6W$+3zuzIRS`7ab1rQAz8qD()tRn4^&3jxg=t1PXq%UE zZQFl$ouJYlh1LVdYkqNloe#yajnO9p7c1W4=YWv&ZL;y<d?E zi;|_so^#|=$)D~TY>?QiKoH$6pV^;aSRwajAyT^iyV>qr$J?2NON_SRLH!lx z3LWwB<1yWV?6`*T>U1Z+jz@Q>8_!rI?S0PC)@qiJY3Oy#?cNKB)h1qC7Y(tzf=HZi zlu^=_a@`&W&BHFcymn2uZZtH(Eq{~|yhCi@*8KoaN*V9%?Hl*i)?{e1VJf$Fq06WT zY%&D0MuvlY6##nnv3$G3T%;uSOHIp)sB1yA zC#jB6dQB?0!GPCKpwVoekHmEl{wSt_uKe9%{ZuEdE5)+pNCtglk+_n?^dB)z>Up8? z580MsdjqZ~2Yu(l@0L}hx??Tg{B$*O7xVf|O5?K*@|CJg&E;Wv<0WGCv@bUl6U=~L zCuH&g;YuT&*|^KI!)etaP7#eN?;A!|Hc83TLHD*kGm>=l@64mdsM=2n&`MStH&*HS z*}IDzKFT{$cL#Tv6BjYv8)BnuLC-Wk@ba=%>ep-IW?~y2ml@KJm2BH%ArR_8mt$}u z8xAyjIazbntF`tS=(Oy6eMndYx``hMLVL9gM@1$v>AW=gkL58VOY0_I_u*fdz7OT%rntXh!>>=AQFKlZ zUho8bOCE};yDTEOSw~=a;oex@{fzsU|L(t0^llHL6}owuUyu470Xnp9>|*7tz-&Vi z_OD5)PX%QHm6e+o0lXc(>89aVm>tq5dzpZ~X2Dl)0y;Ob!I~)w=~3p2G7WJm(z^N7 zd)eh7nO9Td*{j&5I$yYSDkWb-?My=AMTG50i~a693-6m|O*ig*-#9_LHyj$d*;4sf zFex;pDC2yAbX2v#3mlGzy>*9)aJEeL-P*oNXoW(R@oID|&sCuxj%_5V7-p=$b9>{W z=S%VI0AZXoy_oaBnu_&~%bcQ-4UQ%^pJTg?5~A%czZ5i&7PzNpqlI%093X4O1ex@0 zz(-i!_7ZCK=X6!lKOJc#$L?FQXStwTb(u;ym_?VhCbTo4RRr2OXuPl#9n;>j!s1@Q zAKwI~DhjIf3oEa0Vq|`JY3tjm%^R^jDmH{5mT+Y4vQwD+;q;UY#rp2GY&W9M0Uesr zGkRObJ}_HEY908yQvbo zatY(-iBA(O1d=OgOl8uAJ+d4=n@jIT^=5lI=+?% zw7CknG$mHqgGc}59sGrW;4iU9eZ$R-;e>%2Dng_qbUkN{zt=@-?w0D1S|O$Bda#1> zj6feJ(cK03hrIwGb)e5RA+Zl(OdI)gE^6H!425l)mHF$+h*J>CI)w7tp^f|f67)t6 zJG9%y1>&gUf5z4(cYKxQ&)B!`IZCYPh)dH2qSq-;w)KTd%ij(mBk1mv0V6UCa3Dv+abwr2CZmVQ#0oO;%A))@r21 z{wnplXnQ;VS$+GnIHjqZ-`hp&Wlkh-Y0d&%6OSSnb_oktdhKgI>s@bl(D4-Kc-2;C zZH@08TEj*kmOz&$RYL?be9mKK7pqMmGIaI^nt?Y6`}PWTwu;yW#nd6ga5K}ab51zY zm6{DVmdiEWGl>1Joof*^x|R7+An2?N;lh#c+GTuRE<3{42;2kKz<%=wl9}uJ)MD}? zX5CcmD*81}%5ZBuec^^*WsKNC-sWh-!kre}6)#5CRrQFVDK+CqO3o=8YwW8kd2;!@ zwn-y&Q%AM9SNB|94}+JOx~RAJg!KL<7od(<0TphEz8tM>w=2QlPUVb57^_z1UXici zpuGN<{S{}EvPhU28em0fNekUjM<<5<{oNMZUm!}Dq{o0^`Uhp0%lB1^d8GlWkcD%b!rC478n< zR>9Yi8(Ap&SSzFmC<(lZiZd@6Ne=|ssyQ#%^O(h#+1<0@!%_X4nd+N*u~TO$gfvV@ zVJc_NYSu@U`(s?hVXN^{h%pK8=yv9EW}((aicQ?qy%v;olkFO#gjCv6(oKi%aP2po zm&n#dwS~TEjWNKpQvtX7q0%=Al|II-vAB@J>02uH;z!w<;~}5D>*hg%L%dYiM&F{d zrwaCD7nA3*SnCYONK+LoD!-}gHF=-I_rjw#vic2dP$SS=pKX>reXa%&21*Qiu+RD4 z4yO|^X9y6eHCV&hegs_!$5C6d_LVkaDn)uzDr3g9nr*Y|yAlcLcTqtF4 z(aBGTY{vEMQ?i9An>0;Q)1NsS`(f){p))9oQ3<>Vosq_bGo$b`)4Cd$JKRTUzC}r)ZSTBpk`s}RF}XmTkr#! zx~HM6A3xsn-8 z%o6bL{avKPSL%D*sJ3#5fozZj|JJgOl(D`wu!AyJRgp=cIv2bQxl$nt8C&_M+NMn| z6?x`LGBAz6$fz>H z6xET1{5ir>R90u*!1y)eil%#18G_E_s6T%4#L`G;9ffE}rmNhMI8($G&Qe8F)_%od z5jAwl+NurPtotL|*30t?vHvQv#$NKq)ZtxWIRA&yA zKG7rlTd&~WL8eZZZcHi`mG2uI_q{XSstO`>mTICdUE+W@dFqd3`k%8QJ2amAO)E5>?#aVXhiS zG~KDLzEYV5YS{L>8p`dKv@rCUfP~2mXPTV<=75J^h+tUmPaVCYzK@+X$pRq$sj0K( znY!_=i?A2p%5|u)vXSXPf5Q`wntXR9b=F9-$CyHy=&bXrBde+?ug)D!i|$pKC@2Z~ zYG0Dg{wj)IW^~@{&D@*sZyy@PV*w;uAB>$UT3Eym#J6GA${N1LEcx z%_p(5UIZ>)gPp%qa5SUjlAW2IFCWGZJ=*Na> zZoCAHN~X!q?WqfDu$*|bnB|k0_*o+OB>FSYcVRPP`w;n>h1~VxelFN?lVb!IY_@WQ zq~sB-8O(Nls#wt{p6B}4)RI?ww?F;#wmINb%=bOl8KTJJlhQgZ0;vDOjbQXvzb(W3 zg+CXVV0&0=0#`|ZiqtSz2D*$?UHQ1}Yt)I85D7_&(Jc$1v+g49Q-IckSQ)K*fU8Ci z;WxcKvzXnoE+8GkNC~ML?AqUn=clmX(;K!`EeI^8iSSff))Oye=%t2fbXb%MMl~r= z1F(@cbShVtk!2feRhOJ|tc~flu71f}Ct(1j{yJO-dH0oBEoNGqZEL^WRlnxZ4!aoM zx*drek6rXnzGb_r(WDCvhEi6Boi~8S6(ypcT&x7n$6R_ZZSg)gs1Ra01)cJIiEj!0 z)=yjnntLgM+{OVg0+77pbJoE7g^d)))WrX3s4Wm{>V{Pr?g zG3jeo8^;=_6r0?t0~Fyr+h&n8H9&$oytn^ z3#_1g7^m(gOce)OLC%tT-uT_hu*EwcONhUMVFO?A$9T9etCmR*IEh>(!rV0&FfH6< z+y+NjIOyHc^@9B+ZZQqu*K?&?nw-04wR)W91znSLP_T2lc@m)n)xkVwa+&B+(1Ot~ z$2+#NIy1~DCGL1DRf#3pqw^yXzA-zf`W;4FTBk)5Ub_^1jJdeWBb)tJyKJ$Fy3eJ> zyT-o5b$a-wY@2M8Rjl0jF>cJfcWsGZ2-4fW5kbWLl zf$nr}e?~J5c>7^^xbwq)Kn`Ww*&_?b%>xvRXfaD#Kvq07EFM?ww|o9b zJ7Zl3r<}aE>7!(!EIrNrr!8F-F#@{Ep`VX@{&+8&2wcpy4?@lgpRC#Wp>hVOs27?) zO!fQ{-5SZruNOuZ_~il%t9Ceg!>Vfj>wUc=w!vT}UZD|w4)RL){t zoSzMpamWJ)W9I+n1pod?5OYppV0uquf$QEt+ALcLju4JEh2+!!a& z$VScw77QgIIrhj!vYDp~4Z-m}RBO3FqSN8KYs-AsZs?G`c< zupR;z(yhd;UUAIsBk`hET}? zRVXs_MsbJ78Zq~?iQ1!E?u6h{uJ0Bm_TAbVmXe8ErP-#N$-JsrXoW6tFg|8Q#=1oc z83EGyD#XyJj$|l++aecwFLhg7s7E`sNFxhn?#D3D(Zhs9=2AybslBFKuO_c_d-@Wl zMkf(ArlR{ufF5S=ViKMgcv(y90dAbq3HE&&bG_m$7>G<2VX9^RL4DnT6D+SNqmv5t z^gROL%*7RXP`#dzcV7pnrWTTTprl*m7~K8WiSR%3;+!enO*72Bzvs(U%p}&YGfFHz zv-|_A&(#3Brcn8XjiGKP)$p1LoXGs-K_!!amRbLKz??cBO2Xz}XBma?^+6=myBXT^ zo>u^7(iDkMM~p?ub08ST0%-~w7(ZsZVRBzyEE=GD6@u_YtQJvQVC^-u`XTiGZ?5?v z6bG2qfos%-yevKvun@23%}3=mlY*PS-;JwQue}`;Ub>Ai+Yf}$Q@uf}(mBak&?bec zLm%YuSHH0^K@aTZVhgHhDx!qMPvldlWTGnyStj&K^*_1Qzmk|e61@txe=1N{S65x9 z)Ot5}_|%aLI(~q~S9{cT3_O$I=jY=FOJ?Bu_qRP#@p}zp8CxLM!1_7A!hJ*zV6zYTJrL@xQ*PKz@;rT0~T1fb1QG` zWh7sInc+S6*gRKM0cp@+aKVzj-8QyKTrcsvp|&)~CRgJ>GBR;4t1g2wt=9?m4uA-KMjVGr^b$zj zdnm{_o|5Qgd4oH#_)$D=(;|6bh$r9KG@fk>;uFtRy-p)FaCyub<+&g3P zJ~c!}o1W)HXR*GlruD7Tkqb@}s2eCNby%t`ksYsu-|)2**lXLATT>hTJ7Q01V*hF95}@jGzj94NmEYCZ3P0j`^HCw(EKL;38yp zda3$$j@gzzYiiaR`mT<@w|*>9QB%5%N9~i@?pG4?&RU}J-3}b=Z61`CSMNtBj?YW5 z4Y=hr%g2@PKI+hLsvR(6yfX6QCcaO&l^Z*0aQ@ znu*k1!?tKruWdB|gEy!AyPT_w&oB$MS|F2glm(dffCO?I&7WLhXVF&Ly17v)rb;~! zsbS{PS@EZB40Nc{9Ze2Te`Di!0omUBlBs6{t6wygyysFi30rRU?&1*=~Q@W#ZsMp-Y#ORsGFZ;Z6H{15ODF4j+vn zV97a-UdXW-g#Pvv^tvl2n$T_EE|3Sfu8^>j)8f7wOoq+8KQet){SqI-SjO?dU+HX* z0TXHK_`yH9wOcBYScSf&o?*amJ&eRJL3LMj6Qf|)8BUhmSl(=DkJ?W2rm!oDm?u~d zt>LJe88Mxxn0lzdi7aIbS=NT#d~P|K&+|%2MHX*fTopTWkk>f)u{ues?Ii6f5qE;b zmKQ=8xWAoNzhK{)C)YmSOMm^$V5o87p@)I_^GV9Y4PB2~@gwKht=PBk-N6x932IyE z_FVt+{b+kUOj5?W%?DPPghj08z}VbC)33)h9;t{}fD2aN$Ik^zwWlWX#|tUo0CT*T z7D~TazcYTy7LV0J@^}dV3w0`9n|K}=MbUs6>KG8oYW`GL{;j9MjkWo#&cvN`0U&Ew zZmi^h>uJ|r8bd%a{)q_*La42P^`b8j!`>pOH0V&*ilYYi?q=q=73Idqjf-5=nI|s9;6=!K;e>EN^vFEnPvk3;`|DU}=#AX6n0oiOoNp zD9^V8{XjiFb*dQPPZ{itivqCyD(4z=JVsze%f}HGG4{Rcs%6J>G%MOzy7>!X6px)c zSBWhD;1Ie2pSgxngs~GF%%v4U{BplVv={-~ssqdp#{<0@PY&n8P#e5*5AomkYX+&%-5p zbobUFl`jY*UwhP8q5KddAPnoZjHgY)wuZ!pB{Hf?WjO7#w&bdJbs(T`8tt&jWsUdK zioJJ71S%CE0K(HQJUi4@teo~L+rJ7gVp%eECE%{3#<+<=c(BWPWyn>05?k9t(g z>w5np9k8#JBF>P<$bDD2QJ(_A1G${C?!#9!&#b|Rt`Z51_Ksg;{lZ#sz1Q*N;*~Of z^Wu{2^J(t`R(=f$yK0rPrC9w?iTw~2_}DClS|KEE6INdO+-M)KF9Vq{J=I)6Gw1QF z{7Y=CE_^_Zf&Z~0>S!!?w};$v$yrF1 zH_s5E2iq@`yLIz);{*x!!JFE1400aly}QSHcC4lal5|un_We@|_1#joObdsa?SkAv zRPRrU_V2W9o9N=}Cf2=hO{v0n2#fMO8jIG`KecCP`kI{+w;Zk*eKn6Y#PRkiB$sm- zgoPv~O9lm*I*eWa>U2cB?M=SCu(+SA!SZEnY(N_R;<+p#8fMoF+4%%}x^hO_i!5^s zunF2Uy56I$Zo!|S+6`hDAIYC#8EN~68kGzdL29Bep1P{c9fogLWC=7vM+W#-$}W~% z@qr@kk`Dvo4V6hWnbRotj~pi+UX=JyQTOvn#j@&ifX%@87VIhUR$?a*F^88O2Ff?z z1;eDJe@PR(5p`XU`?^F)#IE{AWHA&{JQC2eh=^su*3kdkPj+dVZEwf(IgpuoyYNc? z+lTvS2iTu1)AUHA&tSmsy<9l&M#%p2!>ot7Vohcm{=aAceJ=mk<=r(GyhkLw~e)!d|I?N<&A>PB`+sFLcd|~21XXn(WN*4 z=R}-p%hw8!s^$O>FL*{peuxUjhJb27OgkStO^}0+B~%P_BS()N!eg=3EzSp`-e;&T zb*%oUjjI1(G5*cFcj|-F`1t?%d;g;2wT3w7s{K}6@{r>Am!J5X7TN#BqyLY6SIoL+ zece@c10%k()2;ZdLLhk*kRDr5a{mqO^KS(9KXb_{W&1KzdA-Nkyn%`pE6|-_A-?4& zP~owJZ4=gZ9;`paO_{Lf7)Dt6yB`eQO;*rNaf__NR$7se99~8MLXd^6j{M5|_B>4g z$1>zUePu7(HGC@bS{wcEiA z)s0eL7ZZsW|MySZ*9%Haz2R^CI*xw$ProR7gS&#r)hPC@68RqV46XS5pPcAFJ@|j{ zoBtaQrDtg>(`#XTx@MusI);uFmgUjk9RE=MUz6Q`!|(XJLAt(z&9KIh@J-j#YPX=s z252+JLdRW10=AL>ft)dfoxfXtC&j_*P<#yjER& zNk4uQ>DyF4z=u9Im{()mw^zNnQwgHR5=Zs_oiK$z0jDJbptz{&#Kv9;J!?ducyIT5^Q#Qqim;)7doMYUK3Tz(lsrS& z;PRE>nZ>%FbP8uU4GYn7yn4k(0}jO2&`M{~cJ8}xV_uF1IJ`lh{=fUbkiet(+UKo3N_l8-VntVn{DyQW$_`YWoNdOdt@#QFz=djf_K0DOW^%%pW-dm zr?R!D1E8EVMx;t%3P`Od{W{P=$P_Jj$+mRP<{N9-Yi7OjqF~8%G5!WjJ+u|UT`@nq zn5n(})^@E`!;iPFyYy(!8E6nE&=DumeH;6FYXiO=?MgdT;3E*R^6Gm|S<*hcjj|Oc zo%c9CH6Gl|5ttb<&Y^nw7$a}G={o1)y0>1q=7#B=)<>H-fs#z1hN23t#D$g~i)yP8 zOoVHXSN@_a`j#IndLcUq*D`{hc2eaH;+}Z48OM{j{c;KC^BvTu<{|Ick6#PtOhh~V zUuY5M9es=DmSN*}MH(+?3a@mUHeMH4(nSfKd&`T*%9Rxjb5eJy*Wu~xF_LJHtD@BG zkL-9aEQKaRONn2andxdO3C`WH_+@)ne_wQTG5B@%Kt_17z&ivoDWqZN1D|M+LRnfy z+pnkA4DUNb{d=sWUqb}fHbKI~*!M?|)SdWvbk6{Njr9?Z>B&i0c}?FRsTgpIDDz4N zoP>tKckE<5p#SHm^zh}Z-R*`#amn%V$(Kv*jjU>~$2i0t8Puq*okCwjX<3uxQa{rr zan~e-RG1!Zqo|AU9(Bh z-HnHpEPw(D`6|#(w4BJdlk>RT4Z`Lhlz!-)*7xvhudfs2SYAOGf1_*8NZfg=dzWp5 zTiBk9d})`1H<^>Ix;@XBz3092Cj7j_Fsnt8sB6ed9AKYGehJ2wlX1X6jk?T0J2lTp zw*FzLD~KS<{U>RVw|@JQNL%Jng9i~*uHjH@{({hnENXd%*bw74Tp^zJedIo4bbDsF z5Gd{O{Qk*FhX!wj#OUK(gJEXj znY&U#3KpFaX130vK;!OSi#e-_!m2|4>S{Hr&lPk1X{Ixz1j45GICG$Khuej=ekqGR z>%q-Cc{AQIQRWRXIsf~$EaM$f;}I)AhF`tB&*>dzdc}a}JQZoE{Z_4i@@p~a(_6gWglirSGi}@=koz`q%Sgs# zN}9af+4B_G4{kCD-Q_H|>RLcDc?e3{CMZo?nnb|L`;jwf4|J%HelF}+>@6F`qE;*+ zZ9Sp}ETeJ7%~_bT(9f*R?0Nvu({julOdrya8p`+ALaxOrgswb4I^l8pR;SG znA7Hg;&mylG7pah-vX|MWAhP#c!{l`sQXXIjUesaX{kpqPE%QZYrdP=*Hbq!u8!)* zdON5z5_1^2?)Y#GkG25_CnEZh6KIb%lr0NZs@^!feW?U~!51dX?LL?$D#Ar}E>4nM-mEc}9J0>bd=;tn?Q6O9 zp+;GXG`5jxq;jUrG31KNL>1*o;F+loMpr0Q+xdeA3t!`h3xIzKT9ka0w+VdGFBm`dmau^_B(+{&rWe!KS<_mAyj(7>RB7#^ zVixjnZIMFb*O}R)hiIh7yMm^Wv_m3u0rd_c&lG_xL&G%aR!Tkr$hW$|^3j}McQ)b> zQOoW7Zd)_B$#$<8g@s0SAXlpa@2B+DSZKS|&RM&=vqll|&Z}G2gE4LX zTy$}4T1#P;G;=K1yYZd(KyX(f10&Xfg&zC~7Yf+osGKWMn{tH@`fEg=g?)#^&I(A? zk$Z{n$%A^xYqukop0RrE!RPf9+FpdN;#SOC$P<)zb25D{rKRJ>w7nl?*pgduUD86Nswr)0)0Gs z8D`9hvyar;xfct;BVHr_iJxm6$>nWffz!mi?jd ztYyX>rSa4L!GuUSL?`iKA(&tWuohXnnyaDpF$_$q88{#qu^ycrnN~T{B>O=!x63c5 z`yh^tWO>WeqmdUj`K81su}L40&6;i9LuJiXJFMWB!G0yI>3t8TH%J9KjG#SSx=>dj zI1)<f-!dSE^k8>wj=Z9ANj&$Bm{b zp9fuSqB?6Bwd)b?U!Rhw)kQlL8J{67LWp5qy;>;q=`>J!r*gf*CPLVH5f)DwoFM=Q zZW^J_j#J*8L~En9L7C|$;((SekcUVx(&|_t!+jQMhUK7+Oxr*`PJ~E(8FF9D^T(@& z43}%2F2MnW{wsh>R3C9%joa8$QYIQ07>wyT>zvbR+VqayYH6>agXjv3i!givkQb>L z^JJ0JN38_v0x(R7i7-sq)bUUx96M1YF82_r>qY;TR`_q90Zby?X%Ro%200-LJGS{X z4-b8B4+mrehOq5`bw#NN32VoJ3L*1py9k-pXzRyDSYDw z9nDa?LL>HJy_h&2d-NNiw?F??l<90e76mCaO@$EuFIO}^Mgp1Jw;A>Qi;B`;OW_~i z;7$Vz-uVkt3_y&F5jRsr8`AggLr{H<=-#-%D2O@`r6ea(E>Lzr&t;YjCIA=WXGG@#NQKLeTMM^r zaVh7j3c#HZ3uC>szER(kL|T~YSuyRjL3DezFp0HSk@nD3yVg#}SsS;d+s>;uvf$&+ z9?Le?e8o>ZJ{%zBt}AqGEl)}3miJ&n`nTjidM+yFwDz{z6~XNlg?Q|?cNw11KZW&D zPnnn>4v&5cb?>HxP#%M+y6_bSDSP(d=U+dukxkBPc$&EkqP=6&=F2#h`ln5iMJPVc zJa$!Zlu1^<4Yo#fY&5TP9ap9QOpogt~fsdLb zaX!<34gqB6jm6AYg77n7`p_Cg^hd(z6)OEr$sz;^4*Kk!gIn0cSEzhV! z>~LZl_GXFwxzQ1q<95v++RtaM$Z_Dju)OfeBXVOj+|26_8?vJ;h2|Xa%XP2ekWDTO zV`1AOXzvz1E9&AkaKNO+wpQp>c+t0wyaXkY6!T1Oyr`(kN@2wE*R4vg1s2QvJLA{# z1|0~L)=Exa8+)kIVU?U&pFc_AbG^jr-S@MkKTgw2RX2=sv4y++CS#wUZ}cebCNKqgnCYAu^Yq=J!%8LM-)9 zFYb}OQr}8#A6nX4c$uYtu~Qa{!o4G&aqy1nEH6B-7g&L)j%c*1zh!|+0; zPz^w70?i?51bYf2#G6;_T_nluC4+@JQ+9wxN0>%DGMr)kcarq{wmP~b67nZO{CeJ| z%y{PBvn#z8b3}5Wius{u#Uz|4n8bMXz;@2_TRUFgPGqrE*Wkui^Jj-$_2M>#&+<=H&?0>d_(|y`Hb(j3!BWarqU+oq= zaQkEOqqsrN%5w>WT0_Dg&ia;4uGSPv0q_n}@1edG?F&EHp6a|I)D+(;vNfhnC&+l6 zV1SIL^_Ceby?o9S#s;LSMnJ!Uh~w)SOJH(mJtjd4!A`D{9$N|_{*=7bj*1)14=0f$ zgkMYxVig0LyBDt>%J$pcFUPMyhmE*0t_UXG^Uw~rX7xFPAz}?v2#rW!?j5 zJ;%CltnzhgyZ=?h=JvAKjxT3hjwH+YWOdfP`f%0X@gW@6r1?W=h*4Hsn^ntrE4VbLH6J z8|}%lkz0Xs(!wcEMcqo8YQT=;aMC|f9mJu!yq{^$9zH z4j(>)UHWt~tNE>|eo`3d;ly}IAsD^;!_F{0_C`J-2k4J28`S3}I;71q!L5qt^NJ?i zJ$1%Beh5d{ys!5-x^M0Nv}vuiUI`X@@vV}goc%FzB!ghZ7gl<|b{PqVFuRt$3Z^@+ zOVCQn_I=UY$Fn_j%3XJ0qC}vYZBgaQxmUT~sds&Zqm)~an#)mpw<^EnxQh$?u2y=R zlXLL(xSA}lj6r!CKF8LZSD@Samp2F>?DLh+Hhn~A&Z@5nqw?bRx=|tn`j3G2xc#X; zvUKHXSHX=xlt--@pn?l!mtGX#1EJeFy8Mc+eL{5g1LrnMz_sG%2uT!N#hkyR; z<@OIUhIJ?Sb8hLtuI5Cne~E%IA@Sph>l=|fZT5@0TSx!(5&l<4|NHk0NeiJAZb~8n z^R4Rx!WLjM{yK6qtH0iAm6E$j!x7w-JN%Iv^ALISr0m~tkD`^^V3E=_J43UN*Rgepx@VxLBSc!i0rdH%}?G*eKmA&KiHi zt#jUYsOV69vWWM7ykzc8-)Y;Av_pLAm92ZtIVe?HREnTg$ITt>uk=vImB!@>d3byA zFV#FRwdI1%EKL%1Blal~)>*51Jd-AfWxtKnl~U;N(NxZy-16dEsrd#T0;KFl>Jl|W z^u>(!#3v{2i}$H;nD+eG1-Y@S@!aZi8}zLCMLB89d%W)$%69e*a1U-Nej~^gmg<4a zUkvU@i#pDr&4U|!8oB5>Ox3_M{=fFV1FWg++m|XJVrYsA5@~{p#)1Nhgeoc}6ctb; z3L-i(QfvW%(5p%jK}87y3Ia;Apavqn2x7xd3spo&p(K!G-a5gVapr%Wci(&e_rCAG z?;a4$2{~t_M1Qmo-WB#A=YA8U<^r9 zs0tht*w6HT?_|}=k?`2jGDZR)W4qRg;_#xuOuy#65c(}5Wt8tC;1iT36mWs;GA)I- zSc6VJ=JVg^M(bl` zXf*>ZQPp$z*Cp#nXE{gG@mnH3LWAWv)UsH_uieMDoZg|?!cppMx7(Wo=%70xL3#n8 zE8Ib-#OID<8vAU&B$vn8_{qphoTs1Ju&?`s>chd_fme4M#k;PivAZ5KKbpmYkGO%D z^e5T^z8y0OdR{KnhubL3t}B)+c%URDisd}?Rr}AqcK`7=!VcR9MkKVnuIC7FbuXp>w6|ccfYJQy{4g!c?cbEz^@Wyr9vMvkDtcok zU;dGaMf;+#?iKT!sP#cE^IuB2kA^9A8)4-I?AFcGynNM2A~wvfwt=&WC6KfwDp5qq z_npG0_o4}B?&Mg6Kb`QLuOxFgCzi2{+vOpXK1yD0BBW#6le9L-HUVPacVL$JHqm)7 zCePgMxcf-HIW5+3EV3K#_tIJ&NM$;W@#rjw7H4{3NKxL)VEDZxKI44LA zW_N8wC`$0{20YcheaP72N?G$s7Cg?bnNp6XyhC zIFniOI+?<}y$;3~vT&_j6btYp_7o(EpEAIprrYo>FLrF=EF!k`x@sV8N(XsC?~*z5 z9g}M}mc3*$2d?{Whuq^DGBE@5jgnqp4u<1%JBJ9SNGBFDhRs2qSk8cHkfn!5w@~^6 zJG=PmMe68P50(hO$>Tr~jmVWKQk;G^@K5CR=X=DS?Gkg;+iP>5If7NsI}j$mjQ=g+ zQJi2{_{dPm>z8LA@m1DnKV#Oa;sim332bjsDHE2!H=(?H0 z)U@ax0x0~>cO(`FO3lN;7EnAxd7XhWT3)cHp`SRd^iu`^jj`=V`iL%6MLp44F4oWv z%I7Nv2!tnL;N1_pGbFwleN);{cxqO^x=O9jxu)bJ&BJmn>Y0u#EHCUD=Ir#A|LhbD zVygl=(Y!;4a9Z??BWuD$7EoywMY;_fpNcU?5!AHnE#s{LKfUj0_NSNZ$4W(wfJj8^ zCj9ZFXP`yo5{CM_ZDu7e>3w&ig_SM3qB4a{dx@O>xT`GZ5C3$?GqrSpP+xRn+Y~0d zTB)uO0~3zX&e~BmE0Ppo!Nc@@Ys`%Gh}?NbWmi~+`8Nb8VVc9or{ua2zx%%5uUn;` zsuAE1!DUeV1P1fCe9zgg*Z;ct^!MGBe-Z$~7;T(E7G@vl>gPy#)AU}Rp6e9bae~6x zfacvH{*DtcATvo5OFu;>QqSZCAIHjjHdeZJu{(NMd}6K`}k6I6P|ZT)@qssf~<*{0Fo=Zw(x?(OeWv&W%X5TL}1N_O4m zWRarqHkoDv9Otr@V?|97&jm0HJ%d z<=kA~O;ET-Jy%<@VW-pYpcBbon%lX&jZy&1{uZAD^AI7hJu%~`hIsQmi#io0zj6)$ zLex+Pfw>Q~jZP&3L|P)Y_GBO|u`8&bx!qB1DbhUz)j3qticMJH^HTNGlt-mhsd8FBvAFV#O%x|(vd~=ZgVTD? zYZBad=CalzOYiO1Ojg-l?Isw!V9%1d(g`ucOjj*!WdS!D+Pk^&&5lHdR-Rzl%iVL% zS!xd4)LEy8OK-#Yj`R*Z&Qp`Ev}-@o7ZmJ$v%;oCk?X1We zeu+ZKV{wJ@;`4EG{C&cF>gTst%fLu>4KM?V4U(O|39RQub8VgK=vM0EGiB7@sp99l z17WS5MoY7)IS(AL3&6w=bp3@|ag4qe$2n!D?cNW_7R{G~kwws75ajcLF{17$g>8GT z8RFgjf#H2~9^>|n{)EZ%CzGG7W9bCJL<Ax@H zHBXtzEMd+tA5{3V3Nl`b6q2FPBH{g~L5Ol7U4J{fq_`lTGJVM2!9^(ZxI_k}zUrVsf{y(nIdo~;`mkF#x}&x27C%;-Q~xA^-cD>Ec&5pw9?h zAhszamtV4)Mp3Z41rBp8C{KWl!@rC3sWcW$SHJEh?1~3=6QfuLS=0ypZlRf_QUPnZ zV{Psw$$=ti2lBa99T5U&+c-DGgTCN}zg`^Ry`lv=|8yW&=Hyf;>@Sc*>`s8Z>0vhL z`&2JrP`+dN1p~^@nn1>&!>y#>Gh#rwA|UG7Kwa3f7g{|<#!G(Ui|)78HR@mV3RgH8 zSez_bFFjJ*)%9S#w7Epa@{poMMr_ac7YxUt=_d(lY|Ba+r`+IU2im05rgmIFGn&(5 zv7hg_^_Gz~N<2T5L}?cu(CWULr0R5l@j5A7)lU0b?c2l3L$+lyeWTj9{IIezoQ=h@ zZWmhQ3ZzdgljK={rF+wZhD;h~mfVh;sZO9eP>nF<+_yr9GTdcH*Dk1@s_{(PqY`*x zxLO7&v%m2>%ixFk#q_QtMVxORA4j`tvFK%O3u#(8=X-&WZ+ZUv{5_V>b7# z@O)2OpP&~4;$dnHAiGfLYB~n`eT`vgW1z-NIq|oqQmoydym!`5(Qv``;v>~(il3fk zaGXUnN)+P3pm_ah=ImP-)41j=j0%gw5VfF09TB*IWz6!^?4o$jnJmU?u>?sSAWy-f zuR<(KxTO(j*cYy!_`@uhS~R5@rz?`Fl2Mnb%{Cfn@zb}!IR|`N+@0`PV!MjLd9f-n zoJ;EQ&s$oingX_MQ}y~#R39sAF)&n*#kB6t?iGi+9#7)PlYyN_uqS)es+XZPGz_w% z2W~XI*lsw|awAk4t$?09yfsNC`0gRAf`Mt~;g7hGfGZg3$ZEaE(Q;zz=_dBdpEz%p z%WGn$Dc#G`HD28kj`_X_dw4~NEB+i%Y1WBAH1m89@e0wA=R?Mvow@nXBJB^unew3E z7_x|k3!RvyNq30ET?dx+Gr*am&q}q0Q)EgF1?GjMf&b$6G=1Lv%^EW~M#7ooeKXIh zfXs(cA3Cv;FpL)&iZhZ(E$rkVH|GmuvbvqW!h(FoyaEgFD&2MW^u`bNIFXPx(D8T# zr`CN~gA{ohpRWLWNxz%&hrF)HB~3V&lUw4>UhwkTnj|>-cXs~^0YUx8jZ{}naA#@Y z*gmR6v7rV6qX-PS=L*n?;r(BAHc{7c?Upm{ZYJ|;fk2yVo+b =KwuF_^eEe&FfE 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�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

NeB7StDm=j`hbNv49l2KzH&5+zKG`K!1OIR<)2M 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 jrT;Wa{?~GQ6uONw-cqijauIoj~6Lypd>T8G*a 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

m=&!TwuaY0 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 v7pZM_n9~+MUql?mi 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 literal 0 HcmV?d00001 diff --git a/docs/assets/identity-center-3.png b/docs/assets/identity-center-3.png new file mode 100644 index 0000000000000000000000000000000000000000..79414b119d3352d037ce3c3f04e7a0ec811fecc4 GIT binary patch literal 70839 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 literal 0 HcmV?d00001 diff --git a/docs/assets/identity-center-5.png b/docs/assets/identity-center-5.png new file mode 100644 index 0000000000000000000000000000000000000000..f170c8d5069e09b218959d85fdef2b7b2df45f5c GIT binary patch literal 52918 zcmeFZRa}+b*YCXm2}u#@l9H0{Mi7v0kS-CB?k?$;MxT=8+JJjx7@wV>v0y9@AmC41y9nNYo!d8lsJR`g81ur#^j z284&BQc_aBW!tYjnw02rYZdT$EhvZd>YhPtoa}yIS`x-LKhNdcUkS!wzS)_y(dnqG;o`@Ju8EQot8SCab0#J|nWS?zUdKaUiKz5fYq~G~ zv7`96xitzW7iSWmKjW~ovws6`GB6->eS@ffoB_j{=!uKXaOL#syE=*L+qcK38}tz2 z$wJk*1}kI5OTkz>)t`oB_Ya*I*qJ4dOQdNujGQ3*#Vj_}G@iVh0$jy`+FzT zXRJI;5QKi%9!otu(hzSiNxfC{;b|Uu%YA>7mYe(8^XJbYKDI^@;INOO z@o!p6Dyp@7+VPbm_g4)K4SjQ%|6G5Twa?h1e_((>j~^li4CG+0`rVzAB?MA?G>@(6 z{o$-ytKie;&&b|;dwZGAr6fxh%<%ds@khR%aD3-S(}WxSiA*~=_e+0tBC%I4T(!SR z#e20CM0W-xJ_!mwipj{UMJK4|`ClEC8*b1+vaJ4cT5E=t8;MJy=0SWSBWpV5SlHRy zW;y6b%Q8*(fm74dor8Oc*vYt%G=A66NUH@%Fv5=# zouW7VQEn^CnVm=|ceuN|Upkt?65n1 zcb0g0hU2k3yF}YZ9`N3@U>1)c!GI*4j=v8l<KyXcwFwTSGotafPhpvj9E zUFc~Iso4!}IQ=QCu8un=H@9jOG5G4Xt}C${xs zQx~{hGMZ~5U zAbt1Y4}6o$%}Y|DytaXXSE+pUhWB-&FB?%&+#i`ZEf+(>a7$kc3X*h-i@3;NZYM^G@}92Y0unaoH7?I8);ZcBIMQ`6w%+sK^ib&6@(fC7hCygV7vY zit@REgNYq)o8@H#uqKhm$4C%i85xT-`_&j=cwG61|5cSjZ@Zg&aJ%fSFR$HPmFfefQ@sYoN{eoKl!7ncE z)%Nywuny-_eZujhfYbg=1Qxa2%H*C6c#+{*cXh}8_wO3q4%^I76VsTn2RoQ8g^f~W zIgadyaM{x?dib&24lO)9+=k)2X~uG~$(8U9gQTOni0cJ07M!D_W73ZwnPO+3@i-k` z(5WzEHqK{?l8A)GTIuk0{{49k+g(^#cX}Jbxb}1I&v`Qst8ato%FytQl<7)z8zYORODJfzi5@j?5GN*na}6N_%kui(2ssRjTE7wC zJfFiqzSJs@+ug#we&~DWTq-Ip z?Ur${(%FObyguU(FetlGxj!|K5T}cx?As6=65}Pb%#OpJ5(s~`CYUY z@H8t&f}UIyF^2+M+}FQKckYK3xgZ?h)3*;~_dV zHDW38(U-T{{JGB3D^-I_*NbFF5$0S)3PjZXCER$r2cM*wk3<5yXiq-E{4U2Y`V&fj zuZo>v%Z)$69ga*)ybnS@7%9jU=u2P-!)4Mn9N)COK3;+A=t#?cYRgnmRt^9`I+?>9 zR(TvbI6VA!x9r?EkWB)YvTVCf<3+@x^3huSGeUSog#J<+Iwz;2VvPlU9HR~aEveAz z)IsBn_woxRW#w{%HJtOqNzqmH8}lhE;Q-_}+w;BH)n6w4lMCq%7aOA<5a&-$)+DYG zpJj=o(T5ZAN$sDO>Na;5sh8%GY;{Q|WI7!r+mu;n*gU#=AAZ>lBBjgnY~(D^s6+sX z$O|GOa+xy>q~HE0MheXhxnMy*%pXR;*2 zPo}|-4|f?(#mOIJr8_tJ)osp`xCA|=y4KV4R+k?IK)Aq882t06ZNdEtCRh_q%G@zu zrl#h?UwC(+@#=qSldPv#xzAMWbQKDJ{>+lyQ|mb5fx#AL*Y|w%k(#8#DbVa9A67pL zL_lzDK&BgLx{0S($0<@{4Du5lg(aqc(pFm*GoLO=9Vv*0+q`Eb{rmUt$CK$VM1obN zALe>9w*@7{TiT~A`5=%I;yGN?jpg+jSTnO7sOKaS*o<(sH|e#A{-*bEdr@YvPzbx` zzrD9-^!QZ_0--P3ovAWFqf;-5Ga(sA#%8uXRe`jDtn*Zcjf<@_UV7`bTu>}OReoc_9^Nm z+J2&lSHU{E1R(Oi)n>-kX>{mgOLE_mEBVkji9HEE>g<`yRLKI3!xFpu6ni+HAp5Xe zPb7W-RVz~uD6vp1&K~`OKw<(9xo3BU4MfLJG6ByYY{+?w+1snI<2F+c?{LbAii)oC z;M?5$kM#Amx084u%_7Un${M((FA9`f+#p*ll@nSowRGNI{9wLH)G(aR4Y#{L)Ol6n zuL}>UGM^JiBH!8F%@Pay%THB5mb*&lCtt6bSGK)~m z?xA2~Lpj5J`?WMGpILyBQLcciFOeycdN?fh`}dth&*eJ#f2z)NQswn`JLBWyn0PKP zA^O%!kX&g(lyPuALP)&|kc7`CJq-}QG1B3h-RGs8m&)|07e zS!jpZHHs++csbPH<`kdnOdAI~3_(hlP9xtYBn1nE`r z1bdi~X8W^+M#o+(nckI|tnbIphO6!?M^#nqL>T-q!si`s+uz#Z6I7Lz2V*7b&DQ%c zBz01;6FY-io7VN1pK!~WF1^!OB@4JBUECypQO7ApdU`wrSZ$m-$HtF0FoIR{dh9VS zF>mu}G4!MLDWFmsa_WvCjHgbD$=0kceCfxTRq?hj=wwYiY(Y*Mm%7Sg2NmLSz0Gns z-%Ksz{v@b)-N>wShpl$VIP($P@2p4P6Op0$*( zMob>^DJhyr-dhgUXVn|w3GbIyl2R>X2*dO?)NQi(W^L#YBF9X11xD9&OnY$O?cyzOksSaFcy_%vP} zuHCdUDO87wW!7Mmke~~{dfO*Z653?fSCN+FiB-{LSH{43g|M> zEL70r+D9o@3fs7HiPF;2a^)yx>Y3hLP!uUlaCg<8g^Og$H&RGSH2wMVKk+!+nwS^G zBd?CIPHr9Rlxi}wvnlRVPk-9<6i`N<^U?Bxn`eY9b`ykl2dF)6Que{DEAv{cRarCI|wA}TA3_Wk>JP^OBzx$y$^ z134Z?Z2b^nILD*jaEas}iDYzICC8`lqz$X1t%pgs>3!{?XU@ z&q-lA@h#>1{LU&@&^gT5KoP&^p;?L&Eub0{$7D&Xpm^(zeUqVCO$YIrnc;qh7^MH$ z(Iqz9-4n;;8`43N9m753=B5Yxjkf;sog&))?pj;Rg3IkWK5zs;kiunQDd)E5#N)DB z>>i~+M45yYkD2!!kE%bFO^G6Hm0^nXF?Vv}^mu^ve`3;V5!qy26!Z!}F9;rra1KCk z*omhLaQ^dyX?0zY)NbnsNDID9{qnuLB7H3&Kzwy5APk}*^xo$i9&!nr8aOModjGk3 zOi%ex_P|hevTpo8_?^qGn;`IaEX8{j&^VEZ^`I%n{rp*Ao-tjjhwGLS9kc4H@(Mzy zRf9TJss(SpH#;&q>Khq}PN&8mO2sHl#y4fq&g?fBKLKKECj}KP?6R!61r8EW^S6N7C&4GXZa8;NLQ1SD_ za#_vOO>x!2f8$;{o4-+Gv2Nk8o_c?McZ?|?FrHCR zR<>2={R9J1oOZw-91QdP@vk4yQ_h5=gGR>5qAcH7OUu2Lzp?Hwp9DNT)bS9ZN?&kj zL@=6LN!OYZDe?6*G z|H8HGI)6Bg*0`BxVnSuKZ3qVfR6E*my%=cSAu^=*3<6S@%+G_#QnHAzzgWf?O-C{) z?G@!pd-YdquOQ?P6GQkPhVi=+Ylo}vcPd|Q@p<0%HxLu;pULb^mvX77im5i`=d1f3 zp2yMh@ezh4yv2GH%4j{G3jKFQma;_z_ag^4+x?2TtMaU_4FE8yP>6by0)%%r){_##YFCS=ab{n6q! z{S&eHgoMC0ENbA$aLi__$|r8-YOU#?UEIUTy`uA(1eH?eTdVmhj^P<{9%y|5bl38J z=c6ex8Z1IC%Xcm=?P_%Sa!(Y`uAzxa!<6HxOsT0=WTq$r%!^4jp-7@2Vm@c-gTP(giB6>wIsZGS760(;>vtr)z!;3DE) zMpvIkFDoku2jWQz8gGz~k1!+)CMW-apJ$kr!+gr8q=Zq>>pmD96Fg3jSK2y!B49J* z)A$3tg)?RnbL)E>hjDP?Sxoi4_y0DMu3%zrFVDg+G&uPd7o(j^#fxZr@Gmxpbxamb z+fbbokSw-pyuHbx;o#t4|FZ29mdHrKdW!aWAlVP*;Z{Uc8P3^isf)}RBczz{^o)Nu z0GxsC*<7q&#e2f4V(`GO+A~FifV7PcNn&$vIIhr4(c5|oe7M8NQqGSoD=Yi9$BkQS zCM4gzGj8$?Y#PY8S4OjYOqW_1q=46>mQPy^-eL*Skv>>(h9F`Pi?4N~DK@If#?fox z+}^qcdz|68Uy=xS++9I@(1~a11Qv*St3;KrDC%5xSW9$TVQS?BMjD+lRM@#hJv!W< z5n=q^=+A^euwqZ~guKGX#%+N7_!2AD|NH~rrB6Y8IM764;pEB5$Y9#r$xJ8v#4;__ zO07cD1RC2r*JwcfH=3)(1TVR8t{v1Ss1+5z5gTP>`iwfRSw2=e!L6>Y zj@FyY15pF^2V@90@@~SjNW#z7p8~d&jliRBFlMp#SBHaHwD%G{5J&|p8zyPG51;eV%Jr59EQCT- z^q0m%{zz8nKPkX=YmNd;d{7k0+DU9a#HTxgAWOTxf;F+c>Q|s<^_SI^P6?>oktTrR zmut*na2wSbWVm~-0r3$%BmoKxnG7NCC;hb7A>?OtVIh*PGgFoAA+h;gE%Vb;QysUC zMaZbT#o5^rKu|aA>f?a;oZ&LA+y_W^IG~}S0Tq{Ei%Gi!=uebZdW)WE0_FrSV_&Xp zY=q_He1e*t`{f~1nV<$!d~1ycTYi>&Ydh$s_}12vHZ`|8jOOI!g#uOE5F8WMMpt_W zi%6h87~MrK80O0-bE8|85a*O+8%vv{@L>7(q(^C3#YEcM7OCtY{?fgm_DW_Y1`Enf z=pbjB3JKOMuQFRe28S8$wKbP5IE6?k?!fT2UQRFNh7r0s92K5B6crTcFU+@c2^J~h1r@_fdR6nCYH7qeIy#odtl62Mo z*1t8fEw^9&1&AIQ#?e5{_m_CT3~t{wQauu6^Ay0GK$8KQc#*wM{ZUhcqjq2Z$|=23 ztzTYL6pqi?qMOvaq+`(&5qbvV;^akMNMlNS=`lLG>Fn`-EH}h2R4vXrZV35jcUuqx zkPFsHJ}wT*-uCaz1EtwN2!j;nMYYm*&WnEaN1cW`G7uB^oE{>;y#jaa2lkMgo1Nyx zYsF8x#y?dn-*EoYi(oVHv;DIGS8KH(ZfN)ts62i+gFg)SlN4X-RYc(nyaXE4OydN{88G%3*)zf(w+d;ROYz+dDg%EyZYoXoTT!A_#kB`eVdUj?11X_Jy`1e{fm% zdZvVF&2bhld+syWSj_nXU5J8?j^NEke`jw!ym%yDAfNLgG7L62RtEJj4|ka&0m$68 zr>_nd>w|!gB4=PgLqG^D`)MAb+uV>1l%uW$E3hy7OO05dU|Tsj2y(wVf+~=4bh%-L zszn)=Kc?`wGfJ6#npvAcKPy9Dak+P*3YK7IGSQp|2ugVP_+((uf$mKP}i=S zGa$I3fp=hVa3sr>4V91=3Dj;mdC=q+H z+&qv8BO?blkZTGH3WW9aFpT@-d{cEiq53(f+701E|BX{m*mNP1l1trx4&MhT_gVOo5BY~EzW|yoWO*#$b-kvJr}-tRP0u}i7zEPjY$bNKDJ#t_Mk$^B zCK5E(L_spTyrNr1h5tV}C|?}kEgNQh64)1DqZVc5Keal3l4(Hg3X}*CpWB+%#{GZv zQt~WoL^!(lOTGg#Cw_@rb#@zck=`U{*^czMKz{d*_!RwWEK&n_)C9%{(QEHotJGHU zn12pNZ~+oBM<6n$r%qz2c6<2u*0%N*7-$f;+vpPkEAaf}$d-rw)#IZt(0}|vtjv@B zp)>|}_WSogEI=<6L^SXr802g-TxFs_mx-fQCIBjx%i|FW5C~U6kp?mlx2yNX$sAIg z<^QfAB^LCeVPw(JKo=bzbdiqFgczMjpq^Q;78&%cJSMB6KXF+vc7x)RO3vB-z(;Ru z@G+G3f9|`(P&iV+(>93BO11_BI|w_-}==!cq;1+vRp34nuOh4c)(9r^Rnm z0NmvMuF;#DMF({iP5Hr7YcJ5)2Q=(Qe~C$hzGEcltMpZCOQut+u)55*rXA6ia#{aH zzY83c=EYZe|5UwKS}`QOm>zni$EWwG=OI6hbNj@4lW&H{Y3LwHy(Z*0&8WmF)=$&V z-fQP*Tgm@q_vGc_B&t$ap#JqIg_4M{@mgLn5WBj8sE#7T=ekSEU0=m;+^Cg*c+9^` z<=6~rG-#Kw#$rKng0fErW7K;zt4o^2Xp>1yv!^#UAA}b~pxXyJ?md1D36X4f4XxKU z{`}eCpdrG>>Mwfr?u5izo*W*>XIPN*o(&|%oIbxGWEY1E$^($jAR7boG3DFhU*!&e zk;~_{g=}cRFQ8V_-1-_IJyPKqwE8#G@b%$G`E(XUP*`25)@sb}d7NKAC*ZJsZ@SHq zYgJpe-V2u-*ZDM4-T!cm$!G~IVgq_x31VszA7|} z>&!Q~HqML-ckO8vS%=QuU4=k_Czto{)0Q@hJL6PKjC1n~9+x7`6={p)2E9qJd+Zl$ z#nZNQ=u)1h?e3pWt;GILW5pD6r|rE`oHuUB*;~75F)*#&bqle4YJIaQ$}4{4>7%~V zi(04851tNKn0RPJ7EDY$9zUJfO8$SQ9nzA`8G@)LXgF_W6YNHYFhsC_F9}}gb zc)Ahkqbf@2rJ&4nKfK0jvbP-SQomSU(jvjqw=F6=X8mP2&=Da5@ajtk} z=~aYF(**Pl6eV}&7*SnMg=sgAg`5s5%72ClHoJ?ctJi+m!%SrA@?6|99jn6xC{+M9 zy&4%kJqqMoe0%`5cQLegTAE$cQdad)3A+RBaP?Vitvz((c>i82r8@ zI>c;0eT;^s+Zxt;ucn5R@YsR`Osvj>N+pTY>i*MPof=rsCxdRDr8yroH^5DQ zAVBfHBPJE{dX-RQX&D3{Dx>3Xq@xYR^vUe@-QBWU<)$Y}(04$O(9{RdM5m;@eQOUq z2(fl!mX&~eU4tVauM&SB4g>69w8WEo;BiCU!R8=TT$YH%arP}BtNJ}ry;GkE^X~bf zmth0`7c89Z?t~fW|Dl0{9x`ZVWM~L|F}v07Gxwm+cmhsY-}IF82Rnca^bH8_;6>$n zp;C0}KIdoeqwk?~LZN!?`_`6b#iGw>Gk^bP$seULb8-%`Es=^t;?hu%lvZ=p?10-h7(m{E+X(5W`%i85=HWUE|Cwf!=&>n%-DsV@^{{ z^$-zDdHGC}F`=%fpoHfG>usR*wqMh#v0TSy+=WHHK!_{^eA_fWaxVhGx0cfgj z|8<9Ga#xEtqNm#Ko(pw2h?P=6GuXh0ADO2-bOeAYF=P9j4eoEE*d(fa?M=T-VvdL6 zdeiXVT2Mj%4kqo%@-tVT`ODQ_v9YREvB70>i3;!~sRZ}N@ zspg{J|K_%vViGst3`QxafWH@Fwxq}~Z>&?M;`DkbQm?7WN6D#;{9)k&63&;w`f{M}Z z)ua-UqOCBZXZJPwD=*1fb5nm*T;jj*e!y}j>RKr6Z4m#{K+!>A4FSp+!t#N~Bv%qR@z9JwpTp=0BBsz%LnsR*uq@4$klo#qFv? zgk;7h|Dio=@^y``%$6-j_Eisi1j%pfo^2Uteso(6V78oD!eE}}Lyg4{dN&@BxNJ|2 zKsX%GX?cKl@48FXVA%wpxRD9o=nY9L*3zo^TD^^fwmlrmeOmTjpq7oc8$6uTd{?`< zHH+z%fWt>m;-P+#6~{ek1v*|HVrBlcHdj{~&A0Vm74Jw^Rp+S<++yJq%ML`Ixmh>+ ziMQ&$S5e{}W34E=Uq`9Za77#RY78f9JlxU{Ct3YUYbQ-w}$3{sMfZ;gnz=ZDXU zi7n6fN3ny@JzYa?ru&4LKOI8=EV4ROtf1ZM2IJ)9#EnnWAk1)kpgv!Z9ZD*J3jf+| z2m6%=dVeBA_tfAXl@%K;?Q?Ur(l(Z1QNRTKE?~j>79CB-R=hd}2cdt`42z|fH`+@8 z1iHgZAju|in2#$H?|8RFDrtgE{mCO$VFeIOz=xn=?P@cu(bTIRkqeyMJBS8B23?uG)x^Vj%5jJr|yQoA*?@xG1^8P8UKCZ2g2_}uSq{?NsonKM}cc5)x!-ZQ})9D&C4^Mtf zyyXZ$-*wJOIE=e=Z*6{#{ecD`K5v7Lc-&(#-L*}zk~vit5JY$9L^(mZAQSuIRG64kIR5nx5X_& zhp(n>!IQM2vJ&dmfaGe}yBd(hZp-iB;9#?*Mg#sQp#Hl?n&SsL^@<@ZvD2QxYB+%M zhu@&!JooLm2SrY3Ss61RsDeoZ8deUQ-I3fMt$lAk4<|?3Zw;Yvl>)^R1I^J9m0(1E z-)+Od>T}>mf5P&>&Kh@}H<#5?3&lAh(@*s1^G5!SOy(ZVG<$xKM$@JX8Vny2Bk|e; zt#PnR@D~7`Bp33k));9^KNTydtgnBP`5}msEfLu^Nl!LY>CX3Ms!&6}1vDOJ>di5n zj%EX;alfAQoKk@RFAjoecX+D*Ksf649f@Cl4~%xB8{Axt_fE?xNbP3+P6kaYIs;P* z0nog~LW^?AxzrC60Elzjj1adnBn3fQjchDNQ9A>pa9{=MLBLd#QpXjNO@j!L%0DBy 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 VVIxri3unF|h=$(vylb}}{|5%TomBt; literal 0 HcmV?d00001 diff --git a/docs/assets/identity-center-6.png b/docs/assets/identity-center-6.png new file mode 100644 index 0000000000000000000000000000000000000000..01fe6f73f0642cf59958cb967d89c6d2fb0afa78 GIT binary patch literal 56086 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�%3Ks`U+y(_kqqxuNA$7@j0{I0w=LQNN|QXhOFcX+2~l$A1bQZ{Aum z=57nrj;qp8t8v@5R`X~@Y5THk0ANWZf*820MqgG5>(~RrM(yYtZHsKw4N$Gtu z-|X0Fh7It4ih#Iie&KrGcM6ZfCBJs)c$PrfkQ=^OqLLf8ySG(yO6}D0yflIQT@24K zJ>+tGhN||GvX-dFK>1+a2fy&vqmEKZi@{yLh5x3eVA-TxR@I0gcSlc|T$1hnR!-05 zD@Yug$)`$uCuT4Td@Fz~TiUjt&xM|9iaQ~$l@lqUKe-mcHm5xoJr$RWE1FJ5GvsV5 z;Dzb7H}{%@Q7bh&&a+q~b*elRV({#3&}9n4kiO(t8Q`e8_~gyFE4erg>g4oN9=Da6 z5Fi@lRMYE37^vS;;J8gTO}R$ocr&H`27KM}cZUnfP-mAJp5RxgyrrcMe~ojHW&L-8DkvokRQ|2Z-Lc)sy?#@ zvqr7A^8N{tFSFzV6sS#$xI&miH<0$2W{-|jf2yZJfwaxlAWa42G1O(kVNd39i}ZF% z{q-6WW*?-8h4C06F8gE(mG_r#`beRZ69%rMg6pnfp4Wegwj4q)=br}_?_5A*yCj}x z!2S~WB2=CQl-oD(j>tgRj88s$*7{yau>D{YCwtTUg+xQkP+4^US_o}}2Sn6eV%EC5 z?6H3hC3`*fq^QO<8p-}k+C0ItzIPDGK>U+&`*i((khuB(l!^H7M;|n_|F`*||6{Zg zO-;gY+B_#Fx&EW%UK$`AF*h-3vzPS0@=NQgDYtm5iDLKv>-gWwSp6SU_As;VV|X9FR1L)6C}s+|&|j*CLtW49qXAWo%EF7`I#Wv5fU_MuEARjfCw zIjS4P4)Q;znRg_YEi$ch6Mk&I)^wlEgUKG3ZXL*AeG=G9L>e>-*wi6YTy7NM?H|AJ zaf&1!)!Qtjy{up4*tk+kZQq{G1sC=H%O+Zcs>9j_bTGV`etnVDT+(RBnaq5?zL$Bx zKZ`)teTr>{1zi3*4Xc@+cJlt6lPqQra1jDN`aHQ5GRCQ*URm<8+X-5>;444kF#c}E zi9Fq1Ir(zh`F+5C$y? z$DC!RcNQ_8erW&1%4)h0ahbf7h}Yz1`itUJUT6C(WG&C8;bgAZVj*Owb7oj7 zwQxqKE{U*`J+j49LUS`fwUqdWFE^;`yO@pR$7W*EFA^3zHJ{gZWDHOKa)u*up~d4jQG!D0pU{7=6HjXR+Tan&VI`bVSZsx_-Ij|3CU3gj}1Rb zq~Up2Vmj&KLtet&XPxDRRIlx+298~ISP$f^IURfbjVnp0hgy@0z~pYtl@`c z+dE8MA?6})W4&BubR2GbBacG^EFtGJ?`_kUd!$Psz|4?p)Wi?;-^$-AFagBZelb?m zjT@;{aKwf&U)n86KjWK`yf9*KLNyT3rTURNhW14|QBs8Kael?5)wk-+Uq_D~Gw2-G za4;;bivB`7xz$;6;?ln~iuemd`SSU}c0Bu=raP5QENbc!#@AY(ly$BiX4%MyuVT8> zg5~`3k3anr z%w|c#S7jdO%^zQ1Y{0F0vySVpsTNkvuNXbg_Xfup%_Nn><}1X+?UpNj%3CbFcL(N8 z_7UyYxrqS(##ld*>N#+_UZIpbFco$2l)A2KM&+7Be0zI8a^ zbGzzQx)r2*>y6I4wL~61_=S=b7rCH^U+O@ex;o0S0QHyBIKK5@u87`m{P(dp?#hF^Ufsd0q8@v6+-p3Eh>p)H zWa$3I!z$(JkXeKLo#nCCO)Y&B5KvT^iSE%k=%#MRDCl{4?=^LQl_AGkWEf8jq8qEuOsB`o4N_m>3yJoy*}BmM zNqM;|U<#${x-x8TO5|42l+P4-3AysjY12@nr&gvv?hzK@~!A$8IG`Z^)zqG;-?xCNlBBO$;MJyItvH&JJP-CPq(7ik?1q_Biy&k zvoc0O`EbM3A)ezCxwFZbzxSyDG3y8fpmrY#nPLPmVa?>&`+7m=>l!y;1bhRA+dz`I;YzJg7n+y5Q76l3Cwv#)j2Xs)zQ}Az)&9u5UvjoX7`kHh&Ih03 z^=nF`46C-PdSaLn_wY(Sy7@YFF)P5TrW}YGDrB~gP~~+Sb1je*n0K{wsu7RKvrMCSg_EHVRrFCM)rbR z3CeXyH$SgEDzr!kY)saAg>~j^V$KpqVexoEm8VY7q`a3Mv3o-gsPlwEDHm$8FV=7w-RsGF}MS^G&sV<8VLxDxLHl_e>F_&6% z^8hOGL|qkHzZ4<@WY*a;PaDgl&xU?9w5#3)Dy&RQ!$ZyI?;6S`Vl?9SAgyI0PO5$% z2K2PJ8b>p2=`&XdlPzb)lo||G2St~AuT()@8iA*Ghv2DBguI%h+}g6BH4=E8@+sSL z!XVXb)v>9s-9?+;vjX*V@&^BdlF`dnPk%|2lHSfK9-Yu4R)r% z#QxyZRyoh!2aex6zUp!g*wzXU9aM zJ`+z~CLq73p3=J8(U#Y^Ljx0gVT)Lw*3>s=WKYmFr_QPC_{7$BPiWeAY2m0Q_Y;vR zQ5os-Ff2qS&Xtdswn0S=E~Qvldeh&OD}r6jNG(*=+27z3(XwhUz$fae)T1v?u{P7~ z&JiNa%YtxATkN{$Q{c2)uh1mj&2o2_NM}F7G~yH2pPIFuvU}`alU<4|PxF*p`f7{R zv8>xH0Wq-qx?3GrMbzMyx^Xoem}iYJzNcQtrPxXXnVqLKDhrV=fGS|wikB^D*y$qn zv48mKc=;FeyLKQejBw<{Jo}LcpJ5IOAYt3m;1n)+Bk0ie6*|XbbbZBJGg0Hw>D&&> zhKl>GfbfnGmcvIfLkk}n!>L(hj29y*)A&vXW`LO8iDhDGfy88%OTgPn}o4DipO$XW+Q znw{_RiCGc-#WXGTuAKi3I|lJS9$VXrnTMg`5+eU|?H_44i~om3n^N+AYs)2WsR)>I z>AlCvnkbOh=Dt=4MNb_zHhiDJX18GR-(=TsYd2xnbArAbYKX%6&p&??hDH^Ykf}lC z>#4kTgrsM8e5O+jBQ`QZZaOBat2Ta71ZIZOu^CYP2DU2Kbyj;=IWd84H*P6ERZ=LD zY2CSBZKrEvBNhum@_qlS1XkJRrsWj)#&_Q?1gPdB8v?}l-PhmnaXah*wHKhy2|+m` z!fyS8u8UqA)wTJy|514I?_(W>qm<%q2&vAax#KF9Xqo$2`%%9*30gxU}owuc(Qxcn(zby&M%rsR_n!X zzqgJ4FR@=|%eJOG-2edS78VQ7%^+R8VC%|Rz`l)*B70!=T%=+AU}$i)qj(c=aj5-p zT3)`V(srlrd2QY+%FH(nPKfM>y;#d=I-uZ-sxo%05oYQs!`E%CuFxM{N7IEkc&cRa zR}#&orTEd#K4j^9lWIEYyzSpQL&S`6)@(op`8f;i={5NUr!0F@YB^`$v-HfhuIP=( zNzAm$uok@yz-~(Db!;bAu`O`j)jG4bsuB%J984fdh%>? zgn>=h$nY~g>z-*v%UR15FOmRdqc``DKlT>Tb5Z{zAo3QVO=WS}_0dZ;kZpZsH8m=u^%e3%Qq$buVHZ<6yGX>aTXrxXm1E zu7J@^R#4!Pd~zfG!sMd3tKRmg>rMB^0 zrY^`Fg_dC`o1UisS8jg7?+dgR0f>HMpw7A18%TH11Ua^b1;`E)9~vr9Q%@pDcMaC= zd{Tyb6Y~|~#fYQ|13eHxAh3kK+2MjWYp4+SC|CxzkW|H)(#w>hY9!Cl=5*K!z%aFT zMp0~sVjc7Ta<%z8Whtf5yE%n2VISR|`P^;mJwnt^;BvL^VNSca_O8{Z&v4NmU#~g( zx2&=*)&6f0VxLP-qdrQ2pDsZIwpi;<7eCJLCHxiSu`x?Q^E?vMKl_WlycpTa1J4w) zk>tNosG+=4iBX%Ks=W4p3N8z9^9io;UmO4!$&kB6#`B%JzF`zqvyb!=#cVHDbCx$+#5{QxYcD3^;Ui_pR26@`r-@KTyAf5T4k(uvV$pl4ZQCe+_n~l zo1@+?QVRLwPd17^a7IoC%85$e+o_`Xd^`0R`JBV6ch*=dR{0ITlZ(@Q)rx#tFOKT8tdef*rxXL-O(h8+*p(SrxT0aWku;|xwek!qw zQsCX-z?ld1bxn2;@1$w09(Ony5!ad*$Sd=}&pzd>=lzP$0C|y1@W@*pU*c&Jl;o3G zCirbx*_iZ@?H)C3=@Ang(|n{hDUd>(zLICBp{72K_)&4S)j6eta}eYfrxBvDFauA~k%d4lk-4T8{F0Vmor5kn+cz7c|G;fE zE5P4Wt~eGvNe)s-cw9Qd6@Y^vYm1w!s_970Y)^Ge;Gb5ZJwdKw-@6|aG-Y%xU-M~M zjPfA0BXNwUZ<#24$yg$@lZ)H>?oCP5tfzgSKAoZlDZ-Q$_a=&W8t~Mpsc+gS*aO-9 zUSjszu*Dk;1D@7H-!}@YsH$jw)P-MTHqo}n#i)tpOW*w|CHttzREC3CAGb}yt;Goo zskUs;b|m?(m9;}J*IJXpvg(4v2(!^?Rnc`nkOv;;=I7G7jDEG1f}|9%?88H8Zb&tr z9(gBSr8gZY&7cwW9iBRQ@N)5P%r|k)>XyfkgE_WvRpM9(LzPFCtv(LG8y86THuC9LP zE7QjF8_$3}y)(!m;GN?_CTUm{i{*Te9PH1`n_GV(BU|ix;Bdd{KcTQTDR{fa$$?Rw zjG3T>$I~MP$j-?=5x%6w8Zpn`imx~ZMc5f&Dd?~CdG(a8tCZ5p@tl<;r{V_fZm}5Z zJj(?fSAgu4Dx6l=k-N`!HR#BmhaKCZ_6^#Muv&{3O~WCU#`X;$FZf5LQ08RD1^f+Ds=d?Y zs@vjv>e83pu2&&mP^{6>0MSbKk^X?=R`KpU)N}#t+Uy`ZhC>j^MY{t;H4#mKMwdi z1U0`sQr^#8NNTUX-L-}Gt+#s_$}m7XmG_ftJc7UO*Qo&`!u8p=ptJ@Nm{s9&n78>z z38MzUt#N^8qBjES5SX)a7!ddPSK zgU-}c>py%@#Rzl0ja`W3BTK#8g4AzG`aW_=-!liD6d^|!bhNXY`}D1!R9eEX8oLb< zJul#eN$umO-^-W1;MqQLl8mqaoJnpif97xxfI6GkK9<~seRy{Sx zEJ%hm-33w2#`*7D)^bn#eD7#{o9d86!^OI4UpG<7(E`3P^5|}_U7QlU*knOaID7^1 z>XR1YwIMrltFIjuRIm}tnL*=?C-qRPkE4n0*x!OwoHd>rYkZ-F&Z>@?kf$^Wl|dyf z)U+}1bBS=gYu6zY8r_$LKta9 zcEVj{1m6!UuWgK37arZd{M}kfAL^an!A|;~iR)Sc_I|%VBT+8*20H?;rN@%~*D+UZ zZwIMEeThocKdAL!bJwKEPQ>g{S%oAXX}t?*F%>>IX(5uzw_8A=E z@aZt{*@C`DSVbB0yIQWZt$<3HA>X+k(@1$Gvk4}q`4LTn)Bt6r2v03lrYV% ztb5dxN~S4QA-lMZxr$>%u{yg#0*G-S<}Lp@o!Nf>786%b&?k=DaqUw)+l7$?y32|% z+4I`cH0DLq45(0d!IFQSW90nfM&!1kxlM7A9GDVOlfJ^5VyoXd5*LW9_Xc6UcxKF+ zNEpA`eU$U=_!4s9?{8AhzbWJJ{^xSStVejc(ZnqaSZJ!kvcL2+9=+Dc(!u?e9t&KO z(0k8wVqQsS;e&?`H72ucL-&b+okFVXKy{GM_dXvFl$#Tv-T`;A zvFo!=OgAs^0Ex8m5C|SFZV1twT4F)wka7mj<8?AA+FY$=BXiYAV02ae)6^*$asxsw z0>wmiZ85OsybiKva=DYbrq#n6k0E*mZ|0c8onkj$j>d0Wa(l8D{u)CoQ}wUN70xDb zB8rMiDt#+%x>L)s`x;3@@ftPCozAD?`KN5R1)L1iYS1zJoWf*llr^+Hu+hj3k`l@b zOo%3l?*NqoGw<*d3`t8|S_CoY^akoLYR zD^P;o;f8jBeHTSmoO(csRwYH<3)R>Yx*Zje5H2`T0PfuZmcK? z`zVvjQ&q&FVB)ebAsn?ulD-T@?HZOOzF=5fh%!Cwb zJypA-rfSL&+IcN|3wvVToj9=?G;^`0G1DX?kT8<%wa{qmHdJ7{fm~riroNjkJ-!xb zFuL1ZN@@H^&IBVF9vzAje3V%i!IlYXW86W(Uk!;dFz6d{i>{LOmMJbja__XRo&9IG zE^MNm_-zI6_JsNgX=B;slG^*yV1GgD5`i2HZThs_H@BM?>Df}!<7hCWFt zu(1ADw#~XeFA}$l2tJQU1`Ia!otLCSYEzNzG)VR&^9aAfz}!@?5cl%vD}!FQihC{> z=Xsqq;jiC#98FrGP=jC3C_j9Dil=tBdPD97*K^))GMhV(Q<0C>TuwVu-pHMmzU{j8 z!Y9L=T&TRVUvkrLEt`|I07+wTAuB!nIoe^ z5?&`p6Ry2k^{&Sbo|ElwIYNVfJP}7YpJL8pKMgCtT4QyPSyjEUkyrjbU;Eu*tTKEA z@%HaP@vQCh@simK3Q*B&`9iI1zD)&z?VT-U)u!U+MDFBh#-gX@=(B_*9t;8(nx1<# z*|QnaR>A6x#Yq+KGG{fCUy7FL>C<{gQ&6jlr&1Hg+Fvs1cnTZN6x3g3PyJ*t3{S?k z6Sj6E8$kw@r+ua;6M| zV0a)88u;^yO(wDK-A449e5>HVp2GN2MwIl83H8yt{GCGHtKIOTP1E=R0cDPrAA|l^ zSE0{o98{9Wn#0M8Bpsb)?I^k-rmMs~Tjt+zS1LgC@_rE=04mYyoR_UD{2dW9QidZ!$rKI~YH`ZTB2L~7Q zXIJjUX7fe8$vdfanD#3d$w1*2))(uP=aq|{a;+!ChG3CnkDZ3-#k2D6SSXvk_5BvN z>Ul}20)v3|wsKC|NIMp?!%j{*e=NNk!|b?bKh03%#>Ys94CA3xl|7zx94i}M>Q93C z=fv$D~nojNeq`yoiaHi&^-#aNN5;Hd3fnlst?d8^{n+m|AEx5SF)^OeO+N;j2Y zx;T!}k7!s>i8#Ri=Cz_Z01A)u>#M{ida7+!_LAI=mXKH{WE*yHN=(%-}yMY6x1)lcp0_!h^xEqf*^Us@sK>-qO8I4i3&K6%HV9VFkBaMb&@x zX0sz{kMXyj|NK^m7AqH=8a9ycj^-s|s;5OADj69agVav^_2bM33@qamlrY1Fa7RT) zxK78qG|k`?zc-EI392XobI%YBXIa~?v-pzW!LK#9sM+d1ooTW+W*Bz(KK#@{+e&Bi zz~=*)(1tbl_?aL^1=&)=J1Gfp_KalF)wn%|7B6Rd=zt_|?e%NyzOI6|mMuDNd&90a zyrDr>Hw)6xP7RdfI#yCiz@k!jX>1)gZuGLFYZ_pacdoE7bo4iA6DA5E`1I&uS`5k%)0zcZqZ^6NcyTsco2fA z#-jxm6cJTTSvLW0Pw|8BT2T~}$eJuO@LPitA!BvSdjVRWCA&=KT+s$XQo?snh1U1m zN-4UJ0w9rAfgXg)q~H4VZs4W*qZB}2_vbvlP2H^@jUi|;5*^kz!ceuhG0!oMHSJ`K zRy#dzUp*x!*BK^52eC-bu=Q00#jSrRyAHxS)x%^if!>r|SMDWT{`lLb>M3b~W#Oa; zN7|I(-Q}>!qVA^oa23g=gXXaDa}G3H%FsNl<$3=pB5ni4+9OS#lHEbvT7K>B(r`s~ zBc-u-3u=xJTS!9sdx83@R@khy5P}}_`@xey!a||D0@N0^p|JhIRrv+t;ZYwiCa5{* z)VhkC0)|H#PgFGXVgyz1KG&yUq^Ok&I$RSpMfQ*Hx-ssqgCGhF!=xeT&U9?=j${gZGRmleWDsOd1ix z5O)0az+w`1m|P{p3r!a5@iQ_CRLgykGvzQdM&@0B(svrxY=yC#rVH~F4_O7$G?s=bbRF6#aw1pje6aCqp?=|Mj zY%o6V8@g$RBm+0|z6?~1IWLfIo_QGKacD#)Mtj3i zeU(#ECrIb}8C`X0RheI}u@0vulknM^jvH2lIS&)mB)<|byIzLNq3wGs!oA8t2YZ8j zr`xmmXKwRdm2*Yn&Z!a7^ps8v{7oYQ0$%EowxX#_BculgV6Z5sU)^(2L037FS0&FK z@lqt}?pnhS_S7J=Cd731gxR;0%qo<4_==`5M5g3q(m4^V5OQ@h{sA{Bd6`u`X@4SE zB9(V5^dKX7j5RGL(FPu&_Inz>#7|b5dv2#IKuCz83AG3ckGS(FNo-K@L=l3ysUiH@ z2(i(j;HUO#H0}?KsQr_kqh^FnH=ekD{;_nS6KRomr`Nx4*&}N`jy|N6OxsKQuB$k1 zF|h_)Y+tkG_8V-P*z`A!7qUMdDGmwn3zy|@mC*5mWL+V8VpMmh$n z*=x>ZNcIM1)kx)GA~=5_bWhSf|cr#vYRGAN<aP0Yc%ztaM+%96WGNTNUTtnE;Xr&69T7 z_LWEoFd^+w1~j=MPxC=8Lfa_;Y_;(B(etRfWZw|GsIZKkMW)|@lE1!B zST?XdsFU$b z>?nH#X8+un}w6I9bew+yp0yOR)!+KT*Z-?JSrpQ``4mvt9dM9H2Vm771{#xU6=9P2wg+l z(zzzVxshSnR!GIKo0Y%*x&!Q7vfZce6D$M7(k~~+#tl>+USCvj;B<*Dl%-;@gar;J zwS2D5aaVe2?73kE?|&)i%~T&N;kJ@_=NnyDZ16c%5FsPbO3yM}n67j0QvUApGtsQbJNL_O>$fA2fi=Au_ z3fc8($UCq`GwscKkgLsBq#L)$TdK=p&%Zhq_G$R0)&jmz0~%4jKV2b#HU3%T-)k%i znvQcI3Mf?ba{^`8)lCsK;oe8n^pbjZ$>&Zs>@`(sORQ8&y?wIuehD)se3l&%VIbfz za!jeMW;^-`**|?r$%0%IO5jrnaib_RAKS#RkKn~Bw^~+Uc-TY34ky$#&XO{k^7-;W z_V=}OS_`f~OW8i3OM}T)qC>^o+2LTS?DkOutw^?&I`*XUPjpCudnc%j(aZuP8)w8k zBQxqbZ3#1)jp*!)&d#@cwDDaDNJYq;4wq23I=LV#AN7xlZ9mzn)83z>dPNI*E78{> zv39kR3RYiXZ6|4JSroQr}r=JiJ=*{id*_%g=Fr zJNOXsb%%2sJ^+M%wip~$ny@68RKoBw4C>kVy`Yu^h5!8%K#t+V3kU@#1KMcfs^IqG zF(HS>3KJ?Pe#4>0%J|*v-tTqz*HPVEy;D!`m#p@WDSEX>m)&*VmC^W>(PuwdFRN4U zHJ#A)zIpP?w-o6}c)Dp8xp_R2=vpv+2L0g>iaMG|FBcoe(|{Yu$NQTe+xZQbchyH*Tn>>s z)3$hXXSt-6s8S z?&6Vv;o9m>kOiu%{_U}F<8ahdKT7$YUsT(Z`zR%$wLMkIP-7Hgc4eao`=hiU9n`7B@o6TLaRo?;Eh`)=_Uy3!){bekH0!tVA? zcir;wTq~MPL7GxBe$xcP&-9l}eN0oz8~2u~Fhy?QblPjLNg*PaTKAT9S5%riFW#eR z>Qn|0?zQpdTTy+*s7Ilj;NtU*OSaVn_2a-nwEmCP*%=arD9#?A>mBseu8pU(f1wI) z;L((yXabBJ@m3=AX}bgG_zr*wRQ*WLT9i|vqGft$$GXPQAO-Fcr%pPCBJU-w>YE#E zILOffE3Li^b%tduCy&6^4WwYFxFj_U;{zo7oT|&4;6jv%(J)r&R(m$~iWRi8i`kQs zP{<|bTTEspip#4YDkvsq)RoweyAhxdCMju*Y0{BYDuGGm5w?wT?HNM0^}exzX)JsciTbhV$9gndlDy%L?yx$mp2$5sr^n6Vudtiv zdSqS96=;%sL3$c#ERmydYCSM_NACg>V9ycHOIs9^fCR5y7Ut?B$ck!|otY6~hV2f= z{EXkbGYxo$=Ra!bmEsP2t(X2FIqmVN=Q@?~XuKwd=WFaFx0RV`Q;{U`+JDc=X&~tF z-(;~9H*R7t^6cu#)E3SA!gk+T7&;$xz=+V_o7nV+Y0@zfCB%mVMSCP1ngOjiYl00)m0&o&=D zBTX%G^8SW${mjrD2yR^&;;utQgz(on<@6FZXN%N zM(9eA#Xm$nt-`vvKAQGY)y7ssGUW5@f=Q#8Z?}_cw3w~8UoBFsjs?i4ql|g$Er0sd zM5zhv7TG>=+d&JtrfQL7fBR-aLio8np7bM@p!>IP7cy0kTqk3D%z*Zv}H1eUY}@h>s!jw<8SA6fH1ubW6q72HYmIpoZ;{MaWP8|j0iDT zn=)d?vSVbb>2mD!G5QyAw*rF>)zNA6}yK1#wxP4t^EK&fIE+Iw4=X$Z0WAyhxUhlu<9)QEcA;{ zkk%EP8h3S82ey6U?V0)B%Dw@)Q`cW~yr`W{IbEp?4yaWLq76+!04$QhkA5y->U_?2 zU(~M7xF-5mXs(V+EU=8n^4gDG8wovNx6RM`W{PDQXobyV(Zs^nPiTVp!kTyn1d^!Z z;p@i7mdNELmSGiyetK>7(Qq;yl%{ylKLXgBi-J5=b=zOkqmwmZAp0~?6<&It5uDT~ z*WxW<)~2MgJ`d1(TKa37c=hG$*&pwVJVxXAQI9z|NLM?p zkGFjPX=3owRp=K_{lzB|;BSxu0?^hs0J``=rywajfq#11H>;=CsfIJdpdvvcv7k-) zEqN|oOrH@}R0vP#bbM@r4BB}=5)gEUSa#@RVQ$yUIgY`($kK$RKxj@T3aa)=DTi$+%Z=mgsDbnyI64X`WLcL1F1llZt$+=sV$YO?dBn= zag;&kIUWx^6-8@hkq*zfmP&Bu&63dYGc>Wz%rTQRi~@|(N}CXG00>)Om>j75@IdFV z)!PO#-|PEEe9##nfNrJ3ZwMO}X7USDYO{6YgckmPr2;&bME-H!ORC&$&hcZIo=G9? zfDYJ42a@sXosU;A+J0|gj0sN2)i!jisLz+<+mN$C#{+5w#HZD0=^0XGyl-{45TUUl z!*&A1_oVW~9V=a166z>zcqceC?br)`UJ6r^#j#A{_}wnhR==c95x^l{Rt| z?Ty9Do{0%njW%_!RkO5$w(cKD8x>x6z-pD+x83Yu)=+4zF1eLZxkO8Ja zW=gpV%@Y0LW;G;u%;o)}Z%I~8hUEdzH)-LFdbf3i@-A4(@nyb+MZPrBrILL%A?PWp z^iwp^P1}kUebvKk!$N=;2?F{m z%04zQ1)@r}$Nn+PrscptMZ&}bbdzLToCU9ay<-!(3I26eSemc?pCkPHIFsn;q5~YM-43}>&uQc4LD^~bY$p-sxvzXMI%moDV zVTq@~veyMlnPQ|)0t|M>8*a??jhzt@lsN1s5aFyf;bRA^ATM~G%i1*WNy?;Xk!86u zs9l#NbA{0+U*9yf zAhoKxEB^||tab3YR=0(I#J8!`GYgO##=M&45&dg5aCu4ESQc(ZSjsY z1>ziD>Ud@kJx+!)Jx{~Gxb2_M?yfxhW_Zj)!AIoUkjlqIlkVum%OWYQ2KD#7+rcHz z_}D)_G4j1GO04vBI(D^wT~V>UW-BK2YDX&EW{8cT!Aeiv^CXcw*N)z+j)qQs)L$aO z*|3{{I_;Ho0}dXXVA%#vsd$(V&%C3VU{Fjur3D&rq$|=Nx8WGm&?()n27~DHV$iSK0NJ(8*UD%GCZcw;4l@tSozq zRuv+8sPQc^>wZ(G_!O&7IzgJs9N-Q^m0!J2F*)5p} zd){^V#WV~`L5X-(Me_cFo3sZml8>%LL>TKPa0|X^@lFmVd~G6|mp!WxlqT3$kf!!# z(1$#FcV~yAF@}wi@_omYLw1d?Q~FM`lmBlef~^a{Gt_n-63tm_RZS};V&h{e2H=?S z|6nS`Ng7YTlXN2&l3TaltbDE?B#aFR%3;Cj&MCAN$Di4$v8ce*xtqAHAXDe@iTnZK zXK(3yt`8&&J+U>WMx@vM_yb!c3D(ykU)uQrh)9(Q|Sa6uorov<>vR^sT*MQQ*+9roB z7xZMyt%8e*X4}Hb9Yp~{jZ>rzQo?IcDcT-3MUfZNdA_jOV@I;8PFm~K5dh#3M?;C> zyjcl;-LI@E%jhKM)_KPcQ-b@uM#&Xbs8yn0l4-X(R++mT(X*GAy*H{Deyg&`0KZ|$ zVUo^PG^aqDcw+}qmQ0+EcpJBo0|w7ytEwadCvwKnxeBKFnUO$A9u7}Gf1Ozao<{Uq zW$FOKIMQcVGIt!berx>N^I;AVch%f%RN}M)lC3dsZGiPNPH^|KTP0EbE+n{*(!65H zM7{aL!dW|gkAs4~oE^m1@SOnC%R|wgY(95gkc!P>RF|?vQMoLzgwAV>D-+IWxi#Jl zV7{>Qrp{sh)cFaJ@QWgHUifF8g$mko(H!s_luH`w9lULECSNO zjalfewm-s3PQ&iI{OS%K7etQ-uQW$Oo)2CSQP1;6oANrA`tZQBl_tc9O z(HXq4v)Kfl5yDK~7_8WNwuV!vbyu1Qz0uER>i6V8x!U3m_Z`4oCG+f^eoy|PJv52Un0%y+x7|VpIF-TWxRuX!d zQFOI=v>^sP{HyLM2GvF@xq&9rx!$SqH1pxtr*kIH`4kb#CFx8RJy(b6gRqvNu{2ZO zgU#wRIJy<#!qoi8|EtVzHH3itcj)|^%4&)K={ookMYn%Mp?S)aj@=~B3)U+H16;&Y>e5i%(0~Sxmw`b{u7O%~o)sb28O8(TMJs3B`%wA8B_FL? z(8kXMPIIGno`>G4DE*Anfn?na@d8Q<@5L@-Z>TexrJTTpE<}D{?WO{q@-7`!F%>Vz z?C6mw;#}Ufda>wG?gCcyn77pSyH@3wjr#v&ai8qXFc(QmKCe|6h5hd)uY%q^-jgqA zoSMMvUQ_$**QD^k&!|&m(t&7Oi_b|lzb>g@p5?TwZPMW5(z|LdR;$p(kS2{TMZDRO z`yh(Yy{N7_wtwLYU!sqj%b%ZW^887MVKM`}2!0JS@cP)(!~0*7Q~ydr`d?XR^#50` zP2H_L;Xdb|9#hOihXt&+x&DmKUki~KDc$%fTCDTCo1ICa_9BxJgnBg7c+VITIV^4> zqXKH2(Uz;M4vfRiRcZeYJ604qCEUG1StTXtjzg+poKp|7|cS>%qI8Q=qx z+LF1-Etzce0pu zKSbpf^7Vkjrpfc+S%PzQP}aPI*gkc~h^6;crIJWf`nWwAD3sTOb=dRf0?A9)r2=%h z6Qz^4I{H#lM|#+%I#Ql&a#c%VQ@T2+=`~=MFu1fYG=h<)^ju{3?^Gn3QHDp`EZ$p{ zm0E(JkQC%?0ZAELNLRpk;$e+hbWLt%?r;dt$?SFKb0jPSyJ8(N+51uLnBW6snVVzC z<_1-VW3WX06}%mdypA8W+E10ZQT0ts%ZvbBQZVoF5*Ez8Do~D>{rut;IlEHc1BWR# z*63+|okG>0@=AZ1g==zzGu^Y&Rrsx9ja|4Kl~oF`nEM8t?`lT?Zbbt$0IC^5*lh5v zl=9tH^$)8xU)_p=@Z94+hDGPpj8_W^;?y$ei$n}%+6s>MOFOG8OeQ6FL&d4@Ef72N zL1%*-T-XYtU&{?*hgq0(#LQO?aEau>XrGVl)6QESj%(mH_G%?W){|mGX40Z6>y`+e z9L=KyY*M__4<`D3Z?G1_q*bbPe*)>4%f)NTZ#z7NEV{~WZM-fq;=Pd0A9 zJD_FXE!aIf<1FPXSgmph$&u1R_FD)q-jZ98$HpX`zjlGsWc=+DGB)&FZ;bFv@cE?H%UJ$;9Ic<$KeqHuW|B1B zbJ3kQ_UYAPsdXjEE$gEA1ys~jSRGd!jD5$&a7fX=sJqGR8 zk)>lMVw>obfaX|#;@l|`uqZ&R5W2~vX7qorl^T!FDD5}%#$sx!a7cp!5pD3Ic`M(~ z%d^Dk=d6EB2$uvi0E}-_Oq@pgWHf$&SIVAzS{kya==iltE4*h&wZI$-Pechs1~uaHZKAIk>-_(NXHV0?xQ!D# zvLsdX%govuH5^Df{6wx)%QZeYIM#>-m$k+O{Bv%80w9j0pUC+ar@4Tuno>8f6e8Gd zKH=j+B*(P#%+q&zgC%faR_32I*QK|VL0;a3D$0~f+l4y(iM|=P1=RC6f@0H;zeU`_!aE<2`c zYdx{o{rH?=5HSnbuLDsFNAXDe<|@lY$S?y9o3ZDgZdbcAa1`h|3vOs;jBK`?v(>1_ z2?lx?D|Mc4b+y6B>kHWyxTyXzQin3+z+=eH#BtOJE-HIx&ec=)7=j0KFsI;j~Tx1>Z>TD%Yo zpPTO{=b~h)Gl@DLS<#jDr)>;rZ>zlZu7fGh+32~GyVPK!oYn?2Ej0>h;&Fl4CrY#B_e)m@=-pvqDqF)58 zw|uM?Z$0GixDGL_MJ#E0NK>(j7NmX-+2oJq=q$rGf-Gm;t&;{3#nX`ssCd;F0#44h zMj8IEL0(l04$$xwdJ7%dhy1ck#53X-PIn7H1p}NGH=(oLn%q^6QiuE4Ka2T+A@;W} zm&;^aIbbDpsLQVjnCpJyvL73Llgj-NGw_J3)MN~41{>J2L$79xaF(-u($lRm^_mqh)mKBA}9 zx9&&&GM32FIqz+xP+t3;-ckmVfyKgL8CROKJ^imoOo<`!3QUpQO4qix?t*`I^=j$g z+;NsDMz6AO@H~|MjW@>sALES$LJq_AYHY?@Zq3n3YOV$bY^U4fBQXCo_0{ik(DYEmdzJXbw& zfQ#bcIRdeB2<-;wdK{A&G+1rk{%v1SJMmc8VWHy*xJUYqLjM4TnWG9 zE}$G4UiePY)5WyWJ__?pKkZuia2Sz_HiaDtfoE-KsfTMf4w=RhEvA{8AJ(u@=CN4E zFQ6O|Y?v9CnOLl@G`v-niLLG$9DkTS?Xr!a<#RcUu8e^e4JC_?cEGg^1rWr; zfcuEzP{H~F;{~4*HY+-!YwXbXdlcksog34#<(hwxi(0i=-@%UshrygS+QN69l=xr{ zq2f)zff(vQ)($vsn|EuGN-L+})Q?s%MH-r!Z11%QWLgNUhwP8PvHJQR? zaI=GMxL@g#;8l-D6fgsINzTT@<_*=s*_7UzHdSYz%^a=na&mYuLro5(-bS15X~GyG zO?#uM*;nOHz4a=@ z%b)-;8ts`;R=4eHrxCzoDXe6yiK)&;4V7wWnzP%HnckW`JA|A~jgazfSK3Qe85l^4v8&Q>Rix#5*+}i3R z?Le)bbU@+k<1PmU`%UIF_t7o11%bEr!~3l?ooO>HV8S(ChmO=b8A1t+N0Ghqt=@*8 z6GJlOijCM#3eyuzN0%`UR-Kz_uGYQfIv#ZQLU&QeIBd3E1;UvpvFV<8>85gApN~iT z((@8uMvvmiSP5+Kx;eOC8q-i1W?~F-v^0!bju7apz&Bs$a$EBe6WX26JP38s_}r8X zHJjxIR%~Wk({$KS_K>FBnUCMd_7plRM{h{Wdin6<0zdV3G2c2sz4n_VMnMh7&@*xO z7>;+y47^w`gA7T^4@%0*hH9^77PXNb+7!&Gi@aOU#y{6~UDF2U=_jShc%WWZHaL5( z;!+Kc;jb+v6+9*AD_45MEo>n;uCaPBLHia{61$YDm%Sgmbp9=QKO{oNkQZ$~<5MwA zA+6G>@lqhFWw9bSSxw$LaU=+EJD|d;A*$RkQa8Kn3P_3l@`H;+?@*+(lZ(xDkfS_?`)}4}t<6)yAfEvSF*NHX<5OlM*z(thkT71TC#Y16wS*w{MYEv)n zOyw5RMZmEnOQ+`4KwIA+(aMp_UZQGK)f4Svq-QfDEl6lN^rtf)yFObqy@IV^b zn0KA*s_I%I`(6hJhs4bS!R4&M{2Q?J{HugBT0_(q#iC7<8ye0GOk0+F(bUf2+ zhB*Z#fEk-?;vyM#YAz0QW%{M1uC9X~!s}`Qp?wLvnDv@RSobp9B~;nK9E8>~q(k_m z;NB~l+@$Du8-Z5g@gsyhZcV(2M1CUN+9HEDdi<>x!#X-)??o1>d5I^Ipc z)J%wfa4?tmSHncR)>KSPh3kniXyPi3TSX2^4@E#NtJ<^hCHt_!Ya#k4vi3j;Ey2}I zyX#NWP#(yY-3|Kx~?ZQ4J@ z8z$2?=&f=_OEK%QIc5R3C1%+XE=VI>r|r;OFR)da9H9Us*eJlT$`xt1{vHx+Zf?oQ zgb!s8?U|W3^+y|4hg$C~v5soXQqp-wi(J%_*tC(8=jI?m`&M``SdFmy&KW>w1;yaG zomxT}Sv;i}7GbD$A6XUuo`{1d)8*m zd-H$cXE{5*HcpVq4aCD#lHT0*VT{$?Ks%0Y7{W+1>R5fP8J5Dl5dZrgPWNkRa<|J9 zb|)PQeym3ZYL=8L!%blusUufY-b1Qc1I?kYOyQASiMG5>iZOlr5fW}je(-e4L${vGsYVi~x`I1)s!K64bu#718d-5g>sO(DnMGL?+c$}PG z_VA+F)jFJ~oJ!j?)w*re*0f_kYT75XY;7{lbA$J7XsP+{Q3EIq7yCcJLaS0kiF~Fd zyF{=DFm=<4*95WPW2KbeLVG|MJ?-=;nbAd?w)S4#+^NyvAto`sd|#F zWrdmPl5rk%g8QOMRPk@DdfL~1bGw!$Nm zy1jlTQXU5PTYkY%F=Os1@t9XM$ki+jsm&kMC5R(1W6rpVu~1&7E8_t6mDD{Sp=qLo zRqxC~e_GE5RIk-vt zGEZzX)3IZMjXU*~mi=fL6zK<;C?hg5R;r6Ec@wG%?56l7hcBD1{OhxSzpIl+ibx<+ z#LlZzvCC)7-=j10lwOCBz3A%XV6o~E+J1ns7e#Cj2}UKv#{bh|o#f_G>o!?Cx;qAxn;0S-;62dI@ zs@WUny9f+P{xw3{{CR)#cs`0_z)}TMd_!9y?T76>_!`tHf|()LqCwr_0@|Jin37wMMdTV}ogmOxW3sq8JhvCgMc5fUub<+)~@TIaX_sd5HYuI??R zVGHVwJ@m?U&PALaXR|lZ`X$(m{M~qTP;novy~Dw+hq&+1_Y;623TOheCF8hdFt;H< z%w!`mAr$>PFcH^*TI^~`xWLzF8E&_!A5G8u_P&wJJ`}CcZAoR(V$_q+fu}r6D$d>E zJhFPITBXlXTO^K}#_DI^PbG0xri6s(1XN(OYMk3;=Vin~l(mVD(nd)ZTpPWioUA`? zN(u>6AbB~-SS?mk@N>dcDj$Vt+sHO_Pcgaw`l=}6*1$HxFO@15gY1^J-(bG-wVTH* zFbdKklZ@tT>h178aRXVltCFEGnATmAIj0=S;gAlZN(W*s;RWV(Nu@E(Z72<8&FuHqjB`9xH8+;HjLogqN>PpRU}}_d zs+3xoGg&I4+ow6e6i!gmRF3o5+}XW|NdH5|`r{lq5xDl0U1nD5&3>~!C}cgE6l$M= z_Ivbq$WcNrZ_PVuSMKa9kg=-nkf5MC_x;4oo1x|Y9=aW@tul%Rv}7bXfmSqNo=nx#;LTD8fGMuJ*qLiV+^IKx!#48d zzO8i_J_M`W=EQmSJ%2(Q@|5G&(mCK(ri;*nzxn1Fd<0{m+OeK0R%&{?3qJ!C?bz0f zQX!zdv2%2PfQ`f~qnG@5b~uNKt`gYbdjpJ9shj zdLu5GRX+pN$>E`4Mi8CCx9rc-WXt8A_A>gcgAsqTfQHU2L28nV6j5(>N2-3X_m^TJ zHo~z({uOfGDoAnhrhyO4*bY-p-?QYh=-wxiv21JARZ>jwR^~=#x*;Nfh-@MmHm;hh zqzzRsyNZW^XNHjv{DL!TH|xwD=spW(&)XuM1H8-?#EgY)w{Xn zRtIj3IOnI4>ISXBxr5d6lIc}Xx)oJbdvBA zX~V>DT{SY#w?mCox0;1G6n8T`NpQzAu#mvJ8xsD^nvsVTBqyf}Owy{fl260d`8ZX{ zG`C~TjxjGDge8~luM))XH8MVh9%sxFv>mDIH^&Ww&hPj=^Oga3U zGuI~W4N@gNpbM>Al_~&Qz&LMQG2vq5y~EkvaE$fO)yZ=@pBs5O>DX|ST{MCY z5g!Cw^00Ht^?m?DXcu&!pY!wVW!3H=RZYghIU?C-RR1*ro4WEOD`@S&arLd`^iYbB z&%lBId7`aw8tcC6XB0YNomURcd$*%9Y54Vy=bc$7v1Y7E^;YZKVCoR!X;U)&8^ryt zPDLCyqwIsp=Ek*#m12ceO=d`U`Nwm*$_P9h2JbShR*1HcIY@hE4Moco)OMj4nySvQP8sxDRan#7sWMXHw+uAD1(I8{{-5dY< z;9*{La|3cQS`V>GA39rGKl7wbE#WOG84~nl9^MocNH9FDezRwn79@2X->s%aNz38d zG6OYC638^j$;~Sv>kby+wQ0{@=Wsso-4ztIj=9&<&3z!XreS6Aigv{5`))1r+5GzF z{iZW1YWxFSquM!FTT8}@gYW%CI~k$I`_Ad2{(w`-+;%j@2IGm!WQ2~fAqsY@SKuUv z^XvX&GwU15Y%nQCV&o^^fOiw*8cT}&4R8^GtBV&}9CDJAN{=mAC*A|}w06HYanRzM zKI+_qn{mQ@tzIiyy!Ik1+ti?nh^)>q77_2bJ6wUr}XrXX8b+x;otP7oYdrpCiNw%LEFI0TeDiQcmR)WK@ zW8U+Pnl4rA-RzKK-e%2Op_}@(a?Z%ErhN5?je32E*B6zX!;&c2FIhto0QoGuD{8Zg z#Kf;L7fUy``56n~bSk!cdZG*=t2D6sAlAFUN>A0W`#CeK`KUl!jXgDyV(Q{#tRHM& z*;FT12ISx|FU~w{56TzmMbIRSSMGMD^YiBJJ3|QjJ2Il1T`mC&B}y2mt3+(R z5}aXGqM+(>{i9@;~e)&~HzJ9b<1J1^iUzplzg4H@+ zkrK=fjFA8hJrU*%)W{He%|w|qP}c>1UPgQS(gfJhEUE>|Cd+PgYD@6JZULThp}VWH zXsE{N!8gwVq1I;Q%W&w_Wd>fYrH>p6HntEefsk`j>s9D+gS9?;-Pu6(%mx3`NPmFm z9#AUah{W07|KS}p-yrNmD5EUGJmdYQOo3rg+l{+nLPS?lw*1k~R=!#A>nR&i?S(dP z=J)r*8LquEijDpOfzPxzC9UA{r@Sw;X1h2lT*jtTMV-BN3lnj@1hdmnZ>?uZcYeH? z*INF5{Csf(+8RDAUd)j1cdu@v%RAjMo(Y^74&Wn->pfcbW~8aiyN&W5pS&qUct(0# zH7Yg4l%C25+}D;Zry5>P7yoi)M-Fq$OeV4|!gpUklBu68XHwPNq1nt6wV0i+CEF5l z>crKZ?hlA>H~4r|oRw#dWquMR6cFTZ-~REE2zz{e;DY*Ff9uCoo?%DJ?Z3VeEEX_x zZzgD({rGErd;U?dVI^J(NDMdwqiYF9M)!&t?+W|I5~R}G6vf3xbg7E8qK%^==thLe zunR@5{IEv{AjZjrI3Z|Z8Ecj%S%tP%e4RZlfjK#ho65v|H*z({JrD>xXnTI(nbvIx zW5ecWOH99@Iy8)TQ5xzpK|$GFU)~jw`2C#tV$MCRk*Y!D(Pquk9G#>rFV>TYiz1|V z8=)=V5Tz;=RNQ5ee1#yQ0GJ{e0(iV#mQeJj_DHhYh?SI)DHIgC^Whhuov` zYGQ(Zet8!pr4svd%<$dB{K7(~QA%sNiHK1BTx$Fay<5jYX_O`dSH@}$9^?l7>>iCf1 zlxQi!`ZBHkjop^-tpMj8$}=xPQmTLP7ofs{kC)f$>rf{28Vxp~PY#q?^7#j!JgWm0 zbNfbS@3DIODqbw7X-Mlc^M$`OC$$n*Cg(i*(_1608eK zQB=7mbxD#QjX!o36lAT3n0y$R6nq*vJ1l8Z>-3tsyv$Q>g1Mg=H*w%agsrwc(p#7? z=8$Jf#}qpeOtnnK%&aoLd4_|&rg*em3r~f_1BU8NK2Pb!B(3DHV)<@&S6-70%cX~0r zS@p{~q9s4#vhjoLde@YG$nd!Z$G9H2`Wy5Jo?-Hy@ka?6Un-_f`+P8nIg*7hNW zIFWa^_^Ljs$Z>Se;>-&*zWNWieq;t5tN@Maz`blLNyyOZk(uU?^~)#9nVmB zl)1=~VI)yyYjvcl=sqYn=yiu#_4GM4`2m#-htdlotvQE030bw-dn)u?WQd+Frp>r* zdxsA&vf_#ib;KkUyZ)6?M<$^;tsg3AcUd8&%fP_!OLn*ou(*{`$F-yMQU6<0Srsw^ zoiU)iPse??u*>iTD)_JySk3g(BNKRKaoWmb8yTk9-;$c>!V7md^h`(^3T%G;LIJ+5 zJK$BF_ldbBGdYIIhLbh`(~o>JLwrGN9#W6kEMj+wG+w>w#AyNs`ndI3G_ZB0DEVd zEjUC*Iz2+)MINcVR!{%d@N%pmW~jStt>)9uaK~x}#gXF7*ifMK{kLW1Gno>`o}Oz* zXf68u(MwdDTq~kou(eQHQpWX(w+(Ez-6LX=*Un?U!mVPd%b*tzP5L0at6A|b<3LH; zzKcjruy6j!qxGTxLTxd;QYKhF0prB2^b~p&fEfX&RM3<07;O-(bhMP4H;@=xc)Hrx zSSB*WRpsFPj@xhpnXH{Lf6uKJcttT^UDu@C`{lZRZM3%PXc`s)JMD9)4xrF%T?-QS zQTioUJ5@?rA0#IkessP3)SvZ9SDbLW?Mn3qXZud9((`Bmg%Bp`*z!?Ol5wvFZ)&wC zIavq>dIWtedke`)Kv?GPmn9gp?Vj_}dU<2!Ts3`yH7Dp6wGz`tK};N2m_v1(s5<2g zWk0YL0`?pGs4xmpWS-4f{mhbRw&u;qZC?#$ve&|WIzM|9(+x+@40EOfJzZXjA}U~+ zj)A*zQ(BmPD^i|X{1qnRQV>{iU{#C5gT35$4MWuGO!X0+kuEvA5Jf42*M*HVSYr

{O)*XwcS&x;wKZ0pj6#jKBs+~%v{MCDDn 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 T=N3;YP!EHrtDnm{r-UW|XDlN7 literal 0 HcmV?d00001 diff --git a/docs/assets/keycloak-add-client.png b/docs/assets/keycloak-add-client.png index 36d598318cbe3ce2234c723e65ed0be740ba17e6..acdb3e725b8bfa0aaafea00283c3f421257bb3c6 100644 GIT binary patch literal 62625 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�)C3*wkHaLMJ|egoE$V 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 _hd(t}SwmHf^cO(=PCCrany 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 literal 0 HcmV?d00001 diff --git a/docs/assets/keycloak-add-scope.png b/docs/assets/keycloak-add-scope.png index 200486315e3727a8eff1138be5520a5a38203470..b2b759394619d1629f2edbfcd9e4932ce768f670 100644 GIT binary patch literal 58784 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�o}{zTYWej{}=7}GF+6F6z){2 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

OdU`84)rDIW6hdo)bf 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 NX$iS!dEz=g{|hB=6x#p* diff --git a/docs/assets/keycloak-client-scope.png b/docs/assets/keycloak-client-scope.png index 04d56583ab9262c04402738f727db8e5fc3e5c00..cd9609b5419b70a320bb131cc3c093a454c9c4eb 100644 GIT binary patch literal 82176 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 N^t7SZi#6{&{y(lN+3^4X literal 247556 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 z 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 diff --git a/docs/assets/keycloak-client-secret.png b/docs/assets/keycloak-client-secret.png index b4679b0e9d4ebeae102f11269aa769802865f683..c1a71c3d97f206a92ff2c43af27411413dcf9fab 100644 GIT binary patch literal 67653 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

j@` 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�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 literal 123340 zcmeEuby!sG*8YGZ2r8(AAZ5`ZO6M4$fONNj#L(TTA|;@NNOuk)H8i7A(lB%hf^>Jo z@7eEp-}8N+?>Ptm{ax4Lx{z^Z&)&~|p7pGCuY28V`^!o{xrk4J4}-xjia&iU4}+Zt zFR`EDodbXJYooGYunT&KM~`I1A3dU#wXrlrm>IxePyHiQa5cl8Tt_>;2>pb8`Q7F1 zE24%l@*6pL*!OVc&WqFP*1aQA9C@1c(ms$OI6zsHMLpN6$J@`IwBDRb^fE~rDK490 z4Z(TiB_5Blje}9w6TzL46Y_G`F#(tz|L#3Cwrm1;d@-DwjJh>CJhb_y5bQA=_6ANc z?&kfl!9OSnTlUR9x@Ex>Cbt!-rKovAd6J*ZPRw`~MvEXa!XtSp=of?aIS}WbGZw6l zD6=$Pj#fdJqwbu|HQG8`MPA7ul}xKQ6qYo_;(n74NAO@#4Kpo8uucmf7v&^cf^}mN zuK^{d@TXXv9A3O|+G&Ct(q2xA+TZO+v|$BjiX$IX5$HKIkj1KbPIg?Ip4ljRQ^249 z;D+y0;zG^ioRcEX6ODD7h)2SU&r|%TE?Z|G>Rz<``7A_Hm!eJx^LF-{Qtk~8=F8a& zmtYbk%(t%eZf9n}zxZsOdFH3(bNKG5pVW;@Vbfwy5S!#tp?9h4tV$B^-@&Om+cr{v zQ`ETXjctmXi)JfPd$=uMbh*9TUZPHfJMtd=`;;H(8>DM*c6CVnuZG@8$M%vVxFg<0 zWctV~@tc2$;_!22Q9X;zcR`xu83AMG^iWSJsB@+pG54l)ttnj9;`JDGauiv9h?-g% zc=O(Ps6}H|>LqdweYtMyCL_JI-;>mj#9ZNlvcy-i+o?>iieKg0({nVy`C=ViUR^k% z8YCo5I`sy*sZ12ZfIrD)yJn4JWu3jZ#-R4V>?Oanpiqp6urd{)HeND}>@|#3_Ui|% z^jBuIhy}!~H@fGRt{34?S^__>K(YIrx16mW<0q^o;vJrUkH_$428S>>0Dhy#ld>;6 zfsNMIs@Td(?2;3nHMQT_^3_rss}FVq8dscPT%n#$WS>=Sbrph+6dJvw8G^+4>RK@5 zg}2{adE|Gw?r!oeiq(oMc)zu&M#n?X#m7&C9TpF--ZGQ02K0jSs_dml^b*U&`@5)B5eA6P5_k z*@s`8h|XMr9{u}t0!CF#p@H$S-5b=D3)+GjdN z)cAbGh1|!;E+Jzf7fK;tZBc>F3}c#ecUj;1tLd>_yFRJMq002Zzp21km0a0dN#9=e z>R|@EK9wrr_9gm>3$Z?xpSdW#=_jxSX%c+d>v_g2S6!+|&~L|TUDssJ@_z{*f3|vO z6)y}=?KSyp`G+{VxIbur+#ZqAdfor{=6msCdY9VKj}Kev+UeNtSlnE`!+mwwKdw&r zivNpeHgtPodQaWtv9iL8|o!LD@8TRT6@qGf(!fpNyX=z>I#RkQ3#fC#tLz4V)7EcJ1GcW|REwfykS2rs+ zONTBjbqxucCvCeeF)g_)Ne*?`G4P}KDdV#EMfpkiaqZYu0CtSbK+a9mQ4(gvH0CMHF>1b&}CiC$Bx|&*-b(FS~%He_3B(@^(s# zm+^DU&-cG^Gw|$oCAKB@%Z_!{-fgl<*Nymbx%LuWL$Ut)$MV*x)}vO_)}33eVpd{) zVl3oxJQEl{9qqk|78qs)HBW~-NM12sp1_^ zU%mIZg>$P#Y?)@SlF?-T1%(m+uwBfYbGPt)+ajy`QG+f;R}F=xpKZ`?m~C)PKWVFE zy+(>fdL@vHw2>wL(*-q3Wj17JZ}WT!XUW@7is}|6W<`sog{2b3OsFAK`^IRKHdK4D zw*IKzsHwj0{V#^WQn_s_C}Is%?tnww65}wB!?r_{!`KklfDnqOaAuHfuzzUGAyMEg z|4#u$0Zr@M{DoAulstUeb`RqODTOE$}4S{51|r4Ihl12kXcAuV3#tL zGB4Zh+}+t*4qEOIiWPcH9V2AnbWf1p$zx$~Vy~-ZSaMlurgzDFGH$uL>wtgQi>gJt zS$OHu>iO-c72AQCxco>yGijc?hZr@Y&7j8e_d^?bn zmRrwEKR^3=?W(d&8I}2U)*uT%ERL#=y&R5uOZpr7o>5O@uL%^GOw^VYSldoW*WaJW zs9y>8j3bKuB=|!)`BCz{f;-~$jI3F7b5b@>*Ry{Hi|f<1#o4jd-xdwUe3X!ok{EqI z#KUK|KGdXzh)1L$`qKwwqWGp-6~nU16+)hMM>Qlk(8j%skF60nIwSH%r){NW+`z1L zmc$BY;jE0$gRfj)E#9MzBWC5&J*Tr<8`X~0kVO^s-V^xNA+MPSX`SP#-dEo_kdl7Z z-X7~c@^hrx`DfK;)gyU#L~yZv#olIA_pm{Gp}g9Y8q4FMLv80&H(zYIXL6jR*FM)C zIH$c@zdlv{)$42a*V6l;hLj((cog_I4p}nPBn^d_@R|2@xrH_ERvH9P#roJTw@tN< z@)Jfya^xG-Y#oN~X%T!STU39l^|5HRRIh(RsXm|d4!a2x2eX0FDrzCACZ5^{P zw(nkPz)_=GSZ3~j5V|EP9C5y-{X^vkm2gj0CRLa1%7QT^51vmfwrayR`BO0}L@G~J zG&RV}Z2Fn1l|+&JDl)2C=r<+lW^Krl?!Ne})y#py;0o#Typ(&{h1S(=`y>k@W<-`{ z>Li*ajwy2ucdn0_U9qSr<}(EOw>^F>GDl`&beAGj@ zNMqvgagkwc%{s%*s4=fhE0^m#7gwc}BYVE6X++7WD`s2skmF}#1Ak;iu4|NQ%Z|5u zv%8Fk#L3*y@wX#Z+yUHq57d$K-j29D(zZ9HA5b(YePW`h<*VJJWIivDA$d>6(97@n7UPrRpnyva$G=Vx+C($>QLX( zSEcd2>y4fEu@JN;QlWEi(|h8JZ(gYHrY}hd3w5ucgR5JaK+eHLSd&aed~^KvPWW-w z&P;>Rr4El{=Gpv>>M)ne_?861D%E|=?o_)9b-wz(-obKByX)vl`eN?du-3}JigdM% z$4RDV11`q5IV6}SMp)FNLg*dOUE0Z(y;o1LE^u?+zv@LMfR&g5`&@?CK^WHSSo7dP z&rZ*Sv&UDJ#|Uui3B33RvVs>z3}M?}49U)x>nc$7UVRChUltzWO#Wu)s9@VQ9^|{~ z@O(Ut9F|H46K=*mx6gC&g4=6H#YH+;+unR-x`f}V#cDMYU#oBcalYotme6J(C3@ML z37c@${)C;E+?T)F{>D9!bOacviW^Ev!I;2nJlI(*3K$M}g#~_vv9AB~wHOuy?93nU zW5Zy+2-w*_kC6s{p`Ul)7y8aW{+)H~%<-AH0U%X1_`M$02s6LN`^VWN9B+ z+8EGsv2n89y(x@OOG_(g^TLo{{;}AfUkCpax@l}@XT{IX?&#>q=6IjY(#D9LgO87o z{q8;Xd-qtu5v;b(7IwN$tQNMn{&AAO&hyy7R^J9;Wrwh|poPw>t7mC%Cv@{BbfJI# z{9~L3PKf`xl7;P`w*_vH9r}cwgY7Q+KhF)mDhR#HFN<(8FjIYuFb8u6t|5Hy{#{PN zKfds{kN)S9|M;qst%1!WOLOo|JK_JC^*_J-zd!u-i+@~G?SHPx#moD@FZsVd`SY8C z?9i?Muc`RQJpb`7m}p^qLH2(xnlQeLrqVW8M@qzF8Ab3HSQ+$#MF;+2_{U%9HAa?N zZNV7^gTurhKTveSS{%iVRqP!V8K+5n+AI6`On{e`+#-(t&3qEQMvJ#vDT!UGSAys= z3+M|PLwUBp6QIyqSAzluWK&+^CEt|C!OA+}>vyE9y;sRn6xiNud&uItQa%&wI$eIh zCx3=BI)*ukh!*Yz!@@p`_a9$$(&A8@hjD$IF~pp5bQ=6#NZ7S`iM6Q6bUcNT;Qwod&!$I*6r;hgoNSgpNp|67ZJ4gY@Tx8{rD zlNU_(jE7UY^lw-X91i>D`a8#Y!OxHq!Aw3AcJ!S8kM{WIV&K7?34iA}BID24*zk+H zKce_gHFfCJShOp@bsYBpcgE{YqX{!^$lACP9Y z9B#X-s-8z%DNjPE1DRj-OP#uKJr5JVob{^NH4zg$INsfL!|)8Mw4_Vv4faWjMLMwR zwI~fgMjA>LMhYN?a%OGkOb6G@eYgDDm!e6ov3b}$7L2?;DN~t8Vd`k2l<5dh#GLgw zIT&jUlMyC=LTUY&(q%R&f?H_g#-EG+?-!iuY2w5gIOD2q-)G&m>C7gs!Xv|Bwl2q4 zQ_1PDP8oK$JBA%m*DLeUnz5g>A}Ja*ddM=KB~aN;w+E%gXe1fSY4Fso;doI@DBL$X zZM&=<;VIg|j92~gCtg>64agS2m8*%0l2IGHIbp zy9*Qy>)2GPsF@El)$m~MmLW;WmMk#Eu8_u`r@244=y^pm*lcR$SO}S~RO=_U0r!#t zyMZ1KAIvmusu1@L8$HE5sjk|Xt1NfkR5lwHq)?HfnsRAx zWqrWu(A;V~SS+r4J)iSfAVI3BL94*LXjU`Xa%g*@*+|4+LuXHrzu1Jr^Ppbc$C)K; z_)?CjMU`9B`eLAPYK^N- zHUtHBr95e@^S`PeW$*zo%-Vd>JeI;b=&1WfhUS`x{tW3jij}Gj>DD&#-16z@ zu8OTv-A{Jb?Xi4ogGLDkIz@(U&Dtt!TRn7f8U30iZ>a>hL!VHTn)c^))wmyIOG(6c zeB`(#y!SI~F&+_2#c$^qOeG+eBDR5kk69q+(0jZ+RB1OCT4Fjf}CZ;q?sZEriQj zyK9BeKl@0u^WD{wRB_Ova!JpJL^*Te9oM#uckID(CHr#uO#C%rw_P`L&%G9%2Dv9x zB#cC1>?iyE=m{S5p41}vybJAl!n=@DG|@z#(5e&6oL_a1d*J!2MYq-QFVhztPH2STHVE47+yE#O6khZG$wjj@z_^d4!t=CV`bSSbn#0k_9;DKhr}=O%R@v#c3XetxnfF_em7@2Z zHMcjv<4X@%nobygm4=f?2ZF}K5*`_5Nn6RsC(#HPg7rE9;xauXiya4y8YdCB}XTQ+gxuZ&apk1#(lHv;U}Y_HtxWSJR^5e1A2jG z^+`RnK6aRES*a1O@&oif_VOPY%2RFj=Q0|D1(rWj&3Ha#CZn?Xc4KWWBf(IKakV>F zqco=@#a^y z{N>Dg@t_r3*W8^#Y2wjE%t#O2{=iUztHQ;e304ay)b?@`Dw?&dnwFEw*ZxG+9N{FKl)95uy z7?~H|vuSBbs2{T#_R3vNF|=-cFSOq4v%>h6Q}3{N;9lv{pmX_B$NHVfqbBq7++3I{ zQaa*@6`h!sF?+n(dNQ52A6PXEAH*Dzs&%JJ1%lYer|moyp=uDJH1R>hg7KLU4Xjxu zRq3V27E;GRH_=UNKVJD91+Uff)aPBJj$>M?q6|LH+u)U%QX;W1|5UgF{%FnNddtI4 z^ghACBFDSNL&XRO_x;T|Yhe3^`sr5E>Wqvyf$LKff$YJ<7L}|7%dw8%oeSpDW2V{b z`*l32=D4Oxi4JB{LJIj(&!Kng*^_n99zJ_%=8e+)bE!@{qmD(#*l-*%+*1)FoC}+T z9L9g-owT;;*oY#bmVE_iK@YlSzWe2k1R4p4ClFBn+2{v(6DXVlaPeaX7vlM5%XbcL?FgGtFZQxz>8tu z^RNhv+>=wIyJCi?GqP`Ps6~xyyrPQrUwhW)4HH z%qkA5mF}@Mlg)$H*09+G^e88VLCMCkRo7LT#?e=3zl;3rRFO*>2Y&?K$N5Si z3CY9e9u?+Yyw!?Qf+n~CTt~qEEG9UsyDZvdw2H3 zIrXgdx~npEh>W-o=385>_d8xlQ3KqYBhw3)%R0`=91K5vWZ^Vo_3bECE2<5#BNIlHLci{rJfDlmgu=eItvREY(CKbjjs z+3OSDd=Zv*&=xDiv)ts9XmPwV!=XFWdPuC7OH&$9bCsiBCgigvn-hGkT5fj zWj1sDuyJn9{(J%0fd(4pLR&owYPEC?J_e z+hcEvcY8p;DEhGP#h#Kk6usuk((jq}<$#F8T1XeE<)CI(*hF_HhCb3L!M%_4`5k-3 zE2k#P^^kX>*j!UtTM+o(GVT0VppLfyIYec6MX6S#a;3(VrTpjN zaBQzbRrV5QS74>eR=K)M?7D%5Jpi7bD&iE}W~z&&V;+NBHrVj2$%TA0H6kWAJI!{c z-5S_dW5e~1%8DN0gT<@0zJww7+j(zcNHx=%Q$mC(&YZj8$8A1TrynBpfy;T^8+Rhs zw%vbU6KggzB{<;AD_jCtgbUvMUor9DT9CK%S*Ck1PVMsi&Z1A&a_Bviqa7qNizT*3XZmi z8?Q*Ry!WwFR@Lo1>lS4<a1x=?;^U!w@3l+I_>fFj+=Q`+s}qe`Wih1tC+A zxL-Bd+hsl1eXFo(8I$q|$`(~SNwu=pzy#1eiz64%<}R~J2y@(Z2+Yq(enT^V1y&(Y zTGB5n6Z}oPqnqB0Qj#-ItBg^~sFYbdgahPUT^hO94lIWX-jIKAoU6n;NsUS)lEUv! zjj+RT>jz!ui8uO+h=?P;B9R7Q$?n=@isQ=n_b3v{VGkuL&-j?5u_pJOQ8ECwwEO7W zvP>8dAewh&$RwDx6xI@o2woCc{FF0OW<6QBZ)Y=N-B@39uzcWo>%*}F2=)yC6;L6r z7agpiJ!|gD)Tp- zB~u!P8aipY1!6M$bZesTh z)Oi|RUYn*5oBRlJm=_x}AcJmGIhL!DtaeMdXCN^GqBVz6d#rJ9wo=ayN~b>T8S_-E zO*5mK68`8W@U#N)T&Bt+4#VcwdsC5Q(UDhS6hba`i-j$0gV(_1bM>j-`k-!-wJ2DP zn);x@cEB`p+wrdTL>=$XcaJ8zWkiDFgE)DYtdFMjS0{D~+0$J?2VvEkTsOFe}4gy7E-TpR6$2QS46#cub>AllBf*D=K`Qdw&xgCa@BZ-R z(Ea*)0HkCfi@=mXu1*IZliKT<&5XF7PyA3qhj{y3%8XRJXck*qvGLUFimFJ8O% z;%SX|tmpAwL9em~NqhH~pG{$tD(HQs3sn4^NEv2A`^A9*tD0se0TIsWwr2*b4vQu2f1~z;m#amr;>}_^Jkg(Vc`jOL-!Vr`y1=No$n^ljXf+O z4&7_YHlN7RQf|twX~uDI(b-pbaBI9Qr02ukwk}05Y!vr#SlX;~E0Vb<;}vmk`PNj& zWCNK<5aa#h>ueJJ7$KB)k@;bsW9e51Eg3Wqo3y8uF|lum{P2X0wM9&RrH$8gF4ZsQqaxe)pC{=rhI4r-|vO2nZ!T($a@K#hTsOM-co z((`a_a9fQ~Xj3*xXYC8fuP3&LEkl^|%5EZ;K^|?l(8nqu&Abk%4-`?cc)$EGom9<5 zhcq$9y0HCpSCU96?!nuTJ6A=dMFb!T8s}BZ)%*3OFs zjfDq4AIZ)Q%_H;UM{ngm3`ae<3S%O*eRiq`3cq4Z1Y(ST$exLq+gdMAmrd54LZWAt z%95lyd6AP@<*?37&4En0Wi+RUdR&GRbDm4NxWzkSeu;deXUkhG-EK{)N#;f04BQGs z^Q3lN4BvLRr;)q@P=;bjgFULUhQV-sNeG=!)4i<@;hJJZH{DRFrOpiE0wwR=cK(GN zmfPW|X(o~ZaU_0PZmE3vWUxT8WF*_5^%@}}BAGM4*j`n=%}Dp993aDLJPs#mo@`9F z1^ULV@KzraV@oh!<}%$L`#j`!QR(!o^{fOQXBkY^zdtbmM5HIw&M#0lvM0w!MN3uF z<-4Br2#tK;iwuoC0Z*zwYTwV=`;6*Te*sUN0JQ+7UFd9TRAVY32t4#*X; z=OFH_E$E!^e+RyX!g8S0a*X%K>#ND)VRT$5+xP$0S^o1rWSc-GAmY%jHP_6$+;*m6 z{L8D!v6Ev*)O<&(c$gl<6dFWoaRYZnTz$Nkr1|Khd&>#=_c z$MFAp?0;gif4ulF6!|3_{}+nDMlUf)}R{3b=J{V z^EDV?AW?tkFD~xyw+632&$;_geiLWFb-A1#VtT)pzp9rR_a%ZwbDOJQxXzuUOlaB0 z)&G`GD!L~-SM1dDekPPj{DSLcQ9kvdY!f3%AT!#Y&(CgFH-gUa{pP|1QI z;YtwCcR!%7U`G~VcM5V!|FRSQw&b7n=sOe7z%$|;)BzQ9b@0)`_iEP*=c1}i$7p+~_c}hboY%5pv=6GKryQ;Q7_t>Z$oFDdFnkvj-o2T~99sZ83M!o`Ks= zxB3fh58M(4MTKXRRU7Sr+SM-gMIFM|(ML;`MHR*S9cKsyi2!e}ZCpTp-|&U|2UR_P z=lza20iN%kCr7(K!(-xo-^H??dk#?lksSU zeX(gjTc4(F8^l|e=!4n2jZra|h+g=lTdP;+cNyYKpE5TW8+Y9tLYZH0VMn{=RBsQJ zP$Ri_B+Vgm8CW*NL6b#w08+H5KghXy(5<9)SC9iT??5ypm}q=7Bw$x3;e5A>!9g}=H0J38J$htMau z-E+N!OKAlM#c=fMGq#%DiLxcsaN!2H8M<~*rTbbkM48$tKJ<_&f`Gm1uE&0Ng5j40 zP!CUUSVmM96fddvhGO#}qzOe1t$HN~n>qJBVcf_h6;mTDsDZP&qEeIV0;Ercxt*kj ztvwq9`XNC;+=L#oMpoW6XbvwMe@RdXQ?@tHM&>hq3{$=$p__%{6PFzv_fT|hFij~@ z8%UfMZKQUxhE6JZOR+m0lNL{9rk<}|F}A`=EWW6iAncAWGY9d&6S<`rQY{46M>lcr zsBDeLQ~445+LSPgyU*dMB}H{{1|6urKVVgRQ6K1pno*q+!&*T=qxFXdNF&#+a#P5> z)*d5HPx6%^%L3E}f`M+X0hI2pv1*{+&UI%PX+)*<+CplIsj|u77Sw{o@XNIPVx<9m z<{a6PRI6A@wu;&0ug?5WZKX8ab1UYv2T2VOc}?`WHsND8na6a|N82qx+@w)76wZ!5 zyoWgqPOmR zNDwuQx5FgVyQV>@Hyh=C{BSK+L93!4h~LO!EGiH+O>Scfu-VsFr5Vg3KTxE8R#^F7 zJ!7g-a?|GnW<(xFuY>yTvcL=ELWS?-Yw~(_3iKNr0In!}u8_4*3O*9l$V&SxV}nG* z6hgv2*XpzeWDiUh|A9R?QBU|n^)e7IwLX-`hHpum!cU8VvX`+hGQq-??+=1nbs>pu z=y#fv-9~5SGtGfeHEa#2XZQq`{#5UkXd}{0KGI#3k6D#I{=T=uczdHL;fTdEI@csj z`9w{Y92A8haj=ge&nRt=;H7=xY<^~G%d($`KGL-BvCIWL@ z8JMI;zGXm{lZ@z2-zZ_tdx~n})sbJN_eq;q4^_jbohrR6Ih-G7-9R37^0MK0wU%)5 zqkgde`KxrT-NjeVz$IBrdK^iQoAgtOWDmBNih85g?Q2fg?2BCRK1Tv`0X4%E&y!=E zJ+Ro^K#i)++W{mc-@uUHiL@!7mq6n5ribIh;#}sl3V;voNq0?Z15$yjzLYjk9pX`W z62q=s79(X=)#!%rDC{Q3FM|8@gS5E zL9{^L)$V;2B67?N3HKh_yZd-zyd1~e9W{ZM7#v`H8?LM)G3?%pI^0M&X;tQ8vd}?b zCH{=XHpsd_!O4L6U=&!$QoV`s`6TDkmnO^}xPIJlYM?kvS`B}N4@>JciFaFXdS(Fh zVhQxS@)7fn=mV|P)hA2>OT`MYpgyk@6E2uf0h4$T?z&RV^Fh<5DRc>mWcmJqDRIJy{o@jVhZQilc9;m4+N;6^(V3|G9}#?xihZr~zH(yS*NSTTbj zK9@AW25(lEe>uHAxbhg%yvxfb%OnWngq!A9bN6YxE`4?GeU**;k~%KC(3gwkj#;TP zfn~<_dSVi152{a_2+dyzi|messzFwdx^XD-uB)K�Y(CJ@H#bq4_F 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

<)%u0iZM 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 literal 101393 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

9W2QT;MpZ|wJ{QclR@B5EH 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 L{an^LB{Ts5GT3+1 diff --git a/docs/assets/okta-app.png b/docs/assets/okta-app.png new file mode 100644 index 0000000000000000000000000000000000000000..bfc4570826b0a25f30128f6b84e54a10c9d74f2c GIT binary patch literal 260259 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%0Dw+!%A| zWBx*p`9+`Lf!#<6Jx9~fw?g!^HH3;Ym`f?`{f_2j z!j#pDkOx)vV~U%N71po%@`eyoN5y&-eff1!i;xUI{6}!F*6BlFG0HV*@#Auue)aLm z^Ob(;zLnZca0`wLlLGeCwAI8lA^165lM4 zVL0S(bF|oV22SZCQ?ED!rjb!H_&NqSTkn(m7OkZ%T%`*pf9Fd(igtGabX;Vb!SS36cfJ$%IEc6 zcRt$}=*=HXxo3xXxIG>8E36hRh{;J#mq$qA{*=!58sJ%LbSIXsHg^4F(su{W7!?IC_&#JOaA%)agcO=;xJ;sP`kr$oWbmAP$Al{5)1+ z<=(wPO?_PFvZ74EYg?(9ep+Q@-&A7K@}P_1i3q`qUr8bg*u?`rtVab9(nX^&Ntl!q z0Cbs~&+t2FR8YHP_$&UffCDIvh6T{jMXzJlr>~wJ>cj$A`Cbu6J=s#|jkaR2Z0slSM%eu0Q-y8@6W~>4pjtR%tlj*`57t$Vd=>59|t%7CYX`+$E4sg zk5*u>lPamb8*b>08jfM?$yM`>#ST?lTDJVtN^sA_ba&aI+-_ugk^gi2pAP(dOyXEf z0cS&2&)siNp<6)V>}BacodXm`jlV1RzqrsNSnLI-8M^Fi#R$0EA83&^{~yUy;2z^g zZbE7SXU+2?myC_cvI#F2AY#k@27f84`wpXc1U@(almCK#x?{|P&b9~&js#2`$M*^x zeW|D4YH$9J>-k)M_QxDU`Li~d^TsG%7(!MGv;6})^t;|4q1QkcCGinq!#d}*{pzj8 z3gWrBx&w^av%f<;Q*0h7!)70-#mf$A9ERrmcQNkL3jI+D2KL~m01>n*gsqRn-HdXR z#pr*ZPZr;P>Y#`>&4x#s{wAIDj@%&MpXL$EE!}$8s-&#N50B7~-b5Ql{td5)M_e1t zCI-T>%#8B4!d4sT{|@`2xdQ845<}TjjjK0|V(2A<<9NRf|EV*6@30C?^jUzP%EuX^ z)kEy}lUIou3KS;(bXtxoU7X3NhQ~Gb{}D|D-o6ovIKGp(u*q&WH0_X#2&*z+|9Ak<&?(y z_X#0Rm+*TjIc|*SBPFFhN87Q&Fiw+ZA1=p9YZbkRH1^|r6`D|VG<<)3nD#PIgrcg5 zb;I`1wpr2pPV#dg^y37o`aKgPr}BvUQ;Yt5TDqIiEj)=jmE^0$K=kbH8+=j%^w`mI zN>_}SfAhzc=eaMY>i5-~yp$jfKx8gDQxaFGV{V*61bt1f%J$vxBtUmFm9|VjWv`um z^@5icjsj#kn)eltx;=^dQur?ePF(`k?pgcA8z+B+WLK)inOi<(Oh`0(|7oQD#!B>4 z|K7NRQh)@aUxA~F2#sew-B{qdN_=Yg{o0`Evr37Al??wUdCF&q8-VU2-n=_*KtMFn zUHTm*Tbdvkd^MPe_5t9bEl^De4R~oEoOwL`IXwei3@$qr=0HC!^7md{i4?DFX1cm0 zYI9fIXKw&q4$yudGXgHPZ-9Ks%f7xeG}>)Ty?oSR2fZb)hPu#%w=glq4X| z-Y=ftVzK(2SD7`ud3A~Wz@;nb@b5wB&vg%pf3|laZsfLrZW zxCOZJZd=n1)ER)_#Cav{=~BzZc`P9QV84u90UWI?z>O^;40d~b52sl5JEr}PEy7Rt zM@$gUVClXVU*D1*uu$-ANOpbL?!A=1EurS+oH>LMK9;lo`(qt-oMh3p*A``%15CDU%VChZT7q0%}m0OE|00C}fXE)OfoqXAWLNu3<6fD;>9uybLUz|kw zEjOQR;tq&zO*M>^!_PGfT{Ao{M(T_41N<3?+`_6XtiOw(eBRYM)Y;J45d;zT>YK?6{;feNMNT`nQYqX5_-jU>{7^ z$sxVdEP$-9@xo6G&?kp$4Hb{U(+85>L@w;=oDVyhGOq96;s8STicoYVm4I`*h9hm` zfj+LyV9tBrRZkJOjSrQMQ`(ljN3FmS&3x6?y^5#^S~0at5fFI_F|yai*?R>LzyiSx zb3hK<&*ir97~%*7krt=iXUZdWigj6lKpY7{{woK{%BG{|tSbJ$%b=)>y~LBIO54-p zeKVU&-B8A5L|Ddm`2Ey}R3tSH588;h*<2bn7NW$WyJB1TFlKHwbb9v1vqAvAY2i%y8ppJyFs5}O@RZQz_jV(i8NU=h=u+KE z`gSUz%$@WrMz~>b)X%>&va{E$h87-Gld&ZOLi5TRWhVar#H+ZQ4IVE6ynIXutzV}A zA`P$x@(bUTdRIHa>CP8V3e*{)H{v#bbQ7*mPE*o}9+~p4O^gV24*^L#*N~HksF{{h z8ze>%b7nx7@4L;3Gx-fYn<-!*B>$*Am}y5jeh zC~1j38j!VzQziN(Pm(8QjC2lp%^MDHS+|zd_jh;SCQQJx#7C#@cA9{PrOPOk1cWhMkK!eo5TMu*xi@| z=ybZclN{TCe1`7(O352P4mYPXY=`n4sbR^y2FZL5YpHNLi=D-={xa_l+T_)`)8k#a zDqAh+?6c+GF)m;9;if|Inn+I~$4cCrYyt@9jPmo$I5F2Uj#3RsHJ(3V%77<)-OyD?ha~X#l}QA)pN|#gQ=4lQ=`?F2=0Z$P)h5X`ary-TsNb zTNz`DvZFnMq`ebBC)fpyHhJrCFIjl1bmqZxv~^-pHt;+H z)aZrh!I)K!!vx|W6R)k2ffz+1xU{ULV`TFt_WZNc#^^x*ts=LxBjcbtTUGaqv%{`> zu$X6F?J%FK6f*RNm2^wGuqW3T!p(pFolou$F5uT+}&i^#c-oBn4qW&JjpzWTwL z{&zC9{AV(e(XH3pyHC!g8Nb+Hx47iu_2%mx^~;{;rqYRDYdLd!yV&Ed@R?*g9!aK&hJqzJ)6=vW|A%1e3N61jB2cn95Wi=&ojIc-dYpOetB?soxu` z0#zwO1e&(L-2r5a>Hg<9NrL+ZD;`tzS~OTEQVnj@!kH<-UYlyc1~rb{cAZ*fMFvkc zfbhTy6~E)8B0w5jQQJMv^{qRa4~Lo=kpqgDysXyrCIT8^I#WvdmKwzHCy4EEch)mV zuuXk>TCfeE{rHr^Wp_8B;)@C7^Ttr=MUI~2i!zN$(^e`NWrUZ6WS??cy&(8cunSM5 z$W&u~lt6fS{09mxUG44SQ6fz@?O z7OKx%yc>4bu~j%X4lHZMV6uIE|D(KRvGdyRwr*>Kv>Nu*8x_Ltn~HUF!APh#_MN5z zJ4?TVF(!ny+}Y`Mik-~8jj@ix(u)DQCYdOH;0fHSJ|Gu9R(%O78Ynr)S^ zAkGNFJmhtdmmTfO6XU-da-Z_Ut%n{dknw?RnLg$+E@irc))63Dk}5c7-DcBU6RXOc zt96zcUIIUw_HGh^)=CPAa9Z|u2JBBs;1TnFe4zR<6kdHr76{mqQomW5rtz(8erQnL z`@lIwk#cs@XBVnGZ0bwRV;#D;K0H!TVcKd>vYJMLvLZ(}ffr{-OjH0#Y}NT?b_;UE z>Cu^F&Tx z>0{Fo;9YhSypq5Lo_^U0khg}%yUJ?I0Vt9&^Q{nW9fF6v#!5#KZ07NIp>kNeON z(qWQJ>`*~pn>YCJX>JlzdbKY~l6H3Wi@eqbmr`$-wx)-9OPKzw07PWzE37gIV5qeh zQ}Ho&C?aB+NGeBZSb{UZLtY+qB}pEP8Ye}pO1kF?d+rE?0LYmKwxxme*cjbjj=I{2 zloupj7z+!(c1{ri5&Zhop^(zL^pmp3=^g(rI1-P*TmAe2SU|!E@4?Sx9R+W*0_hQm z82$4=y}4SapDt9vO*+Tv#xX^kW7G17XW8+UH{7jbMifm1ebGZ79a;eKJB5&2s!PAf zTMjCR#{?&CF_d(Xv=_Na)B79DPaOJbjT!m>(1-boD4u?&?ru7oSJ)ZW(MSz<#fJgX zXxVQO6hPp(AiY2W>`-r-y)rpH{=GK|4rCQfDyN)N8{H`_&?9kyrHhLx%lW|aI6yE?+TqyL8)XJ%Xl)&qR*{2$@^-hC%N5(=oyxiREZ8^S)}gtBTE^Dul~_Zp83??DL=x_w0cAZ7Ck0e90z;}n%z2(lHF zEPdc!A;8d@oc7{sR?GHMmQy-DvDizOm&60^RJ$nSdwk-PLt5*+ z1k~0f#Q&msXEuoFamPVFkQ5>ZK=HAVbe6|J8%;R5^9OE@4Fr3s)nZERj|}*pvt@!f za}nntPTfZvG7JwNp%DMqIDfVahQ~d_z+L6W`qG9FQZ0b*Bu_PWKJMf#7&f6Tx{m2Cqi3aZ`z^Dcnf4_)y_uC@cJ zyyX4m2jfPu-`-{LFkHccydJ6|R7rh&!*vLerzB=TpCk0$h4OSp`7&jXZ-b#C?-~0A z3vv_%eKs2<#(rI2^AkMSVu%8s$I1dcU}Eedy9FTeVheN%PMs z@{W6^Pe|PMjCu{E2GN0Pqn@o$tX>eUUeMOoz#%FrHqSEN4;See%-LV&#u;;0ao+hk zL~YbA%_sbM3v5ZWFI7Mxnb*7f?&?HMA}{N=x{X%vzY>`08m9B@VcLB3<@CL;P~*Y_}T{izV2h82a4Bz$Ca6?@tAzW z4$vAl?G1+SbTSP05p^GHN0rpC%d>8*1JXb4z7=LjV*Qnj$UOLfJ{l{>Uu){qGZH{H zrx^a(l$pgR0sJxP z>n?D@HBb-08?ZzJkc*W9i*m=#ja-L+*Elh~pndJXH;YB2`JT_g_=R+#WmnAC4Xe2J zbS>2QzWLX;74TOG=t(}XP9_`rY*PfiXAn#5Piqi38&1jYaS$gFgpqYE$mlOHpDrFpI*Nvx_`2;UogCR46{o|z+FR#4GOt$C0E_N~9=`?bW|DoyP&2)=y{i)zmp>mV06Cf8-%GcEgGJZBGJgqZo zOlEv`!MklIAHcO9+c&vMZ6!}MKNnG(GaVHBU7Wd-h>C4;4#G5=eU5u&dyUeao4Pt^ z{Y|Bt6Wliw6R@OE=JHU<!M+orpkZmH6(_gfEgK>b$$J?rQDDWPj4z0MdZ)loOH>hM~DeSr$eSGc_ z3B;Ht;TIjWD&y`Q=O>K&Ua-C(s-Mu1WEk|aJ@oxx74i98JbTR$>iSvbLA%8=Hzk8aceC{v*QO|JQ#MU{*$0j zfG&R_iIQ*h4WJOOlK51t2aEW4nFB=8H+Ell7`x}n^;N5M4N^$tgb<)G|NfDFut>*b zMgO9|jY!N~Nv#1$K!LHizsR}3&D1b)oIVYQMEVi~_o+QULXJnRg}9fDFJ@VfOo>320ZnSK(`vQIDRewDAAFOhVBT<3`@ z^I!cLb-B;9t3aBZF8(cM;vZ?Bl%LfD395P(mhT`CAa!_;iplMf*|+N|q?l4fGCM@4 zaItE;5$SZldJ9>CgDo-DY46e&4V{r0gW4C7;0>Z|SJ&r2N=!Gvm8YTWvl+eu=?CVB z{cEYQd{hn|j?03V2}-wEwFg-S22?2>$5NDQj#_agQIbg_-odM(;)i@dIy1>@SMb~B zqvvtF^)QyBnf~=*JAiicPN98Og~3n;xR$nLdyO4+=17Etb_zh7x&fN5s=)%K6kfaM zN+_E|@9cPGzkeuuxM(CAa9(vs>N6~S-VxB8244DxF8U|=8llZQ z@gZBMrvYnrc;`{k zvvHZUnWuv4^9l=mR^m_aeY(k;61IMji$DS|D**CYanrcc-X0`b^%qwx-1=5Q@)o%Z zYoXJC_#rE2{n+(C+`Q~VfC6@z+@kDtN)+?s4nYe zrW^SU;42e{0|i4wwC5AO%+YP=v?=zH5wY_lxMEq0tlQ?KO^E>PG(EKkorXBa;&(T? zXg=Sp2i!KK`&Y5^Q6>QwW2bX^&!VcK)~Dn~i=~?a#w~t9V3dhJieF>q7FP>w#^wx% z6(cJo4C}APRSV}7<9Yl8t$8D3r3)h=hjAVif8Gq)?&XZu_aIIduL9K(+VeFptNJxn zP3m~SESWg(59{dJ9eq7~Bl~z<_Du~Pt0<&}6;alIeY;<1sxP^X6tf>mTR;F%7Fx|O<_vdb z#F4$pgQ!$7^Z*HRq;%($aP5LYyg}`ZA?t8vi3sX*@5ZaVzZPSN3d~N%s)*8~ zJCyoT#2nI)03=zpzis~)_y@u)V3;K$2HX!8!FM*Fl(@6;NbX|`#cyz7wwuGBu6#NA z(vmYNLgjF8$*)Ym2FUa2=lNJ+>Fb1e-%Gk>ur{*gyEft!;NrLXonAfjOH)7RA6bE) z>)46=Kx!91@@(JweD| z-x+6|KWDsuo{_!xL++J%&o%3H&AC`YlOmGwqzyN*OI7~-Skn+W#(-8X5AZ}`ze52v z;LZzA{)4VQf)9Smw@e&X+AZnQiR((h(ba-_NwhhuE#BP|uwW?6hWzi4Nq5D?V^d#d zY?;L{Fo!!n$Kgi?|N6ZWPJ4jn-IM1)WQOet;ADI?jqBULT2e3fw8so300V5g8!zUR z64>)wK6&@Wsj24Ws3nlT$PEUvPbto7rmm<;eYC3VI!4(1QVEY^0cJjn*qp|}c5~gE zkx%v&+avVSEEI2gfWZ(q%1OH9zQVW{DEQXw|BjE$&mTBME^#uBisIj0Q+v{FWVR}3 zEO(SY+l)dh?@^XEf3Ip%_+C39Vo^)I;_mA6iaJ_^Wp>uUwqh9joWG?&rE{xkj@;G! z)~=RuXLUfxFHf%b1r~DVl^5uO-Pb0Sho$j zLRsvDadYdmQ+MIVTEm3=gosve+LLX{lSKuYks|K*-e1 zu;6M1+ts*?A6W&HF_O}sG}DSSRMWExy3``ZGGp}f3y1VlSC3x;RWwHt(@L2OzCUiSWtatnMs8S}z(2-iX??kW{n4Kb{RUGi^7r{I8#=Y$wxgAD z_eL!2uW=aou}8rHHO|?BRI3}Mp@@_Ie(0|h6u@XnyHt072a`6mt=kMAey7rala_|r zZ<3=B*WQ!Xqr$H8bHjC0VhT>MfggdOjh*eVFa8CXkuHeg zPEdl7Z(-#a`&kOQuJ;2C<^j)8lDMoar1q9H-^gqoW$P;Hx~nZk*PP4)svRXaRduqx zeh9UMsFl_lb%6N*@4?Ilv#4-EhhzrMduh^n4V69V*bGebo7E9O*mKF8^S8 zIG?qQdGd_?ZQ5N#{M|7ei9iUBU&BWHG9pppoh~vG?N47}9cS7HV9FW!PGA_$?V519 z3wfR(8v7*>SWh#5_q=}65WdIt7pRxWwC2v(llhQ;YN7FbkS4&>#){Wm+d78N;ae#^ zrK==@{Suh@p|D?2aj4Gl_`LR6296?J3k#T4^pW$<6aCJ7+)F^{5@po`SYf@(Kik-F$eD48DF7mR%Uo*IMTC1wDJomH7lYqEer|n zbJ46Rx$bj&FI;psWU+yPma!RE)7KUxd6bd=A?y>#TQDiY*6@D%Cr4BR{=(D9 zD1y*zG|=(S&rg8&J&QAjm8`CjQMFB$WfNZb zu7erbeHj|69oak|aIElbQewKy^MaJGDyrkgf%-I~nrS@G?*&FM1p^Cyo9n6p=8WpD=Lt1ijt6?>x-92Jap8V35r zI3%2`(}TCA2AGd=tQ`4IM4=JQN#YmjMAa3YQoaE$$gK=BUahUfmryW#5N*5%xK`}vV`$>JKcExv#q@DmWvWW%!31@v!R zvO)TeXn_ZDCYUC>Rm*V64ghNpz!B)T8;790OO<>84b&ADx{nKqr?zMcl#mz+wvV z2CyW9sczBFa)D#_HOxaI>?-XA&Vk17OIKDq)&n0o)y^lR3VY25hdXh7j502L7J)l~5F`2RBtYgnH@i4Mn{r>@TvfWvNO#~zO|lCv(o^OE7%8FC z7;ok7r4~b49zT?a@4nOWv+6>$TPL$05(j1_TmZbM*M;u(?=2gPjuH1I!W`44ZuyL~ z_Wey04M_C?M)%$4V3FBd-`J7Mp7D(zmBM*{g*P<8xNlbM3HYTtz`Qfky`{ntPSGD` z2v!I9xnwRk=FYrrAos6}aO3!*`Eh}7oFG(>zaZg>@9Mt(;HQ7f(o;DOFzn`MvaXxi zClf%9`}q11sS5VwxZ!9|Ir7X6tT>>GlY7rCMM1xlRr(C@ zrY$#9On^Vn-jnwCJssQMM$r%YvF8952zD!Kak60QK~E7>qYD6Z{G9^zZ?BFtp$~K9 z^R?|Pbor+52Y#%xaES@2mlxh_A624Wihgr$>iYOzwh-WJh0pB83Lw5?Kr|!Js{RHb zZa!MdO4HDj2VCLC;U^>$$LYa9B~|WRj;n%AVk5W2^?MCk4V~-(YwWE*yVy|?D>Rd# z%{kEE$ZS#3OE-!oH_6=DL3<*kGtW^t3@+$U!fcsmQGY*4VfT+8fh>3WO8f@Fe4*K2 z7tTDGXmhB=F+Ve_a`AI6wCnQ7;C`T(kixBRkkMk-#K)b929wG9A{$vjO1$sySuYYz zR1>?dnb$@R1*=XS7$waO)w~6WBOPa4Px9?{v+56hNF|^D`Tn-<^IGf+|7zFu%&njs z^d6Q*;)6wpD*{>0$iD18{KzL4w(5O@&w^8#Emwx-_~z?e$q8POyLi3L$^dVADkx-4bNI1LD1d ze+AMZBEA-@Y$Tu)RdaPY{4_s!rkw;eQzJp6&|b$lJcu_E)I@IhjeNR2P(6Z{YDiEP zKXKJ4#JOu_DdQ~YSw-`A?psf4>YALxE@X;ZHSNDqInAvc3YcMUb9%g6S4tLrMor?YmDdc}4F5Iat9LQ~S#Maxb^bg8#v#pL-B! z(2cCOwAIPBIS&wy?*Ld#XLuPmK9CF_L011mi%7Sn#Cqdb5F^oSUmzj}zAIwDj z3|t7MUA*<_v0GA}Q3r_feOGe+;F9Z(KYX1e@q|o%|3x0s1`>F6Cmj}Ag`*u#S1NGh z0zUz_yoPAEpzt=lEh}>8Jp`DpfS_-ncaSp3MXu7`TwVD#c;hinYBg#J@R_v}xC^IC zj3E728e#Gsy5uffG0eKn-`G8Ht^N;?vX_J3$?Y)MxAMtl(+lY4CUl&B!J+HkD|_mZ zV*ZC%BaO@#CW)mhej^|GxbD2adiw9oMt`MV}N4A4_8Ox0OH_P4slnd}64Kfre^sJvnsiYl{7iG-@1F^DNpV`Z? zV>Q=XnR^2I7C-&|A73acjEsc1tdlAD3wr#9*{63GRYiWaI{c$d-|{X^*NLxwFujyr z2^f@vz2Bc4raQtX2*%YVWmU@psLG$mG~D|yd2c??7@Enuqw6X$vIZ&ku(pgxr24ic zx2v^WEK~91b(`J=?C8D3X5>~TwSivtITLfLAH<4(M(X3^2(LSNuU5a~d<&p0XD0e$ z7ld_sjy`?FPFZ%#`;7(5Ho(t!&sv+vx2kc1Q$NJ}`(wW$_J;+70Fo$4aIcqTx(%vB zcZhTUz8M%27MtjDH`+5n`|nrRITib?@7LR`nV>pc>67^j}`)uOnN}(hd%J zxl@Jn)++xi+?ku_+J5F2j&;B;9mW02UiU}NY(FcXfqb={mVUUaZU5glKGARg)whAz z9if}=$bK?@!O5dUf5!nfaAjE=;9UR_$13l}Z(mzMb1Hy1BSx0@{fAKbFzCQ~Wn0?E zEeN{kR1Efvr<--aVOn99xMv-bYIR;iXClZHtf< zn(lUK`&nPkk+0`DmXBrWq}*2fjZY0eThFrW-7U$q z{c36z*P!zPrf#j<2U!0U>A9`)o^bK*?Tk}``7l{EcpD)3zMBHy;uah}wxxz8bP+>4 zR6al%7{YU8Xf>zq0{5?wQ_^0|7q^vQT}gKNI&rU)B-Z#6^uGdkU3-(C8Myr%32LhM z-1=952Y)L(b@^yoz^HG1rScD!X0zU0Gm+y|jk_h&vzOw&jJxH@Z2|ed z1MR&+MHejQ(oIMG3-0}P3fd-mJD#@j+-sN8ojcOLI#$^IdOe`?D?)%fZtBUdYjK6V zI|w5Zm3t1c3SGXWnVP|D%y7T*avt;clh!`@YB8qEvsA~{Xz&&XsN=sJph3!o zTV?NZWd3t74tiiGweY{Ol0G9XMmxG%zP#Kq)~6@C2niPW|ouiCAZd7S6 zGF5h;J@xc#NVn${V>PUJJoh}I`lI95^B>!XtE*k3haZ?NOrTo~;3vEmG7MqXTbHT) z9OyW(S~n%W&a!GQ5puE!!}HNK;sD=8)@?`Ty{&TFpCk^@&~v<>4y*gUL$NnB_b;K| zVght@roR6WG5&d#=Ppb5Xq6a>N&n{_{Pk@T9QuGOq5l;B&!7MMkMGiVQQSWOTRQmf z8~*#-z$F87{hbbX|DDeM`)wR5C7{1{Ze{R>(_aDg4tu}xy$xUs!v6|yTTRHH{#SVa zDBapW{;%f!HwN4Pzt{G^CWZcgZQg(EF;JcV-#QG5E63V$9Wqk1zElD#q6Hui=5!Uf zw)STXHf@Ki8=s5}_lz|mO#pA`t44~B7U(-9%f2)tG*pYROZ(*3Ii1|mb&&W$VtjeFJ`;SY!$W2irm}y$2p_^v~>g>7Iw5iVHyKR4lke(lWx9i$rGoc-j zZlfM5uR&vg_OtE%<_#E%jZphnXZ{mY#Ki33x9`3WhHE#1$vnolA*YXU9BWd3`gViz z2~6rmK8h4~LCXepfeC3kRw*|vEAd@FUjFThd?e1s&^z>(an|;SNbennD#}pGc)RTQ zolW;C455jZcRg_v1~2R0sqM&N)^Db-7j<=1&NoH#a2dwa6!*o7Z|}^hjKrzBp{|d3 zWq9g>nWiBD02Y#6O2_`bwVs}1C^YQHj-T5m?%QhMS&{j0xn7># z*mB<{5~i6+W&CC=_t#*#{Ob+6FBJ~eJ_IRKNS2=zQqtAaSwk$ zY8iR-TzG-B%Vm09rK8&p%J=c%UZFyxA}%F{?SHv`^g^1m&Tx)Tpr%j=>vGy*y(P?i zLueMN<-h*3X(8tKp6>9B8aAjkIxy->%}^@Fk{x!hKwc#+RXfdv*5-k>iSTgZZfs(A z;8Eeeds%nJ#3XNt?+JKL?8K+VgUPpTpx$L=9))vG(iK=vdoM3}V5FcC@ABuhzjo;} zTAH!Wo0~NHpKytvRGuV{1k=B@(f8AyBI4q8&yi-@+sR^6z)22wGg*-*G&%3rQ&Dar)W)2Ys7HM6E=f*pc zk-Uk|i31IG?!qbea@J+VcZ8$cn-H~7c2pVRZB)Eij&_-^hby9oW%Is)Hts})I0el^BXm$%x0g&Iic01tx}t7~UH7+;*WIK&JU^_aJ{vjKg< z^bUJ={rKTGNwuyMZI+t)d_VXEycW|F@6S8om%mhyjKGvEC1*!K;cAQZKXey8OtqnD z?-1lyVOe?A>aSN3Be=(lPk(9whoxh3$v!N~%=qWq`RX1!bCJxlq2x}}C6r8AVgaBHiX>*^3ln~ZMc zJN$)9Q)xrjNnwUn(Ze+n_Tt3RCzHuS0OBXU(&u^!!e*sk1}Du^5Eci5)y#W9FPB9m zw>ZnXwZ+yDkjxaLgu2u}kFbL_X2zwo!ZOfn9}^gyUz&o!>Pa)fVR{kbi{JfP3S8@6 zVZ-kEj8JrY&9QopAy=L4{STfnV$yq*Q7KsvyOo+fm^YLVx2i>3eEo8gLrls?%T$p-ov`g99*?UqImrH@U}$06P?PD_ycDd?pkLEWzpo+o9GH+;^Z z1+xmp2<>(1;R`p2t;t!V$1ZCbSq%{J7Yc&qUmZ8z$1YMW4Q3wutuA}pPEm%)v&MSy@y>ym;MZ3 z#YqECsr_x+zm})w4GlGA19me;>E@vLy{RORSWAU01JHV&>*D@c@(Tb7P=~hxddVHngdRB%`jrSB^9vBAJ-cY-5Zmj#_$->!6 z7PSHZp3g}EjBzAhpnK5-a73S2MwQX^_tZ7=5LOZa%m*CP?)bM23p! zw6StvMkA?7UWQ}`AUxSNFdp2T*X`O)LU0SpgbQ=+jt*;Lr zpl`0$cO^C)r@uE@!F;LRNV8!H@gd3Penf^hQLf!EEN<~k@QQk7-Mhs6RIeH#zW&{1!_kD| zi#~e%+I67!i|*TnW>--!hQes=qn9KG=$V=PP4 zr6(48p9C-FOlo5-Rveyy#bFrNqB%px1sYmi57#^q6s$LAj}sRh*HH!;Oh9|A-L^Zv=!-LeDpOOyS>vjN!41Hd7FM_R^;WKQhsz-17bDfiStD!ho z_}V_XG44K+nctkl>?r~i7JkVgblA08MI zFii0-`SOgu|_#V$)RqH?#1;x&)SJaet_mO*S?x=g(q`pk^FeN=o`ZRg; z>AYG9MZcOdF<9TZIogt)k)A}Hs?z}`Bq|-%3R=08o&$lRjT+G|7^DjU>xo*LUjkFQ zCl^}P6VeK2m6@JeK4~EePZiFAO|;!k@NqaGh4Y~136Y*@3*tCr9R|b-_uz&O<%2Gu z;fLrh#9Gu3`ndF2-t5FPM|FJ|MKCdHZ>_oncN^D zj~?fzWmX?T=;GiB`vMG?Dw%Bl{H$9H?wVtCCA#zXL&= z3*@;#my`47hf}ZRqhCo?a@xVDKFPB>=X$2Vq`(4=f*&#dTsM<+)atR{#Q&e)pqcr` zV(r-F>DTgX+3~?78$ZK-5V~NeOU3n#J2EY&0Qx9UAEyNu+kFon^{A35K8rMY2i^M8 zkTgqCFjwn&ucO<`(S{+>*=edG(6gdwsU|}WPRlOV8SOFV8u~aJfIZKIQ)h_mZ>$B* z;FzNO61K?!f}W=6`{rx9fVGF~TIPVfh#x}XT1u;_o>-KW>G+KH=y`>&fQeHHjiWB8 z;wC6r@vx6smVOTNzM#XNUg&W(OM3o3AZx2K4iW7d4_r8DVz}J!YbH4UD!x3CGQ&&% z7xVx{GKO z5gpF0_DL;438j7xflY{<4IRm#!!?M44U|)=e0n_i<;3`)FN0NK1+6F}F9NnT1<-E? zh2>Vg=OTtdg~h%A54Lv%ATD>^I5#>Fyjm+4OnOaFSXJi}9lezHDr>H;=jd@WYA&_F zG6UdDn`=i|J?>f`M*0PrF|Crob^YhhK#Ht9Sf4on|PlrSOQIq84$I&XN~-0J5; zf%^i$kR&y(cXzS$XcdbDdbMD0qKEuI@i+9s>V&Wtre#fDS z5rLeP5_6Bayt=?wlx1)4b$}ohjBDyZSAoM*2Er3CFRD?EX6+eWB;0 za(RX32da~6Ekb6{^T~A{*I*G@3~5SJY77SFNEO!y+0uP6K57z; zPfetrdWj?wUxMXn9bmH3fiS?7@8e=54nstPnfWke4hoTg59B(wn$%#2?pfs z=MpB~7Cn}vUN{mrX9Gf!$#^#(x9V%~FKOO58d3;#omgTG1BV?Zv)y|kW1pI&*@^^& z&l_wmu~W-8v;Y(>)~woiwjM%vPO^Q^tWk$;FZ-rEPmtyL=DShM-x~Y+z*)u@U5gW3 zEOQggEYLeF1vBql+%>DoF(ZPb#g-1bNMM5S_ShTno0V*y6FEpc1f2`;z_!gOzLp}W zZovYk>Jk<)D#_R|s@g0TdT1H{41n8Yn>_>&2npiUs#s5DG86G~4Ue4jX-Z?1 zxSMXJMQqsu!I_&+T&NEf>&bOcQ$OWyZAIWrh_nHwT44+d;o-F%20EAs)&!Me;}$@$ z#F7k##3QA>d=hDMx?G??kj9KsLCg??C_xLAkNR$XdB_3WeW2hS_NyPS=;v2w-Lz`L zr`!!wV#MI>y?&$cfMAbw9{Jr3WuT)kUdZB+9XDYcq(e^YL3|efyj?Y!D!fsTP2&0u zX3pQxk^Uk%)|S%~ZB)EUY@8$(?F|59f$$bOovnH-&3>PdO533exu9t9ZnwG0-->=< z{{t2_)4Olj*p>(8SWgd`=;c{BPP|ItclmLwt~Z(~x!p9lP`l`?o*&)y!khTUE6JV_ z%Zy?tv|F0w-eWxzKVWm$ig3!3djmjV?4J3|&>i=_=$G&$nAC)|HbhQ;uKjpJ0Ck8E z+?2g2x{JvzV?DitCn3<0K}{2+OU?cgYD4`|9Gbg-(lWR|Q_?DV_M{EP!rL z-5*_SUekxc43t?872!zFNrAXIwl@xj#WNqxNp%KL9PMgL(X`)DY}wIZMd04ud=^ve zqgWN0gkwsEJ#Ls*`Y%kE1{Z865b1TKy#c^>Mxqxmx~Vm4%A(~Ui9d5GGfe;`)2yDL z6Rg@d!|M9#wxV->F~$AP-WJ`o9E9Q}QgA8oPW9&%+PyF>Xu$M zn_!U!dUDcAdqD1Q)Exs{BD+tOpAzEdUli!2WF8*uFc?>JtuEy&%j(x3NB{g;`oWX) zrQQ&Ow=~XR)m$$OGg9;qi^CrA2AZ`S6jy^fVy-iFkB4<+l>1N~lE_yxawb^hq~f)k zowi`Vns%m3FMi?)9RjfcMh#81move7(5qN`_CQ3}<+NU5TvJ**o%eKCA|}n)%5b%x zoOy!!DIte3T3Hmg+U_}p4#T+oIFZmXQr`;7x`SAenX~1#(%q?ACGa z4b!%c8dBky_o z(Q$e!lU0(bDxZ#igb=QDb~i{BK;j$(B7|}tHl-Bv#5$cUs}qpZhn!=>5a;#t9x19{5%>ito6$n*-+KXY zynjK_b_GwvKDLCV&SiLmo9rs3GSJDfFMkm_Il!$i)G-*g>iU^}w#)SbX3AT$XyE}s z!eW1(p$kSxUCGfD7WH^nS3E(uLr34dNW)WjgerQ~i3qlYG${!hf5KrqI>1s4bNgaZ zvE`tI0+1epMWPN2os%E$I{^bpm?VUP*WH?x#;E`?Mvkfu5F= z&O5A4Te{@2BOKgX=|3ln2w|yENFd7N-6qcP|E`ifP4(#Ti_e#yKJzE#dOZzTI>0sB_T>C5xS*@l{zu6Zp!^qlx&sHs&ol{ZY{LCF;+V+O86-Fl$- zaMU@X329}CTjo_-#fbPz@zvQd@eDxSLr0C?D|u}IOe*zN>z1|?w4OzU|wYPh^Iiz)iS=vh4#t=RfD z7Dep>Z(=_65SX_5>Txyu9;yK9Q_cQtpjr?_%iJX1BE+-Nxr%v5u6W-4_2kR?Fp|yY zQ2E)AmnxShWBEuyXcJ>Ewa=|Os}Cn$*)nDi3&~XDa-F^|S!7U3(V~BCx??%YG8?X4 z13i|7l&T$Ir7)JV%%7bUXi7c9D>@2-0*EC>g%ux~7O7+WqR~yf86ADoQO7Uq5SS`v zlGFJ}H#56;JoL~w*-wwU%4QxLmt@#}3ose!R+SNO2Z4uU(-o0L$j9!SGv~@ou!W zA9h9nY~jiP+^yF0N?yfMxPhOq;m;?EdtF#>3@s$aJ%sW?ZQ?B|8|r6DyR4zgFw)#= z`*>P#5`!Gqe$nydMmW!$VTTZ$MLg&8=a?Qqtkp?oso^}a0Zd zF*kd{6lkv?6bU~w7h>4}di@3B2o~<}LJ3M578hqxL~9pKcB1j!rXUMt%E~K|?rR2v zv*P%6$;Y~dA*<6Mh_3s_;BzLN6#50#}qK=h@qbM#v7m&-2d=Mq~?)I-gaH1^4< zrMV>0vzgbcTeZrwxnHM#mXM1F2|X-VMF4k-XQLY*^JncA9P*?18%`#BI>gpZy(=B6 zI|F5)4vPBn`JRv404MeyBXpE7d|GCH_$#5WvnDmHioe)p^}eUs{kG&l2l(Q68XKlM znxquAh=6)TZ3arJdji_MtuI8)`&k4Qv`?l3tM{GiJ|xRw3N4-kyeSXvOUpfx$y^h) zskx>8cJoy`ke!B)OMz->(C zx?NS;%x#!JNKrxkSE))O<|-8kwo%Nc2xmHC=BH8k680n4F+TL@rTBc!X2t0Ry}(P| z#@rn%-``eYhk4S11>m^xDR$Qwz%SWn3e{VtXb-xymSVmGDJE#&u?RUSQyBv%=F)H* z{<38@DK{t@HL0$;INw;OzE}hpnf9Qel$d_S&|aMa4;&L4MDxAB(%3NIn|uY-j=`iV zbn4v?IYBQA`}VOdra{tjWEQ-cr| zQvRo722Kc3nM$IjzX3(q<|Q)E55Ikx?WE-K=WC7bYBnYnYUb=-N?4q_VrXmK>Q#gl zV?z(8Zz}#BZi%K=mo{U0Q@tAwb(-Nz&7jJ{o|3m%Rn0zRnS{7U>{%s7kYhi)E)mKy z5y{~^ZFUZ@X#E>$PQ2Ix%rmHU4CdWLUHWm5&$@r0_v8+206Es!|ikKLMh_hoYo%#baQS+j6TFMH`{1j|HW&wCzsrl~U#t;M2g@lk})_w6U&IP`2C z_xjpCP;A3pc~i#IUOH2MuHa@faWf+A)M&Q6b5FFP^=hZC&q(n;R=9Wa>gA4M z8|g103y$qW;t952-cD*WG}wj`L|~Sh!VW5pT;g;@j?FCi%|Uuxxs}Inhk@>Fuuu;J zxDI;GhRaOepx3Sy%+{5XoNUP3^`Kxr_~k6I!(KTZ*a#g^e47AuYp%*`*Dr2ni80yx z!5LfMI}VEcgP{3(^e4YaWh!nK4N4|Z%L0_i?6p+A?3>OrhgcpB6rz;GEeP*eJTbl8 zv`-dN>=L%5$1jmQNi%q3^9kU^ydekR%KaiE|4dWnz?01@;nChb0in$r_M|e5{`I8U zecCqKxW=NEZ0)YgK|c$vvx|M#O25)RAC`now@DSdDBa1ou#Ry1_dkRa2?KK0+n_Af z7!>60651iM{LbY>0!PhxiG!9r#FN+2Jv|QR>doS(s?%8uT+GL;(mI%1(o8z0csj7|Q|7k1!rIOxc|1ox5m=l{{XBlJL4cf>!NOlcXJnAnQB-oN_J>Vg0(1bexhnxb&=jy??r9aePI{FKswhH+-Wrv}uQKNpN&bUnIU;neiq} z`V$CYjL}fW`;b%dJvTE&i(!zuq9{~t0%`!&5k1ZyoNrqc7?FX_=T2Ig_}Lvek&@)8 zzU0o&{y;c&Q{XiMHAOot!XFjjCPn?b>KXoWlRgrU$uDv=VJH~iffq=xf_N515k&!j zTXZa-O{iyu~|j zE|Oa@6t=rSFKf7?y=QJ_pusVNuBBhK4!w*<_9;NgXst8v9x_; z7%~ZMN9g?#&Hw3cDSDFWt916jCdlbj&sD+}S7bj&$Ubr*QN-!)zgyYX3M`-D4Q(fM zZL2eq9T}c*Y%Ce=Qbw~1~0iLG) z)V2nO*nfcTFn~XwkjrUo`JZp^Fk|4q1Bw`UV@Cu(M>@R*323|`RXSm|t<{K6&2ZCq z({q?D%eC)lHcC*EwfR8gUi}@viJS%ektE+%x`dFh^K;iyy)r+BeK&8n*cOAfcl*Rn z=13|zgmP?V!v6C;XD^X&#Y8*S(;nnoVwg_+;i1W&oA!7=y(3;Y=G>0o@s-?0u0u(> z>|i{RJIslEO1TThHROGQqvjy#k6C$Quo2bLG5YuBI%w$s%cSrmZf@UJ`;+_xnU z-#28e63^}9d;eziF!++oL$L?fOQl)gZ*0o}GfmTfa$8&O+*?Z?jF10)y;b&5;)+GP z#7Jc}6}wGaVho~AAAj9m8|mhQgU~35;a8 z-9(9r4(5rub9MXkEilgiqr=`j!9tw#d+eM+VT5 zVxHvc4J>2as_EG}up`$wzykLNMWTw`{=24B=@E1k8abOCQB8IzQo z(8-@OH_)~4y`OcVVuqzN^RpH4lceGVdzed=t^AjkJ6-EtB6^M=-XAuR8)R+eSkv^g zb^b-$nzQz4_DpZm_-v!c^^_rxu2NKO{xBwLcRXJed6 ze`87Cs8kTz6oHnZF?{K-O#`u!@rSBM!~H#lKX0Q+Jqe++J4tcwYnDN+m3I@zw|yzI z6gW2UVJ%l^_y!5td+YI)1 zZ9eGuwBHw+9TC71IV`S|FV6hCE=`s~mh$FZc-`o$oFceBB=~2c8UH1(@zh&(HrS&IsJ=$Bef&kf2nA8~tV5r>E8@ z|DxX&KbQWw2f%TBSVwS!n3+)9Mzf^~yL=N0`L*7AX!L5wZ&`m4M(;QNWJz{MT~9@NO|zY>oc?=*4s2n>pB8)N?+AClkxxc| zx^Jt~rt0(`+mt{#QP;$2`w&@LWCfc5}r%BVZ>1wA z{gwpui5qG$AJ|q>4A2l0_CO^>htI|e@= zE;=wj(gYx2-X@*Z`*PoJ6pSvj<(5!)YmJwuW1{3LQ=1-6V#F0b8oU!Bzx!eFS6bLDK=uoLN5=E;BLvefs1!+6QGSCqT zUjie+?6JI}J0?k!he^Z*tS$)`us6szJa%S7r*=_oG7Tt5K7!jE z1>gR)wq4Nc>(uN1{)Mze=rgfG0vNPz14d2RajE%vcw9_Zdzf%X0tY7%<3Ts5y^lt{07>Jx&m84f?ztZbP7;MbAXXbi+B(U}gsi~&joMPD5a4KuLc zsH#b%W5Z;|yx(#`I?HGJGPf=13q!KZ78#G9S~tgjz;n}?*Myr+q4CiXa_VZ~%N#1c zu?X(w`B-miwUn?#aYa$o3u!8Eq4Q$Smiay?dWdSGInk0@jfj-0&xtj-YDXC8< z?#ci=+WA30WE>3Y@n|gEp!)jiY~4-k89KimoSTbwWYg4t%xJ?ZFueBxAOg|E(YqHE zJ{W=NO(|EYknDjd3dWVu!*s`q!|h`J*$G_bfP9|2Yi7EX?gA)AhZEPYQ=;*vAJ8fl z-*_3ZJw$9PrR;g~)p;OoCb)5RGJWXi zhpRunrKV5Pty1|ON5{lCJsNAI3iC*-lv>r1jHhEETCW;ZEDaDdvdh^qsm@2i(5g`` z`hAB^T3|bJf~QKXH=@PF!L2Ca{i>Fr=vENv+s{Z&0J`gMY>Wh=R{o}J9A30Hj_$y~ znc2Qr!}#C(o`qx7zsI)RZOVV9!=nnf6qPPU#G;sqnCCMC(6$epGELc_8LR`=9O;v% za?*3r4;b!GTbJ&|tq4GCm-um|!&^=%MvxqVzjXZbYQ2)|TXI;1ekAE&YqGWp7@>3n z!oelesg1%fV4J|RFM4C7n`%Q%+uA~%<<^M!d$0gRO2PW2e zLEFq!a`Dg8*O5di}?`NU|h2K+Z5(9?wqIx-; zZp7))vVw6mr8HFlm{XqvMmpzyQ?6L_$EY&KJeH(#N9}D2e~O}P%Alj?tFZvyi%b2& zm>>=*jvG$63NcFb1|CkgPrJi86{AUK__XFX+Kx7Z*yr7<3wOZT&QV@$9|vY*yV@T zC#}L&Km)X&VU|-u{aYdycsIvp|Cx+NG1S$0-zWq*`5#p0zq@oteb-c+M z2PXk-zjVO)l|j;JRfxaPzLbK*>4#tr65?t-T)FTHYrJE_aZI-DIHeR-SP^Shl4J8c z0&G!pmQ0}IZ>;m*9CQ$}C2@sG&xDQcXdhO)z+fBL$T`^&jBuAAl(75hQ?m zkRg%NqEY&L1d4%iWyw+wYLxIznIaZN3&FMmkA>G!`>?$0p6$h}P4kJU2kP`l4%Lo+ zIz!n+q%0pTl52OCcN|6)HrAtghHF|c)8chaFzAa#6rtT9@jrT38Msj5S+P*{*u+r? zmwswT5P3edr_t-yO_|eT^f)A9GwwoHhN63YE9*5J<)^j|G?mY~jmN=A7bpj-^F%1)U^3@az z5vNAdj6Z5o-92$S9fWBb8X7}f%PSs_!|l|^k6G1_Z5;DG2t8NKyzhfTMYIN-8qaW= zi?<0bCF2KOR)Z~rXa{iv;wC7Ll4?5M6r3ibE=*c!Buo<%uP7R$ioJ=;d7PR_!spe5 zZ8oB7-_ETdtrMD-xt&J&NNAAFhc@5O+6|%8UyebQxcLa$bqWfcvvB?%EhR%l&Z;!~ zQ1^PsK>SI{l5i9?pt1BmmiF_X*`><(SjYf6&LjG+=@+3_IW$1*mioE45^0ElRk(VJ zVIt=(=TvG=$n(WMfA;)c>Qj8&Jxb^4p@{4(ITNt?`QE9x?xP%LM@%+SrC(bf@j;QJ z$Bvdm%p0=N`23`eoaU25&znb8&c(C{s}J@q&J5Us%fcPfn7yd;c^MARRQMXah{97&>VeqSQ3s5zE&Xh7|~RnHh(5ll3xUnMkoX5>aH zGS}IWy~L~`X{}g#r8k^x8xMt_8>(LyguXQ04Bsq8Wgs)<>|!FXG$+Bz-l`POMCu|y z($1TH2sAj`=sP7oK|*qe%{pFz_g5`==lZ{OJpDytPX-X zI+^4+9t`Q~%0y60W_9g*J>vpFCpv2E2dlOv1gMfw7a)*do1G`Ctwgk?bIK=rJDa$p zM~>~vo*x%mm$N(L^!FIVvF2r8>fZB8kt39XDd!S*Dfz+|)IP(dL?A@Vn$lQl|9s?K zzNkd0d~(jwU;Ywsx<>8ps6wc$xKvq(OQmUl>B6_VAR*M#Nx88v9f`Z z!&jZh)OEoqZxpmAM@ot{33(S%o;a!kfgTsHlleWmXCvI<+PaE&4Cz?5gKGL)azOH; zA8*7mt!v92T?ykTN^wQ;WkBf21vd>$E5xbfBBqrZ{lWOSR>vWwMeM zoRzH9d=ZqASt(GBRJ~Ee)n@lAdbz);UXN9*a=K0Q6c^L5X9dO8`faIs;Z_W_)U(@s z)AoU=FOF=s&)q3aU%$CO3w-DX7^aaW_8?y72Maqqmj=W1fg$D>W$3ibS5Y)X#Z3N| zrsIQPk>SdcJ;20)_#C?Iq{(-jZVyhUFP^;DBQ^DrDq}!6zdC%huT(DTEr~a96lB+( z?@-iye6pxC8>f9Ts}|zoj3_Ayst7B?*iQ@1IsweV2n~I(Fhm%vQj4RM4wMajX=-Yk z`lcQ>R9B8K-HZp%Zu$7PgznJ;TfzS>UuyDiVLa4JyAe75QIPl^OH#K?q!tIqy$_Pg zZpXeTI{K_ pwOG=iohb0~&V62d-qoXVg2FbF*!3&SGm*Z`OxF4UsTgtAm%l;Fy& zn*V*%a_nA~xOweK&BMr12p=nwP7If9snGnsR#7d9>sFIafXDyE-g|~M)irIPfD{cX zs33|`YzU|zUFk@%(IKK#0R;ge(mSDupduDTdT${JvCsoVlp?)^-WBN`0)f!ZikgJ) z^}DX~o%8$s^ISaO+I#J_X3d(p=boAOzwsGZ$-~xiu>W^6ddgXw@Ju8 zoy75Ti*2v@=~?rwN)_nrqRVtmia}3Uzfgvr;WyNEL&#zBlYxKjK5_iFr_j8XNwWZoojdo;+5k)QcA-2IFek|_v3He{44atueo;y7+mqtfN7HBQBK3< zHSb5v?F|e9Q_{?6R`Vm4lr)rr9Dt5w@X2{9O>yxj9=r4?(ZEwPDFwM-Zev~%59;Ph z>V+fBaT(UK*&3J3Dt5EATJ>l}Ge;Sn8 zS-0dB5y;n=sVC*Oe9!y>66P~>my`Sz>3ktf-f8hn<1D#rR4u5Z2!0v014!#iP?WXU zSKWr!T3?3)Wu7aWbmb~tFmN9oSm8bSf=u8jfeguD*_0L=@tTuIWPpf_O z$+n1s^ma4#iEE9xd=z~ z-`=>)U2s3ZL>v_Rd2YY=Bg6~H3~nqyOid2?NFDna9J&=8l@>M$Nu$N2Mvb&mfL2P} z{o<<&HOZ$=Zd72(Mj;ZlZP@u)bXk)d^{A6+2(tg;q7_+&gh}A?c>w6=_~wb zaI6{Shv1VPw&4c6$aW~f3W8l_4~7hTC{67F)U^ue1tbz?LZcM-yhBF!%n#fIw%;Q- zIzG1;qJ*IbI&9g78%en6y-z7G+eLJrH4*u~BqmE24AQ5QWk*RK;{Q1u_DaeRA-P>S4cEEj% z1N$}}AR*#`4$(YLn}lTnT3zL2PT52q<@oedQ29;_9L`qAC*RiN4{#|*>bTJUGH*2F zm~IgQSqAo`8&rJgEQ4Fu9LtieKlTv-51C84ykt+}4LLehH|R4|M)C=!fQ{ z@Uu4joYDQnua{*+IglL+Z7k^!PN2Gav?>!;G6j@y;X%ldwzY;hsZJI*k3q-KuP)cBFubL&hZz`r>V=75$xP9rH7ZUsG&eB}yM z1SzRKbp+_e_QxKmAV5b}L4u<5#a-nLTo3g#w35<#pwN+!qaubQ$&G5mT9wJ1(ZqbarJ2H5C%mS9R%<{UrlovZ*?5h({XL$bEq@f@*on*Gn8(o2=X*0kd080!gI- z^^tvuGjA0lEZGPn`N3E4Q|WSpez}J+SyB>#L9%^q?p7S&fX~}cw5F|qs#V>rv5WszJ1FBKt2ha(T8=R{*V)5P(+XR zJR8#i)>}x8Y#4%DJ1L>Z7+l{mp}J!kO~Xuqfsk!cWh9e0>p`TpR(p`7;EU_nC<2vM zLe>o}o6G!dy3VOI%A9`%LUV}>tB=B&Vkt~a`KLtoYXtAUZ-0;hZY^p#z#3=wB3kIH z>3la>M<+xAVW4J_J@)l}pn=p_k=&Z?PaspGmE*vjHyTOq(9msWL)`_yN(YhCK<*aX%uHtl@udlsJb~nnD{Wg4g4a-n zxUM}Sj~;p%@j*~VUUxO)gNBW;UDtv_!_M!KOrArGDS!8uBPbC@7n}}6xDoYlU$z2M6WexI7eiURaYW_&- zxHt{GDJh~wdr@osK@aw&Ea7`xKspr4nnI(voDnpK245l|jSGK^d>=A5wmw42P5Zy= z{4pBH1j|9|2H>qBpJ`GjcI?x=i)dmkUZc`bh`pkuwto5U7b*$o2f)BpYloL{1irGq zc_Nwr5cDY`zwHTUnM^6Z_vbDhuAZ}7&7M$6Bq))BWCwRvhVw*gB{6RPRc<-WCqp&L z477sKfGSu(=%MzArP%{u4UT(cTd9H_WuALSBFnt`KyrLkSL-sQb{K{wK*6I2BS>KG z_iW#gNL!0aWUOK(oCO^P1s7`g&U44<_5(sF3nHwulBdw6?A$ z;)V)c5a@g6WSYu!0E=(PmdqxBi_T=VQVe_6L^+ns-&2T)C?`6@>MvZRFaIN&Mvkit z6shUW@z(&VY8f&t59x3Uk=1#S@k|DQFU&-YP6^l%1l@Mu=tf;&UMnwu1VtLzZX=Oe zGvk8TZwzi8x0Vz6nLw~AkCF8_QZO)xU}!qFI8xW&mIje;&&a=zl*VnY<%_Xe6KNDg zAW8?RHi)6D-8C|{$b&D%k_=m-vCB%7=X1xu9Q(WQ-=jcWQ^KxqJpBR)D!q>(h;SxW zD#~JvdUcf%%ez@mT0^wxfHv@-#DfYlIrp=XkXv$2L za#<3#$U)}+PtLtUV!~Hl9eCMFj}1uc&`lwzDnwtoB*TJRt5x9oV3n|{n+Rb>yUxvC zxkTp$mN5Q`BAqu9f^=kP(}kJBuRWTfuA`qJ$Z|ThhG#oGf@c2%@x-ws)Z5TqUilVh_?sz z1k2%!$EK^3`EENKTkDN^em@aZIB^r|jgWOCN&%Rrd;=K;jb7(yCSgS24^N3CpomKc zzWo6gx#wsg^P-gqB(vB)1kL3@dq|hp_+h3kmew@XYlJuJiq(rNbiw*59{@4MF2?_w zcTTgqUm6pkO$EvtJgHaB7mKU5g2x4!ED-~0IW$_P$xNrk4%0o6RIy!MYW>{PItJlh zjzXRWjdSmT9xnDx5I$Bz%B@JYq5j_@J#;KcK3|<+a9u2CB&uV>_(OEQXEduP*hDuI zBbX57z(2?>CeJpXz_pgg&#@VOoyj(j)=l?GY25~dye8ZOVI;$HnBTXp2(+%n#=QgKCyFsFtOgXe%1<@SB1(J86ojev0-M=ol@k)gV`5!Q+rHR$6Wjj@>~ z9n|LAG{|}A9nc+7VyYiqkfnrMU#qhR9vV!<5!!u`(^y67xRqtOzN+8*JR_kr&IMr}=NUjZK)w(<5ws zY!ymx`wOQOi;M02fL3$aX%`?&5_ev?RdCFAWytTd5J*3Lwu)rVp3q!DZsv z!c|Q=%5CaoDvcEUR4gkQ@{r#{3`R##?Na#d%7-M|zEIzEB%0FmuNqil=! zUUeaxRXr3s;~4Dm=N)uLYN7*l@b2BX*@1{$V1g!==FwUCX8 zX?WLCLiIN}Z%!us1}>wA6IPAsaFOvc@W^h$8MnUxHwmD_$Q-|T*Egn7v<$T}MnWw9 z&x{RTUh3@Y2tHNd+R{{jB(}okJ>GQ5^eOVqdYgPDSW5!YyqjJMXk^(-XxjMPqv=U_ zUXan;$~A|#0z4A~ye2dlpj3IFHY34;{P8r*BzvB2KHvunMJS)?5bgO(R~r$31d~_Q z4_JBK0Ox-Q_->%KlM)-Z^=Zf=%RzqW_a>M!gRu`**mQw;Y;H#MsP2!HD{?c3P?8}x zd3T2tWOViv0GO7@Db%}<$Oryllr>(LD?qdLxY$QbKW$j<7r8`NU-mk0QtaeQAckSSX7tsg)POp##GS`7Jne+d4RK%hS zbV)N(PVP|A4ZIs8Zu=g@odgPPdh@fztXeC88_5KSOvCANP^uz;inH+`W$0d(`8Vqc z|7H|&C7iwvQ~{f@&mQn4J^;*JAABeyTa3t` zt1*$5eAf4DQ5_YgoUj=pV-Uq*S$pU2b?Zy3D~4#+FiiyXB>}^6e`&g4B=dB0QUK$u z*L()#N}xXoRLmnLG@L^^)Z6r*A-O~3Sa^_x`NV+zpA`3BuJBlywt~>JssmkWoB5MS zwyGog7x}t1h%#1V7f^Qqpc}(p<45KKNT#-?CwY1^n{kgUcBrD?>iie&UZy``Isb#2 zz8TJYSx@qB=)6KOYe4;?^L3K|=sROLVnSqnj%QF4hP28PR`iyKVlO+^>oEb_MA0t= zNzZyb^oY>cY*Pe(6^OE=AzAWJG&i9_IUuN$mjqGC_U`+!8QdD-fe>ma>v-q+-{giskA=+Zpyr)$J4-s) zYq9Jk#4|uF(Pz_2n7R#RUYY7_0>ue`rk{rH9`9XnlYONdoVm;lEkb0fd#9Os>FdJx zk_FCGygGMUFG*7`ct@ss<(vC~_%cNSDmS?Cb@&2q4k6K0BogaNmHoYR}6s_uBUC z)mSGRQvdhY{G#<5N#s8uwv6CsV&x}@G3VJOb!g*kjv$j?k>a9nLrM0{`M5!jQPeFx&Z^IUs+ph9J_Q^Pz)4NfGnI ze*cG8p13?T#}8+aB4rc-U*{&RDgFoJFXkOHaX3K?uRZs9K;mIAuWnEG?~J}*9RfQ>Zg?_PdRGG}+|?5D1% zmoq{9W$%J{%L}pYb}TnmjZy#n*IFTer#v_JtDG@f)7lfhM=o7DPdO{xuLS%qvQ1AQ z8AtwV8FF%l@0WPiYn_h%7e|=o`#z7+q*2I!O1HV|={z61KaeD=tG~luA0!P4Y0JE7 zPPwpzuM>yxv_};G^BzDxPo27~5b3a+`|yB056O8aZqOUZV-Wd^rane_(BDr>?AlEp z6oY;Cgz{7tG2#~~XREc5myEpOi)Ts85Q#DWZcN$i-Kpo9NBw0+u6(Dg8R@5IkL8Qw z4?X4!qB47Z={|x|R72jLg}C6yd(~q8UGM_(pxNPXJ3GdfYnW!j zL!CMOI8CtUhEw8>#nmEiEhqF?&wu6^$+ywAF2{1KUZ!PEaan0GCX1K1`~YP9b)Ymt6id9eC5#gHKc_ zV*h9Hm=(xFcE|gQlF&4e6DLBPoTZ9d=FYh4pJignCyRqAm+c~jb0g2~Cm+eULW}-H za!M$o^el=zNJy&a0i{{Jd<$`Jjh*xr)&4mc+^pp3>4u(O-+9#iiEm>0WU2ShGVop@ z@2&E9MG}&-x0N@Ed%IvQl)Zz;_vd8zDUk;~4LvxuVf#5WzVg0%meaK%DLI)OUUk>ori zFjn4&C_`fsuocRMOgb0IXGqd|JGYU;nv!6p8S-1iQ$|}^{@JE$4ZfQR8*0zbETq(jWcI_{hs}UPFkT z(qKW8<@YiKx@cbgcNvVxZ(1I{zJJFYe*Ko#GGU9e{|w@FAn&d6EH3FQWmKgyac}Ld z&dvNgzhC4*H7{8WcB~Vx=x-6fb?NAT>IXm~BHw2gZJ7%9Ngqp1n|zwM5H~n;|D%5j zl^-uzO{%P^rp{0%=cx-Ox6YZ(9r*8idMG09(#7|O9`{@A`Q0W??xW|29r41y;!p`C zt=-4M`1`q(oI>tl;?`PvokDQ`BSN5)4Cw~+JFYFfLlfM^{}u?JoN1EBOqsf}7Wq}Y z@d|AeMVWYeH&Fe8hxer&p#1BG(*TQdcN>f1xt|zVFRMvSrqv8{0$S3PAr(60$GF(w`rXr zF}{dj>!L#x2s?}G4*!aHGI@H+xYNl!lx0vhA-<{wcILvrB7TuP$Takb6=izNytKbX zT=V>$9hb;o_$DUn0&--~wjHul}xf31zkMMJiL2i3$IkpHeWHTi1} z5BZ%rw_{;lt`b*!_SaSY9l!kZP~Kwl!YSjWlSe6YQ8py5_5s+*_J6OxSL8t_nAmG5 z(_`j+K>Q{;Bc8Q?=Vw8lpT+3qE7_D$C%K8MO|w^&JDTH*6PG(RnRw^3LYs3%Y zM6y@@yQ$ISwZnSN^O1N3#77d3M1R`IzXtU)ikZ70+xdWltUBcB8NnHE8k*D*`*-E! z$tDVo^oM&pj?tG{qGlM3+qg!#5dTOV6^*hUS)Gi>Uu~lpt%X2)z^>bpT+Yfn>D}Q z?l2MVG7+uZK}BfF?SEc#?K5eM%%YiOD|c-B5b-tW+>L+UBEN8$WD1=h7-P?-*cIRJ z62<4Jp*L~|dFQV}q2nc;quag=@7_~ngt?1@I0_xcWwql-{!tN$<7A;b+`kkYO}Tzp z?1_VvOP=cfTT|c)dC(K-2d631lTS<}UKDIjQNMyHYVPk(QDh?^GHPsh9tGkJ1{v^- zE4wc?EB&*06s;BeX2-5ipYQ`!rf<5q|D&^=YAFaC6q(1c6|<&8#T{rX8msZAE!`@^^pme^=6aw z=j7u~no(D5A=DLDM$S0ySVnEOMh=J1X znDmh;#lRE3x9_Pl@l!5S=KFrE=Vag5_LhC7?TMQJtr7$nj(@;#T#~63$o$T0yU-S{L~_i(2NMqSw-yDJuWHCjOUe=f5}Xe_9m(HBSB)W9Prd$v?*3|J^v@b)c>Q z<(?Gt?fUsmygownVcY8ar9Xshj0nPCzkmPf6Mn#xp|_gCTcDz;)R#xS#1JNJ9KAOJ zK^-q0kWBdK^5XYXzksoSu45*BCzA6QwsE6T=J8#f$VjLKO%FyY*{W+m1C_Toe}Rfz zubvB*Y1$Q~rM$ghAl~V6YsY<|8B5!N8ss*wE<2ra_2tvv_xw=)o1ZPz((H#-w3C&d z8d~{mtyx|ki(Q%~*r06r@qLaF5ugcxU_;*Wh)a>rW>zX}a9}{(qV=pMW=m|b=IpTs z#;~B8*vleVy+=ZVnQAe|?oYJdA3f6+c5kvxueGN~=FZCe4~<&T6w9i2eKZF49ppiT zXUz=4G2c>;o>&?adHl7~K09?mghZet+DQs~?s-vZ4I)5mssmu*UOur~@}>4pyB=1Rgthee z8evQ**cjZtP|)6IgL=~ndjEQP+ZMSk9CTkB;WKvZ%|k1w%dVjYghePeSd<_j#|yYm zfBcbzdI<`}n;pHFm~b4_5)*oXjacM|BCR_o@;)0YqOb2_aGo@Lz=kUPDXcS~S-^M7 zCqmHV1mU7l2FUF15)=?gHal_dK>n7PO~=Pl)Og*SbGObjMoHBy9xNnGs(kWVyKzWf zO8@YM=or04n*Or0bw1-s<-%qaI{7kyXYuBc`XyPtUePa?UF{kN-@(nt8adJo3Jf-~ zAopd%7nca0d)8g`c=z3WWAzFCyx$IR@BKur+uANvZRF@L!?i)%*huH|JB5Im<|v1J zo5X@$ab}A9r4Nq^dXY?$PxgD?2Bcn8|9}M6WxTH_t7N6;m~q~%oDI2d_;x==!EK>3 z;#b+ahx?lJ`t-F-BlNq1`n_a)NZx{Ryv`XE8VZ}hT2|=lVuYpFemSh1dkc2A20bSb z9)3>G(@I@>f%fR-#hS>7>NhgEjX0{G<1J0s6J$3ZuJdTb^IFDg)!5i*MeZ@!E}=Q8 zoRl4PyISUGMQ_;sF;@AFjisivTiL}cudJGj?T4Q@v}s*7;k%V}3yOqFWoa^5B1guS z@dsDtax{+}J-c*o`!Mdsz3I25@sDhG0Ce1r zw8E0UZewxy?~nH!GS9_bXc4#7uf1al4F`0ouCmwYdo|ie#ith{or>qb?84uCez?Ig zThRI(fE)5osd$t8Oi;zC85JbDdx&S#VIBzTJK_7ah0ql}jCQQ}j93wVAl?|i4dx@( z*FW_eU*h-voT>S=1mPCAvzTM(IkS8VWz%UeJR7S`|IsmuU+ zW$4s$U#R>GP_UyfCL&ZX&VP7(b?S4IcvOzG17~od5@-s3zF?rIQouSwW3IG7RTLf< zdU^Cniv-g7&Y+87fnBK=NM#&Ns;$&6+o%zLu{8%0eOSHk4sQXPPfV;+>r;!tu==ix zV`txEK~JL*0Al5?jd#8Q4bYyAC(i4ntA+z4>N{wmYK@L=Me$|blEI$Su*?-0^jXg~ zl6W-4d?$apNk~KGg-7kNVfla*z$JRw-HA!`YX?R9dZ5^h?W^&i5jboK^ql9hjK5PU zK&$|;lGO;vfVH5dQ>ke9xr7sGH^LX+ZHid0ftHe>%7SHrcZqm~O~Gi9(}d?@qtwlN zfGcNF^5RgoQKa1?&;;l#tW@l3?zwC*3nS24`L@n2)^#4Qnm1=Qp=O+ox!5^B*1;%x~fIKZ`s9oe_F`ncA3g6U$zuv7%F)*!KHMm~~Dz(Ixs>lG{Y!;o? znJ2gL;akozIiEMuOZ5+=K>MBRQCGIB;&!U&WvJ>v>UAEv%7Gz zqo8A~s)y`pesCD`#6jnoF6$M!YqjBZ!bbPw6CN45W`6_yfNfoX0F=n=pUlAXXX(Ko zR4qwse){z3SD$0ke$WwU8A88+lfH#^9k_EI$WH;@!+FY>n}O5q?xW7gk&W2x@=x&X zO;^v^37NLB1lY&>Q_nxw%r4{}ocsv5l3JkAX}`ssjkA|$DdwirOBO+21m3F08KM51 z4Ra1Zxa}J^*OC3)(51W5rA+DRn}N#~dXHC4y3CDVja09D)-(;q*~AZAMHz3t_QJJP zkDi;`1&FllVvTtU(h)oZb1=-NMfK~x4?hlh!m{Dd+p`i*fjoj25q&;qIAPNI7fhB~ zGZll>SJr~dPH!&OT{eoE^w`JCBZP~CcROn<#&wBFcW+1vAT(oK&;t6z4>j1Z;_Jo~S$ zxi)Wt17Zhje<3k&8gxNld3UH`m5ZkGrigXh=f$Cb?PmpioyGf($-cW=1X5|lLSHzh z?W4E({0Zbj-??nQK-=qG&Czd*cD!$Ps|^JYm)@BFQYcZx9y>j-sU9V{-0p_%VK@oS z^TV$U`u477XGm9_CM_K<5TsIDyLG+}<9Q+B=Ul%F2ro}ev_DBB^_T}7t%*D=xsU|%XJWEfm)9|J%zGtL$Qf$SSdQ+)d z%vC2spkKA%Jqlg<7z1+i)Cth^UabHNg0eQoXICFG(!~4XAZ9zj;YZ+Q^RJZ;HwM3c zFxB|}>V)F3?Hf=-={snCH{1XGSH@QP-7pL{{NjU^>7!LPuj(Tg(&e7Up$rOgCRXxc zU%9^lMrlK->bl|j1Z3Yy=UK1M95C%{r&c>hhP~r9V@ret`XIZak^Dlun51asP&w06 z`g{IZ(9m8ruBW^GTM(0o!Y60@IIx^Os7GQC&{AQ8&eL6E^`(~Qq(q;{G089l?K@^> zteHJ*GSe_Nw$T~4dH>~6k=nlJ17^FrjH|X|Cb||ZCVKL8avbmM8YuO|iz%emlp9Td z+IlfH7`h#JJE9rh_&`Gj;Qu8vlb5=dUf!JB-q&k8I?K7_Cd0H==e%TEi|-ZC7ww#l zPQXa*yE_Pn!=gkd?(2Egqc~&0Ir7|oN06BOyq6=Rqjc}|U2m$MClcmU&vnWttk_x8mxxgaNboem%UtDkp;02RWPgT zpsd%-v4V1^a;oHkHk1j*`0)AQ8Jc^|iLtZ#JZc`wc9_kayW;i{T;5K>hR*G*LcJS) zqWy^yNgj@`9(asKogJBjW4_q!r(Zlcqti^Sw0Q%t&8JF!;;J00LOM(COB~v(YW?bi zVo+?%#$9y3>{HNUx4hFv;V+8qgZ&T;p?wA^}Auwte|T+fVR2lMrEbW^Fl=^Ah- zXs^1sbzVvF&0Q^~uz% z4w-73Xc`I_1%(Ljo6+cdV1!&}z4RWAQzg?s0Jb<^#K(x&Q7y&l(YQIovMnmJ`AFJc z*W?MU(FTb@=9vR$rhgfVc?(Nju<6N-NPF1*N$AcPexp-$EzizZOt8;S;njVp)k*W}&G9ZKDj4^*H!!tU^H{i1cXWPSF_39G^Vfhe5CoJsi zw$xA%d2Bfr)!mli3fpv;Yf8N3)8J2Cp|tjZY5|!g5V6#V<{ni6#doapk41p)+=ieJ z^pPc%;P5_537ZdXaw@5^zb+HT&8PE$4Pg-P_ESp= zbXolzSzL;1T}t;%l*9D3DcS9%M+dA80^iNH;BxVCRDX~6RJfk89p-SXjHh}|K}w#% zt?k}YkTCEW_87g|I88a*~7;BaP!agR|WoAj0?Uu%vhg9dyFJwnRvx3f|A=)-T zsAunHGU!PDgRg+MMbW0=Qn$EwOO{SX&cLzF z-JF-lrf#i_+h^oHXzBCG%rN3}nQ{r5AoyhV_r33yBYK}M#=B1U^k=GXZ{ca(7i(g3 zZaW{p))|=Tl7~>-_e-pFp=$5?NW{$ylOiLp1?83JC$r7x1~%*G_Z*tBD1E{qK7Amf zZA(2$xWVW0#4T$MoCg#g8Y=js-& zVXN~Q^=r$y{qN&ZCBiL?k24&$Y@U>VIwp#+d_OCc9uz$*02d!?On}R=?`~T^FR_L` z)mY1aPBBRKVd(mV%V(Ft?o8|jW6i-hS38r<iI%KI zef!Os5*NQ?(^op}c5evERhMwrx#n+SBKkL7713|9_2$@nKi$gCktn5I{xBB1ReJvR zhvqW3q3BJ*l+Mh>GsW+X2E`wCRji{s%~MP?r`C}DQd6Bhvw_cnPk*B4*qfM*Z2`ir zV9=X3kV9P=ySbFWVau((eOMGRldi}lQ}4n!{cddsZ8njc?kg>`bI6zJ%Z>y2+Y?@< z8q1SLadF|x5_1?Jtl?STf!}R`y8ThOX%YN{W%(S6n2rvs5-QuB2|RR#VlXK+R3(l@C;QQv}%o+yHj+jvI9Wt8zEX;RXvQc_S@Kt(L zyj00lrqt~CfiTNYwxBdQer0N4Gw0zU&8OX+w5U&@sOD6fkcpam@8;U1^P+7*{|{S7 zkp!ROmfiw|fG2j&^{9z`-)=6IGt`;q2i|d;{&==zQw%kf+POR{X^c|d#Dd*^WAyQ7 z)PT#<*Le8nIFn+W49s(0|IVSu*_eEjDXkQh8N>UL0yj&C>zGMtssGnB%y56VRe z4VBuyl#}hl)HDhjuI3k%+V$Ubu+_-_O1oO(9e|g&liS{upWpDD$}Sq%(%)VfK3m*h zG6+gexmywNT;thHBEyDabD)XiSaD~wbgp`1BYf~}?HX#0P}eccUeeTZf7#{!)L@7@ z!8IM9YFBzxqAdr%tubfWzv4MJGckB?Y;!DRf?cH3#WtfC-kMrdlz-OX^`uZ|i4LC$ z-Zg(=%h<^?5+k*A5@iW;W)JsYuyAmXv@`j$lR3qbDj!*Rw`W(dhr*1VAx>gIt!RK{6?;0mGv>5JB_sFb zzEbz8mNQS+#9&7)VsdVLx*B>sycBfwgT=^dHXob3%UtKhH=foKb(h^i9Sr(gd|h_?aP()F-rcVcVB1!U-%eM1IR$XP z0JNo`K2BYg!v#+-q4$JB7ZtuO&vgww)$gWIu%Ro{tvGeBH(lMkJ?lCBzMpCyo1>Fi z<=hf$OYJiNQ+wUZvIJyDol1F^^8yc^*<|Uv29?%Q8(W`6V=U{TFD*6;Aldo8&4&g! z<_GE7 zd{r)5(c>!U-~MEh^Vt4Yq!6LR+p^wGME%*rsacwjge<&@-68|C*xh7>-l0^%XT^jw zQv1s{ow|!Ax1KgC7|kt8n4I$HPIcH~sOwlqy6H5k&S?Y1o~qEVcW5M<5XtIYQw4MJ=99urFL@zOcpVM+U7@=jW4e{yyt25&6YCR z82bbt%Q1p2wML>|m#Y1~)IHAEQ-HF2fJ{SWwg+C6SC-ken0XJ2jWqE;fHI_TqBcHFt`b(xGqQ*;ciPQsd*-QgLx zr#V@%cWZ^rRibj9H!j^`Krea$C(zMZ(Ba#@8MOjYcxdV3LiIVv7lSQDj(S3{Fpsq- zN96}@PRFzeJkB*Z3Y0dV!&D#a(Rm0*ie@!`8(&xD1i;pWjgD?s&O~Q1V)JbDs%MNv z-C_qlVNZ{HjF-Die~U1QpB`beNWUK|#QSynDmk+vk$3kh+6m|CnQ){3kaJ9iJ5O&% z0Su1y_EIlc?-3g__6)Cwz5rYQ+~pf8%*AM;xDP)T6ArxvbX3xH2eqxK3gBHe=6U1B z$CS_|omPv#o?4Vt-Y9B)Y?Rdh4B!G&7WIV_MO7-U?yFOYMj6%;*^>Q^O-AWAzljUn z86W(x{Ic^*%DIX`jx?8YkBPL1LbU;u%W`+>brCdJ82q)Vjtix>qa!L<8FLM^G{6W% zTnuDjF^yh?iHvk~U#!4+*MhiCqs}Y2QmSzlP4Opy7n`?KzAXkDsFl*#nAjLYhZ+oi z9xE+3Qbr$?S^I^ZCfwC4Dmut;>yn<)X3N!c_XfQkp9IaH#d>~O968qoU7j*I^HN&4 ztC4tp(rOJ=}2Sgg;WqPOBu?Z^Z7aTH@ z88IdW1MtQ|0o8u-&34ZLATYUEPPbn^*>`MJ;%uD8a-_{@=3CUdlmVPAy?8*)bZb_1 zsB3TOf!^84_NLdh;Y}Bf#ybwib>=(+LOftHr#7qx0CL#&_voz@e~JOYs`RLP<3KdO#vJqF32QO6Lm47B^xxM_k-1+8eTPcx32lFnB+TIa@(W5P{r_(YoKPdtLt1;U7Jzc5R+AMl5&7z^l*n9Xz z`7p-4RY=jI-mT&0T*4mCG5cZTSB{OBdqWN^jRnxc3HO$uNL_J>=Hak2rsUFon)m2r zff2g?=AF%#$#$-;k4gOwK0W~PRL{@0!j29c2{f&>?V7hfD*C`zZL+CA<;A2$NXghw ziy!csMy?Xar{8F4414ZjfJFnfL(i-jUdPFnZ?`65_5+)AxoUX7@%C6PZ~3zi%NmE7 z7Z?KioSi;gT5?X0%wd|Dyi3*M=P1}!c~rz$ ztUPzoPA4A2X87o}#9_nah&^J5whPC$#8-C(7xZ_uhv>QM^a5tX_b7AAn(9lfx?o@oi`No;8?ayUdycq?Z!|ZIHR5#J8~_4TA7Ngr zO>MirrZVG|JxK9NQ?GNM+7+Dc$x*bu#bCDkogiGJ_ATEqr~_bEDv?-N&{{7&FFzQ` zz+O`(Uq3Z7<2q=>Pnf?VINqPibO<+N=)Fq|R@~p)SuC?kRnBz{OV#&`wa!p{0G3YA zoabMDEW>Ztx`87;{Zo2N`H!h~w66K-AwpStdcX@>;CTi3-b;c_oP*prt$?e0!ZP zDcZa^SwhAE_o4FxA-t!YVN4pQzS1n&ExsM0qGxD-C*JweTuF2*F#I1%!plHA-=e+8 zR<8AJVh_l-O}TAG*Q*i2ql`O244{F%-)&fJWlErbmWi8yk?)Ng9;$5_^xx0T1DOY4 z%;e?emvBSDx_2XeDdUXp?9=_gce=N=K{s$S6acIjWj>btMES%ofd<~0__X$Dv8;Ol zT?$2Opo~i!WZIs)^giArSObCqc}u__CHe@APcEBi(TiLc^NpM~R<1+pW+F@3q?g-t zLasJ?-{c>|j84!5Kb>U~Q%k1$NkrV{lKL1C(LpP1DexddxLKxlT18Z=&1qKL6%JwL?^W|qZIeY^$I$28nmJF@ z1}CX2N`7@wxNEP}{Vq~hjq4SYn9;(VpRw;e50&T&ChFPpVbI>{WRXIm3&gvcBav5uraAoh$ydE}Xw&-B4 zHCf2CwGv%`aSTs7_cNSB0tCYT8n=FC+|5~fTyyN#Q3XF=#BrdaHrJVOD>nD4wq3%u zdoEv^z4gg978yKSNusKuE>o6112`P1TT#ICrJH&$@WO~@Q8WqtfNB^@c-`n4IO1aQ%U#R44a^2uI{xvXL-O-!$sdqHXYN_ zc#&aPMQ#hMHQNARxQ>oF8_`ix6m_mYBf&1DMh|ZvHW7N(!k}ccT^<(iBZdql&(gkBbD^FVsjy40zLxIIEN5_4OUHn!AB!v3e;;jZz~hLq*0eeIwKYqevY^O{ z%L;I(YC5K@CC=1!rnL-y-OpXN+;JSWZ)=@Sk6~Z*T7RF%X1k3~!3E=2RcT_SE?svO zfemUnPpsW-?4s3A`RU6oHe1D=T2#XXq2!oJ6(pv z;Kov9m6XJiq^0(rp)`;BybHt&eC+Wd(G%j6~LgvEb1`ks5zYLqyIDr>OufEBmpnSR*lf};xJ|#Xx^T!m--g{NL!t~+ z4fs^u3{?8&u$pO^e4Bh6Mu6y$mDwVhPU7U?r9Z=ebiCk_JHf+s!E=+aPn@aY>_Ehe zt%A>+&du)L^9Nyz1-&yVOeKA(<#OA{kkM7}CDtaNhG;o_=kO6HY|wF4vL?4@b6fq9 zB-H819!mf~=uh$WQvAF7HbXT=FDJe^yT&17N#M)(1W^_z)y>M0Df&S3 z;YF~uO8a8zO`6*r_2;CFHV+FL{}hu6UosJ~pIz|(n)6Bt_yyjk^Cg{vJez`rlbg35 z)lS7;KjRw#v~h8|cG+w~gsm~$)q}IpJO={;Jc#Zp)h#yD&F%aN%r3h@_@<3~y=-x! z$?5gEFi?Q!N7|kxpo^7WK0iw4PWy>M9`E6P*GzakIhE@xWdh8oqh>V9=x`fQs>i&F z_nJ7pS&GWO`$J#gXX*##m`iwS0U#s!q%I@DhadD+X@%NnlzDsh+8gH-CYMDP?6qa* zkXq<>b(}k7vA4qXTRZ^$pHyAAsTaz={MPW1LfqrtetN7}n4zoD&4S1&AWJ1$vn5L_ zhEojty>35Yf(u)ehR)1yt>63^YWWjZ06)ud_UDIhZKhWBg>RxeLD=i$**K>+5q*7q zPu(MjFHD8x+jr({M}HJLT=l+_jw4K|n#Yu!YVho{ulw!`Gd<41MTgsM_&6A5VDNLW z>S4mh(uGlvRc|4aNOzWKi|H;Q3)O;W1CmSOAsoYMdbHL3H}V^3BP(!wPohd!o;2L7 zZbUCN&T5x=xp#jY_H_Rd7*W4G@!2J23kCv84#1`A9=W&XAr%=_QY9~1erTdODW(6+ zxvcj`>rhXRgWhB=Yat^1-3vq0SNylLjnL`>rJ|O{VR=Nd#!>S{!){O%RyFq0^>{Cb zd-d_eC~)<0$K38QGe;TENYM45v!mtAWNTVj8`8P#xcjfyF0nY!o56D!>N4Z;ywBmL zx{(p#!?G)&*c{86OA4Z|3qGLHeIDF=XNZA`x`A~yQTpVGH!X%zdY>k6vVcfqBP|EB z6!{EPL5~d%!m7R;ngsX!U+M#0@;FTF`wACaTub4eTWeZ? zsbM83wS6QQ)KGnElao1TyVO*;uq9ADe#Lz<^Xx{2LhxwagVwIlr`-Dj1P;U+E7Ua|99#_e2T~)r-Ldqb{17A<~H~6i#oPDM02^ z%v#U>R+1KGWiD^tUFfX^m43_@_X^Mk$hnV9tu&qUE%aW;Yok{NhX+nMO?4&96R?W$%i+_#-Vc z!sOcq`WltcfY#j|8FZ$ML=qTvzTIYPiI7ZV$qe4G@40>HZWuN_+-cG&EK)#X1O({u zeK^}62Cx~k2+X!mP*IX(Z|`9*Y)|h{OpD+gj!}9$b;QWiR;Oqxr*{NYWEO@6t>6c) zv^mW(RuGm^3!FDjdwk1v9LpK#?Jb@Ip2AO`_6&(_k8cHDYtIbni2(p<`KSBlt&A6V z^1Q~A6h_tN0|yLR1p3t^Hg!N`@K(W*C7a?Sc`$QyxChp;;|{dntRA&(`gq`NQ{+Ol zdsnj}05dqk)Hytti#6EAig_$b;ieVA8I?}m(OC`A>A+8J&9kw*ygqHcWkkv5Ie8+n zR5FB9MnzT6&AvCQGXc>53j8kO)1J%yXpED)S@{e)jRf2Uz_%(bX{gCBQX(XlUVZ6V zpEnR=7mm*|QFfjiP_yoM|05e4b#cwQ7_@JyLAyAWGa1b#h(BDdpuYilO8g1l`8B<% zBLiNJ46w^BArpjlT6VOX30;vB`{Rj0463xMZE6lE8xi~|gBgo`N5 zFQ_B?6UHMh3xC44CpXt*ff&HP5=lTO`gK?}#zqNsN6tP}dn7!Tg{EU1cmo1%;udIu z_o61M?Wn=#|A)OdkB55i|HdsLl_*IiQA*iE2w94v60&BU?EB74cG990vNM*-zV8NO zsmLDL_nGYL82dEF%-nD1y3XO8>-v4~`}g?$^ZVoe=Nx)qKA-pUTA$D7t6*?xB}SkS z*E8KO{U-LO1T*)|rH77QjKf!UG&20Uvu~dyYatwsHeQ-CvTd#x4tn~&DYIy2TV6C5 zI$BLJX%TB=+ty0>uAm$0mJgco0L6CA4FXf^U_~aVLY|)&F%M64Z9|Va3fJQ$$zs&H z&>FMFBR_?narj)%%uIZ$#Z|6=ye?+gfMr6wmL%cx7COFqzd>asnErxPCNKza2i&tR zN)X}JuD7*gKwml;=I#rEMIiiX>8<}@)A07jzSCveV+Fodr05pYM^})hBh~IAYrEM< zJ?XgE)KDpnJqV+V5Ng~no3uznT=!- zZi^9shkibq_Re9nVmOv+%XkX(`+jVtTN=OI1%DUnFPc$_mnwWwB`5g+=yY0DM-odP zcGj#bAm^Lzl(<=(~m z(kwal4as3i_a#{PviktmkO0JF4{!0yQ%9%xZpT|+Uk_I}EEj57SoAqXyaX}~wm z3Xw)Co{%FwOJ6SSlC{a@quHvC8!oM*)v*su~C`8$4czM4C0OTsn4(&EDA3O6?ZE>Jh-hDpsR~iH%_+)jCHF#uZ zt}narP-O8xRGie00S?~jhM7 z1V|uXaJk5JhL>5<@CuoT^*1XZ8+km4;pYK^7@h-rzyn~WO4a=bA(MM;e?@sf2Rw9K z;FANM)Vg9q&@+Z{qug2oPw&^e*peyE`TKLDN{qx)4fIX7rdGmz*Nuhg- z@YhrQ`IY<=a9B;!mH(5m{O3>nYwHdHnTmVAID7w-rTq8j`v$i77~kFh)SdbJk6q#f zZ$U*D|DPoDzdt0C4%oCOJU#!oV*h*%CQ9%YCvLX<7ma|wElgk$*tCWR9}e>V4_@S1XwW(l^^X&6|&X4ire=4ObwK?iErXU?ywjT|gyhjvEJz@W~4t z%xhtCr0~MJ9sRoBlShZE+%nJm{hn~0{-6|B!Ests_w+aVUuQetXdQSmKuYKXYjux! z>?3jipU(;~?e3@ty#@f^i!)zLin`A!gH~6+@a;w!Jj>+H7MuCmSLh!CQEuXhEelx$b949088h_+gQ)@#ovg{ubXAkAt?k2Oxb-+xp)_S-GG(Xy(? zU_vanp5E0~;09jgFuvK}P4HFVM{wOg9&X=%?8D~aUm~t>1k*N*x$i9ai$W$?ItO8B zpVCJlOPYEc0g@Lur=f+DB)jOj79T*F_=AJNs~V(4O}i-yd2r!WBngx?M5b=)lrJUR zu(9vi+^TRIDbri*tNJMjarthMQS%8^p%SN8=ku}yE`iob2e{GgkoMhy+>f_54E#)% zCA_x{T&Lc%+YbP}>IhnfMV5QQ_|A>3!OV8hU)!tJ3kP;WT{d_++*TKJ*;+wAZw8DQ zi@6&z+a!<@ec)Zd&KQGZv2oH=`ma*~bUxIdqH4Z_>wxM1sFUw@WOqp+wu%-$+Xb0C zjfwRww@XLDZ)B*$UFzJ#Y*)gAP zx%w!f9^mC&DO^B@G)*nVuR;I}DvA>A%$hE@gIONwaf7u9ESJS!khe#ZEaN5JVl0M= zCZ;OSA7Yv+Df*injEFM0IJE4?a-Q#-4SDr1Mn%XiFn$qM`f?ql#cIE|_3m$UikHc~b?jq>(>OMAp4N54ErCGPsbQs)glipTZyi6RyW<7z=+03%bh z5x-vl{0QgiG^>muYnitS6vfxOZi7xzT50q_5khr$nz4;Q^R9~z;v|ww8!q!#A3Go} zLJ`<;%6V|20NN@zW|W}i*K3rVy97$YAKHb;rF;)C30lb0E9dyWFmjSJSO~;D9s6Ze(Nrk&%8jh@!RI*fG2U)(+4ygj}X9n)CUXzQ4I= zxqk@`8I?yERU&U>?`9xx=?)s#dbP(13ys_*CamJ;l!k@RjBhK}?_=BV10qF%0RrXD zfP4t%VurskJ7zmrJYux?n#1q%UXf+5lfQ><*T^5ydy~T@hz3@&@13@1&#w;JI(6`_)?EX*u~KR=F%$K^{!$=stE#u&=GvZ6%rKUMC>VCVC@5iRDk(mpX&-N+31E^ zSo>QRK8vkI#veD~bPsfNViLVqhO0V%0ODBRx%o~Q+P9wHprT_|)T}9YTbB&`LP@u$ zs<9*>-DN)aF{U0MYJpCFzQ<1?%ko@fnb#mnT9}zhp=XvKt8_`V=~Y?>)GoM?599Ub znKRjsrwsA!5YlAVThD_|F}NteTQ+=I@(>6pQaEqyD_%V9I#PKQsvBa?x+3_9w|+rT zZ3zr&s!#Z(&QK`b1BhI3iu*I_KYkWZ8$UD2n`aFH4d?CWydbCqnd}`g=U(8@;)+j? zSAdsn$$G3dO7Y1cv!3MRRQai#J}QZCyxLH4C^CiH~^*c*!l{TxPKN5MKGl`b%Fz-VbQ#zp-KWkpYnFA=#don_ za$sKq>PTsy1BX$ygf`HF3f04&-ZUI^%ksfTj`LzAz-10f!=cdz0De3Sdh~nfYPU}4 zHYs#>biYKfXWXsnE9ao#Btq== z000;XRqY0fGMrn4l5e=py7TA?wiocWP)xSn*{<1H+*t=$v>A|PY)L5X0vzsS5rEnn z!`(5UOW2OZV86`liv>j|d$~lX$_oompoXUhy z)!}E^O1fkTLtdxMDc0alb;ACP?`y#US7_V5;5GzPRXKwZf;>xLZn~1S<##N*}spN6jOTgK8yl zM5DR1ui)ucyDDp9Ew=7LPsSrYp>_l+fy-f()GKKBb9t@#b(`m0f0@xIFc7ZMC;k~s zg-PIyyh=J)_Q!l5XBgSU6>a;a(0)AGw(?i#<#!JWo4!X$JVPA*f9)EB}-ED!b={kQCdn-1bPw-|S2BU-o4_iM8y^ zUx^Q=!lAu=Q$4V6zW-)44%-rfY@v5$$+3F}dlW=r(|ZwBx~&8pSbCvZJ7tFJVEs^` zOPOy_zOWAxj)`)R@qwa=ei7@R6iHhd@7d%c>&Zk@WFACVHThQS=&a?vFh5l=4cdzJ z>c4r74o9{OhLZK^iAS9KvN9)W;{iNJNXBc-YAI*jUfibVS_TRv^+K_Ru2l?;3#`t- z^2|EaPrTX|vFbEL-g23|nXSL20?<#^OojI&<)|&cQke2u&`}4kEy_BK2d*AYj1b+l%|qTQW&utnR&ANa-s_ep<`6Ry=}l zC(tvE+lGX&e(7P@dHHJ`hPLaSe-g}Us6N_B*yv|@Q#VM*OACo z@}ULoY81sqGUdin_kMxk_H$_!H=y@0X=aAW>R;UPFm>)Jz6b^+S?UTwNXbz=noXU^ z0(me;hL`#3?DUB-9Dr{N&vhswrT#q7$m>Q$zXS*~S@pXB2>ZacFUvyL$a7|9_ULz| zT{y;vmr^k{KJ+UfL3~ zrxw2Ng~9XhKiz~bpUE~R8j8-MV>L=GRj*eVAuG6*y_IT=e&E-vIQh@WcQ_5}hokd> z3+ySiQVk!1&@+@I`K;SOwTq4##1;S`nxGJ-WOF9=YCD)`RqOZ=K;`l^Gh^OeNa`KW zH%Pe_0H}i;t)M64+`8F-N9Z!=SrH*n1JQ+#`{o2U2DagCg3SgCo-@SZtWu-!q&q()^)Nx3AjKq*R zp!6evUtg+3^6PI_82bBmiUY?KT~f~JPq9`pTAko{%jvCwUzKa-(ALYd^1D-Ea?@Yw z$XB{#a6Sc6iO(kKQNJUQ%<_HjHrv&S4@6OE_zHW^+8d5z>c^$#KR&zvo zeqIp}GcutA=VW6scd5OBk+CmhI-v~yV-J|LHF7tXwsJi|AK}Q;pOa)!XKQH6Z!ok+ zz>Yn8wCK^8(cBNXP`&}$8iA({&W_5D2mg~Q_~;jS&WU&V4}cUm0_BBVpn_Ek7_);d zv46fC8~@9mhRqP>F*FY75Ly1BnwfRBb>T@|0LHa)N4I(Qy!5tbZ}b9bEurggHKO3) zznnmD;;|dd`%Ylv4=14amlK#<-1$Py=mjxI@>=8(aTwvtApEQyn|)DFO?xcjYKqT@ zBQ6X+_s7z7qhH#5UL%~;=GKcR`5uuD$g)W9?L~_x!N!AaW8=W^S7`?{sW#DLKvKNQ z&o>Klu?Y^?z5{+qnWG@OG3s<0t}c`dxcO{VzG^l>uKsO5#?kdF^4kuTOJ`!0cuU@l zcHWcQdvxhgne8XKH9lMYgWSLT>Wn(oD7xEQI(7ln3BZHxrt=ZAZJKKD%WgZ5qmVGQ zjp#WKQd-AB!7=&9eS@-`_OWXJ#>8{&8ITuNfeW;5PLBQduPRPt7H$hDq7HcXcZkj({07@~^divmuiQqNse z9iHK$c|9QR!7vSr{3gO=p$9Poq(j{LxBMcL_(pseKw{!B@@tI@+Tb87IbQPg>m_6@ zAaG3&TPbHRNY%7KT-86`leZD|(FjP1 zFA0BI)Nzy`;x^0gr29y8&&OzGe?|!E1#k}o2Ll~0EHd`SFu--Ad821KrIB!<$+q@T zc69qup_9pakrEGTUrNY_Iy!pzX1RoxK)MUov0XP) zBiQM>1gF>P-92e@iycOxHa*k&c+g41WbDDE#Ssx8yS}$q%>fgcV}LT|h{I0$1n41b zJud?k_6QIgdg<9ZZv!m~W_I){27&$nz!lF%-2wDhC4CuGPqr6&)j;sqIk2(2VfPm5 zQN5RySP<`Q167jXvqGZ3%h5x{Q!2Q883!*0Q#&T3t_I+!M=6j{mql(FoD(uX7cV`G zj+3a!ouXm#XaER{NAg~2k0L>^d_HnumT%--3ED4Raa<|PZ-9;L25v-0Y?z0=KIv~h zqWjceKH`U4kj}n!Fb~-C5!(0v@)6HHh(sLv(P~;Os0#^K@~pU)+xAa`lOPT;U}q_n z*}M1RKNN$sLF+IY^t76$&l(;Mjf~Z;a|9|K!BVH6Vn+ONO{s2#1t-vcO>>)r6LTXv zGaE#WX*TUDato@Z9N7%L82oG`ilt`L{S&U6M9`0HuNJgor|sEEYQcmSuitqVS9*7OyA0`UZsT%5izSq35? z)Sw;dg*1%B50J#gxd{?Z+yI}xE`D`%_M}ZwI_R+1tlSs}7{Jo@$j0 z;+L#Kko1uVE2t$@oDL;Bx8e@d)&&@JB<{RyZjKcUjCiWUmA&XKFkW442&<-yW8V3F z!~8g)+gVe8?vhL40h6aaLbmF{WVVC7&#$OH@S${v)0B}*gD^B=G(Rz4_n+UVz==cG|G1vZhSNKLn;pOyRSE>am;&d^2%(I*EMsb z{*!DCm0zAy?;p*~#HJv&i2k5DCL&t0&L_>1P21kN{gn=@3^L?NUtN&eTV*smU{$-9 z3?WFUgXobpNm1MDv1{J?HK`|(VA*#=bk;%O8bbU2=xHB}p{<;UR!-=h#OoKG>pdJA zWqp$`-P(LS^=DII&-%D&22iYx`K>R| z#irjyG|H?eF4Mg^cmBf7)v&Yg%NjyA*HQC54|5-WeU7iq96H!X{UgQ-bdv`gasFA0 z&)xlvcqi~n?uf8h9YZRkrb4{;Kih5LvNSyo0Yju8Ng^1+HC+r*)ndA{{YYi=@(ZCN z;;*xBo_YCxIf`0Pzo*~-9$o&Ro}t7ByKvK?rY9qYPDgQj9U-f;o_Y=15`XMRc;A^fEiDShH~!;UIOT8Ap#s4uGC!gWbB^+x z-QnjCZf(vUkFUN7y^)2OtUAS+Gxd^M`~}N}oDcp1S#y&|PtY@IzhU_I%iN)#PY4F{ zS)+sKQl}f=Fk9Yx@tfR`p^mX=jkxPN*C}xK?WriB^IJ4)a*e<3Ur)1tL~be_I?U7+ zNXHHdxVgP@vS;d*DdiQ_l=m^ujY!AOB_uthoU{u${-D_gncw?+;L3F9!#iQYKccxp zq6DL4LhZ7cK2QiBXTAE*KfC_{@|@8$hnY0b-V$(Rg#Erlg+ZQDH>riuY>rjh_A>9Y;|?n?WoIlCfIK1aIjdd zXY*chRi4q1w3w-gTyKi-H^(GvMqITzovkN`D9bZa&x!6lJ0Ekq3r`n(<*0e2yn&K^!2im;riZ{q zB}y?1eE9C#QqDRZIVvc?IlIK|`7o5CTkaNrqD1bl8~?0K_i+&_;nuJ>wExqdy`hSi zl^?UGYVcB!XiaXgqSi3-zI`EnwD$RQ%Pjo5&HT*?IB|WN;7x6uc~xvTzNeyW%u_G$ zrbpZ6lQ~-Va{&$#f%5fMR-?9d$6mxnb)iR>oV)f#$$InS3%FKU zTq4ZM6|T`!Y;ppd9Zm?OYHU8o3RUV|_&)A*?a~xhaHcnuHsE`5Fh)0jy=r>>LDfwE z%hzLGU#S{dXF0;JqQ<8NHqcerw^SmvzIwtBSAN@L`QJwnGMAmZ9@# zS5j@HbwGX5Z1@Jh>L^CJCXAE9hVBUYb8ED*zL!qQLKyoH?lUkE8yYgW-}x45>Qz~- zL*oe$<%a=h(3&6rv-iEfej56mnsmYE&JCO?x@i4u{ujzvy548!iR9c*KXGr9T(zM& z&3Rant#g{8mUwKJ9+{%pqn2uYSAzVzkN!00tavJeTB2(c*N)-j1^cr`lFRb^>H)ts z$%nIlZ+anQ7!(*)@)nur@vj34#~=^&1!v+>)n!ej#){T)+K3MVfFE+a}g&wX^$|< zEg2fS!aYvbS{w@gx+(d$2x~vf{NtbJXcdEyj0y3T~T7dWq=2 z@~u+yLwoh`te*|UMaJLJR1)w);>Hdb?7*@{C$B zg{tdz5k+^l#wY9JRi9lQ@#wZx*F zzu~QOUDI#Y<9Pbcm-i*M-g%GvLbTK8ULzG~gGrPh#WOThjr~oG@%<{)NOPlQVp*P9 z!yS>TM>gZ-1|OG_M6j96oG3X9WIXp)k%3mKq2Pl5LzmVR>5!Vz^76y0o0iwqvqpO`O5u{$qz2&bA1?{EBneic}Ds-=u_veD_dG6H9s`P1I- zzAS$1-EY?lQOkM#z)pW8*Jug+?z-xu0!yV|TSW7)cX7U7vUzeBTH^SG)Wki~V>DLY=RATLp=lP^wzH%@G);D#O7j+#l%pxre|Z0lR` z<(|h-OvXz10(Yf*1>ANzdf2GpDEv81u;yv@sN=n+N=IzJNTq6aD0ggmt3I5mE9N?t zMDR$c6SWS=vXca0?0ih;5)LzZ zU7|fsRnnTrkuFFC7A3KG6{LCXup!2d3_ERorp-S|~ zd!K1N`5wXjF;Z7#ESF;(=(;MqRvP*GFY9g(tv1Wa_o*+1-TvTe5}&vt)~WiDV$F)# zy?^_V5t`(oaxQFq((n>1oJUhPfk(1o7%Y<1gVHZk#kaGz*M5d*Uv}!uTkliyHt$Fr zV)C$zb?wR1@e>uZS*2Ke$b1~NNKeNX+s0$}O6R0nvK5l>6E1DOI5~8Evz7a4kAWjDQ7pkk(KR%p#C*QZVQxTfItf`BIr%s*28TN0Oev`y0 z1}8nG{A}zx8e(8E>!dWEi}uMXD|1j+ehY)}5q_MDs9tDkF=)`CN9aD4otzP^}?{} zPK?TPFw@N24E?G;l%GSvfS{m2aq@{^DWf?d>PwSa{S@8w*CYAl6Kf}f$3%po54v~e z=3-93VxsP)nkh-k+CT3aRH4W*$f78oDoa`)`K;hre{~$7T5P7-T5Kn5OA9{379V3_ zYm7`zH#E!rSj=;D$YSTl*Hudu(oxt{CWL&%uCoq4r? z>jv!Z_ji^KS^`jpwqG)z_GgX#8K)n7L&?22T;%*}&rVu@%x8J8YkS+?c%ESuPGRcYfL(lDo5buQ8A946poV%E|41x(D}P+fC92 z`@?yj=5C>XRX~6-l&;N9US(t=4ywBgiAa~Hj9;#Ie1oyN+4cLRn~+smDk9%`yB6En z^kMFy;@bHM!_!VlbU$`toCM#*_lSA5(X0(*QXPQc{KJ79<_wRZiWiaBDxyjgL{e8Q z>CSk>o}~N?on1q~THBAB4nE(6sw?!ePnl+FGO%!lrXV0*K1GAIx1Ri-s~4Qe2k}Rq z;0(rtL^=7w`@w_3C8|Qbq2-tP>))}(y@j@N<@F?kk5Z}oGFKsZxJ^Kibf#y(Ef}Qe}Y#pi0t_}U7**HUK%aHk~PhsM`mvM&1Ed^%3``c1o zH(umBj=wJ8yI2rnik^gcOWS^lDiG=AIjcmBBJhXT3Z9%1XQ#$V%Bl?Nw~u6>sHxhKngNkm5;UIpwm zn}IdyYwVfdL1mA|(;PVY3W1~xTNSLnN_94Y1uj{X7TbBkCVKG~mdxiq#d%Fl;KS@W^>LH||=9U`|*IT{W+aI73g;Q_6?m}H9 z$_~%Dh1G&wX>C_nvjZ%{SQ)lVaoZSMk=_}SQgAI+`(3=Fwi2|$#}n>@*se5CYLAg) zp15$;{56H}Mg6`5T0{R_h`@Ke;U?e&1a8cA^1g=T@mi_$3=X{$+bht{aWET$ohdE!tqXX*9-)kY#$6n^aCmzUU*yl$hwc+ z)_7As_NNxWB6WINgV^1g%sZ+ki&|O;sRr@ZH=34}mcbYXQGw+kORp!`n4c7n3wIezCjhHG2HU_iV(iI$vx{-&+2|7WUXi+LSdTw!Gc>R{xn5UDyyTUu9ys#Mge= zEAnf>#I660;n>UA3H=z6spbZH(n{xH#kp@;Q{)Frx`o=s4)9FrT4b`kG0yYA>qdOk$^fu&K!EQD3qs(~U0&N}}O*uyj z94YLvYZ!fBqQDb^YSqT@Qp3222}-UY%*|M?-_|ELqGx*22h~fD8syOwI$v}V_4LM9 z1~Tk+^Q)KUuJEC9apuwEga0Gq`ThxI1f{7)oz_P?51E=7wy^e2f71bc2>&O|h}n}2 zy>}c&e`sZ*3*`ixRf&9Cd^2yDZ@j9G5jm06vx&&RYL1=|jSzx~XKV0g_K9-cC~f3t ziO*A%n$$|^Z>qg|cQA+F=;muJ;V!+w43z?h5!~yv-ce+PjH3P1jDe)_{O70Im`ioc zUAJPP2b|r3y!~IdF7U;XL{Uo9J0@9S^4BTZ-`sTCI?-(dNmc0TIhRL|^XXjHn~qqo znrT|Zf9Gdu{m-FZ2fRyK6{x2r$Wck{1DpTrSMob2IOSF3x}GQhvuO2?9}l0UIuvX} zIz0a$=KsK_?){AgB~W%F-a6`a0Lb~@en&pxC=+J}p6es^KmNc!zfI$yq-xyU;Gd`e zpY8+rWE(;GfVkPPfaCwWSMdWSp5Cs~k5~WsQUCbG|GS9)SdatXhX3D1{C_@<|NlCP z;ZFmBdT<*ceOmxlw<{p5gGNDR6u;h`w*ZUxtD^FsP9G#$>XcN9DCz^XS&@%l_E2L0 zyA%mT?P!*XS7Q4jyjN_lz{Wi?0loH$YSL75*uz>c-H{ET)&mIHUPuopW<)?@%sEoV z5w)uWl;!mh?~?kMtx>zjhk;h-DLx&o)BL}swj2{%v75hw7~9up>XpsS@5W1R^lA|+ zY&@~|Be~lF6tn^jL)8>c*K9oaj$e6w{BFEdVG9f`5CcA_YR0Jg7B{=J?DB=oK2?3R zTzVZVX212t8GZs(TSX@|Q@i#r`&`}^pg6-PRCn_}x5_hF{-BIUPT`MJUxAuGjqf6d zjF%RGMmcnq+k3`li*^VM{`zRru(Z4eq*<+3B)SD2GD`dJ7T-B7u+s@m*UpWBHd=Vi z>sI<6MvxiK-G~DH-1~Jtc=PXXA>SO5z19wo`4N)Vn>qL%1wfMl72?}7+pA74`Y7+s zd6#w2f*KJjbDod@@&HvgA*$4!FOQn}A!=TNw-Ovj%1y{bUn1D}l273^ZtRcP3y`N} z;gs+A$QbIl-}X%OrjjzU0piLQ0Bj)(F;+SYp!)Y+XFF`}F*ro0_~{FR_AYcuD@)xN zkX-E-6S2wdEtHde*-owD7aK4z{Bmhnn&ppkE4a49(i7V$6v7tCmin)rfo=Fr)d)KF z?VyKVF@H`U?V)>=N1N9&$}~x;JY6-ZU1K(IchDBB{oVTW@An^wGn{g7?SCnx9^hHb zWdwv?=wbRY>^}WVv}^OjE`CEgb2&3qBT;PW;R4$KqqTaL0cNBe@RHFUnW7+fS=`J> z@t18k&YUm{qTRyydErkuQh;$6OXx6Xf}HpE`8RLhMi^GP0=Ul&oYSi*#Up7J!Lc#8 zP$7AM3QtM%SR&-xA{^CPnAL72D^D~U#SGxo3=6A{9H=F9@B!E1nIeEb_SZjhGSlbj zZ6L!)<<=GU!((nFX{jGpNNDj=4B)dq1VW5k75Z5e-StVE(DUHAP&=5- ztEo6N{=(TUQ2~XTvKko3$Z^>3L&d4-cSA-swoYVj5jD5kT}2Pxt^nY!X`d;(pZM$Z zDFSZ?B}d!zqKcWM5}bE7B`&Ka-&*_jtk4eViN#BKyshP~qi`9o-a5%nEIh@lb#wk) zP@ z!P?D;o47I11I%W#Ajq0EHLvgKn3CMO-izUCzN}dNNO;sKS06FXh&_4s_vs!&MBLWwwebeR$VK) zqOZ=PM$r>6ALkx$g`UEXC^bLq0Pz-s@>zy z-*i^z)yd-o5)KhmG)I0z7Tpr#fEGOpG)yb7C_qB60G-usl$f24L|4JvuG(G3J~Pnp z{aiu7aWJv-B&cOkr}$q7izoE zpQ{@ZdQwO;Bd;$4wWbFi`Ec$aHmz`?X5*Q9Ax$P`C;6A+`X=Twh1U3ujULUKeI|yMD zv#lKS*?8Z{12oW)@i871-skPDFeCkxs72 zk1|9PLMbF-0I1}LN9 zNq4!Q%BTTUi~wMkUC6Zm`uuK&8v8Q-ex&jBi^J6cogY4&fSz)^6U0=wgv8ce!J$TV zfJ>X{W%Q+kJe!cT>C5`mPMrIJE%WqY1TrrqSF6V0=e{+tKV9E4J#l zJ!}F-Go(mr`^s*?4ERLvTh?kK(Q ztW>|N>$n_`xJz9T(4T}dwtl|*P!auxh0kv-s&OUsjIchaEqHjFc7Mie6c`Q<-6_Ql z81!1K{`w?4ml(w6WY5cW?^yXnwOc1%WzqZe*|QhP1T<(Eh}`E^_4#TuK=S{3HKoHj zZ1}+RcFu5Mi8xt059Cwfp@-c2l)<1B`>-3&^h(Tc*QB;hF4akNY%spO6K#W(y0Z-C zdPs25=~g4Ue>%^!^{*6%e)htRBYf}BIU&#e{QmA^NR=Yl#kjq8bP&>2oZ7!>Xi9a{ z$pGcc-VInSSJ{$1y9l`Z?Xw;DJ2^V}llhi7G5()FIr}nbIea$**==IuNZt(@*m}sk z8=upeH0%rJ02)^yzH(Z`vg2^d(w6+-lVvc_$J=<6N1d}??PGeX4y)j9XZ{qqppwQ< z`rJ?G{HpbNGbb?&AZqzDH4n0J?#MBdv6)=h*&l?q(Bm5w?6zo^eyLB(o5rPX8hp}9>4211Qs}8Il3;eLF675sQK?=+XPRO_ zT%1+1A;>fx`b36e`Y2q$Py!K~9&>~z(V)t0&N0O*V-6@xc3Jf~xzJCT)3#)ec{ z=PK}hn!dgx$Fwc)JfBzMz)TuTyUzz(i!Qbp7p73t9f!871e_`}nj^$<3hscuctvu2 zg-EqUyZ87KVcWN<7Io-Qx23Hrq5cDx64C=OX!&rvKuTtO=Mz-LdDgg)7$<7wSmq}* zzKP6z-7Cjj^xbB$w9B%3MqiEK48&orMcH%5orYGW5M%F1WrJ5U$UtE7TZJZ=qUEyl z^xpYu+)vpXc2MuKc=4VAX}4LG&39)xcB0@sI=&;8hqGxXUx z-=FB=_hh0mP@tzqr#`X84r9!c8R6Wa%^sId=F?oRqQ1z@be!GqHK;HW_5m*js z#N+{lOW;SUiRtHuf^Xxm9jGF3&d}UAw)%>_)umbeqDhtrgMMP&=JK$#KKbGpazW0= z?&)-2_pgrO8Vu+tUQm;~yOQ}TptJm3#i0=tkd{P*(rVI=8~dzT;JiRHqc^;LA;>>C z)g+UaUKPKKNAEx!sRc6)6{yJqVs!9=)7gYBM&ku`Ur z{;d%lW9v=ac@J7F2Q=IZwdELQb`dU9+?5`?)b?Zwm!@}NyFy_DKY!vK&;7n)?q;~< z#h`s-XcJv?u|&e~Q5&HUw~8`I$tBlrJ*iE?0(rga1?+GTBh4P;ls>CEPQs?lx;rWlr% zP7c1oz>qXoY2J!+DbVG}9Lwuv&Ain|UwSXaJ6|tW_^Ea|^!XFwjU}Jzts2n$x#4~< ztD2=FRFAE*Rw--lS8dZU%ut~=Xva|s?Y}-fnE3FKbNp`{gl!Eqnin!xu{6FGnn567 z>uc{q%VsCPKlJI0&lbcWXW7r@nxv9veXSBtfHeksKV|RJWZfLNcyNPD9+&M*9X5BK z8i3YSe=gY_&BbnnVNaSL>!N>?WJXl!#M%G+coCH`d;mbjX&>lJ)yO}h|HLhcHvVdK z?}0#sGyEAPHo;lfs9zq~oP^&^BxDcrf=PP}tTpR#!+Tagg9XspT z<-a|w1c)8)Z+d)_G09+$7OIPL7 zW+61bLgSo-qixrfO>dO96PsT3rjj>i7wF z=L4Gmljldec#@TD>b>kM1S$wZGi}l3bC0eI>Kb|EcX_=AyrxHh^{xWsdH4n)=pn0dg-jPG zZ+fD1uER65s1euZ?gpRaqm4KFWkLC&e;G6rH;scC&@}6HJB!vI5)v>r&!w&^u~aijrPmSiA;Q2Fol^ ztDV^(H=FBY+{Q0tiUPcK7QWdHMiNREFjPAZiS{mw^=8zXd<*280A$c0$s4>5fx^UF^hKF!2*Ha%=(J}W$6MxbL&}f&DOx^ z_Oq2SA(c3t?_kq1AEz#Tf9G7<5AdK4cV_J=g}RxZ2)J+f(K!VSr7B5y?yf0O8$+@+ z#JpvTDNW7ISD6gh08u;8m$7CmVx43M?-cDPJ{8x@sJUqQNGAUhiSjco+;aT!hJD7U zD>m#1OB-?}o1K$;D}D*5Y#R`Fimfn}?01vW6iiZT%)4kKv_%{2H5m)>i&jm#fWyNl z#}pJ=y|E4sBp5;~?@70$Kr23(=4k6Uy!b$aPo4=BTsf~N9vD;4S$lY1?j2Au=wFeO zuP5!8S8b2nGx2aDY-3C_u9b6jxJT)Kxy<4#cl$<( z&v1pvf^7S-%GLp3)3hUGyte}ekDc4hJNt+bDnM9mz)hF5+m8A=1>7_Ta_btxmZ#GF zxd6_Inx+)x-Iw*)U=ydP^bW|L_ny8oLmZ?{q!8}2u6Rm03Cdp6tyjIPy6%ly_b&== zaIlV9EAh;BCK;TM>^sgBxzo|l&9fK#ZIZrL07KV&!TZi^$D6Z{F&vz>$mURXX3_6? zct)<%!;cX=#-0+sF;WR5XL;fbvGJs4COP7%XXfQN((-}&36nBsBE@6(BfZWb74WkE zo4MSj3hI#3=}fQ(6A3!3Fm>uMHc8ia-s4HI3TUp^AeTzZxnRr9$kAljxD;Z7N&3^u zLaoZ6lUrRbuBMb@<=k^sUvihi>*H0Jr5*2wv4R_gC&z!+dz>lfKw-)UjbVkpAe^bW zD@(9-+u5{9aqGNYNrVlwhd41b$}bHs)B*-C3{l8k=I)5~Q%xa-8mv6?mV=8SWxU8F zYDc0oEKzK#EZ<{kfDU}@3%3;nU>YEjVGNCG0RAI`t)DU+{nU_3x_dEtnUV#U4MP~+70ReuBxM5n@pUG$X`Cq+Zc{D zdXYXb@z>%tmoZs{SJhOs z?sX}llN)HAyeA7Wb~5D0J`>w=T0^HGXVQF+h*5RhAg#Jkj<_vvulihr@GtD_Ml-*& zqRGmV4yxFtzO&K;634*WJyr@JZiG3h=U_L%P!jC6({KrgQQ#j-IJLqMGS);Mr5C3v z_QoYUrtG4<*@cHQ9!Vn%m44uz)@nuX)2~MCbd(p~ukzP$*v+qLwv>4=xCN*)TVOq9 zeRq&EJF4E)itJmMnmOQQ*yr?RCU%p61kW3TS-@yg+Lz?uC)9`@*TV%jMNjRIeH?3e5Ob*6Oy$fK-&TwtcCTLMfj zC_gZ3>AR8hZ3Y9iK`;iUK-y3aXIdI5YNeVDYK&l1D$R=MX3XmmiQ6H^&RieT0b2AW zVE&;3^nC*m^BWS|Mfv-`637MA7L^pztLfIrW`|BB?PR5k*>tFM0@4D-ZdF?k#5q)r zeYY%}9=G^5_h+a1T90n~W$Tqz175E#=p1}4(9E29e@-k4Fvu>0MJw%(1Wuc}vzA&$ z)0=w3tSw5S@rBN|^FW^3I1Ue5C#=k!qZ=cG$ucRYh{QczT0OPRD+FUHhk5UD5D&D* z3KbHG0~>oMN-s!#RIN>Pa4FK2oHjB~405QxkQHy-CooXY%5cumWI*heBgxpu6csi& zE;7DW5I9dpeAX?z3dUIfz}k7@K}HFL%iS7PENlz0ec3@ogB-f-wd7&=I`x4PGthCWUdDH zQQ`Hr%k#i6Sucp~U|pXM@XKL8H)Xl7r#Q9fhsxO@h9K7~#g^u-j(;Q_g zU!H}-M=2C=4Z~7Udm%NMe$5ilu2A*(U+jHlRMcDBHy{Qe0-_=yf(0T1N;fDdDBTSz z-8pnxpdv_jNOwqsfC$nd9YZKRLk!*VUfglObKlQ;-?iSa&zG}2e;63{?7gr0g?hb@ z1MBlSlLxb+j~|C^Y`T&*U0O;DRD=371^iMtK4YH7?(m+bSO)8(yYxU*v1xLnUEk~b zAj~g6OA@)b{z=C>bE`wYMnkzvgG4lbZpGjQAUb&yx-fS=_hB>!3sf&%;TLe2P>cx6Rb4x#YLhM0}R9^Knsz)Gi>voFSwoXGf=J3Wu-#lC&&wb){E0RpFu>xl_>;?>p+F7_mar=JniIaul8qZJDX*>^ zo3axNL2SjsPdV1bZ@7&kD=Yg&r(SBMipqL>mAo-lFm|tg_G$2S`ALSk!9sOl!_iPQ zpxA;ae1OW8*i;cXPJ8;?l}|7iYVMEbc+h&%>alU1JrPxOVjpIxwArVv`TC(W-qYYa z;V505lnI}lkKIgywzr#nzjgmYY%X5w1ba&l&7V#bXs#|!W0iPMrH92j^}ebJpvD$y zf3tWEWN-AiuHxeRelmDJMp; zx?1Fl23*3Z=2luBk;r}x0g{E!K8Aht!qEi>|GFoCt4^w_%zQX@=@iXJ3QdgfGO)3e zpnN!S#NhhJD?B9*0VIvo_q2)r_z8>nd}|8YTOhSI4^ybO;xKNQ<1ruMDa%PvCu8=d z@=TR?Ym3@Sb7%KGmIEC&=2$*MGo98Y)#cW)juKq3Vc*1=B?BOh#AP_=R4M9bzPvf^ zcZxCPZ-aQ6l{3UN`Y{HLoJXzU!yh|#@n<0+vdf-02bMa14D3JG_q{-bH53<4e zVXZ)eGY6`FD_aydQ?J+|Bb zdfoZE@Yy_LVfyRAfBPm_+W&on|I-Tn?+U`s>y&zisqXq>RgX82k4d~D;HDIx5ZD`f zS5*XmqLKF2hv|R@meM2<|0_>SK4tvvG{f$y0nD2Irf37%(IRsx+0L*liI zt>1i=!;In3I}uSF>wNxm%X_Xe!XB=SC8{`fc)sD^-CT;B>vzAS$kueBnf@dC;*(%! zE7CtMjKkN9xxf=x+T^hJg z&aURp-1+P2w^F)KmlBAmCJU~<+3eFc(OaJ2j9w6n;Vadh`MDne1M>|HH4K>AnW`2T zzMFr1u+N{QlDEpcHv7%<#?3ou?}?Uh`NtnA!g_6=kGcJuW?^;-@sRjSsumi5 zY*%*K{LLYkQ1?h+T5jyu&(F8FrXn8|p(5Cs1K{_b{Mzb_alH#2lwv*C9RYd>q-Ru^ z`J?7~OyUT$**shqN}r=Xi$kO93b*UF59H8KfW)r#O@L8twZdh0>8`rnY?kE2Tgq%g zcv-AQ<*q#lgzV*(sl*8hfLn)GqDCpp^B(E8rz3772xXg65S42j;4?cmp9M6~c|ZdR z5q-!$`fC9VglQdM?C3CBsmRwQDV?s9GqPTRVLu3~5Wcs;5HO`)haycTYNgTj(3FD3 zYD^bt8c5sM#8}LR=Rkl{>v8vaETj{DS?bvus(?JO`8LbquB;&dKG-GzlUwkL1+_e1rgEp<*Ljff`%PV`k+H*U>={`W1BvB6JG1p!QMJrl}wR z4D-T;*%z&Mw!peClmGnc)s2xL4(u`e(LEY!SGKNoP-^)wxiP9->UxFSIxf78t=Kv0 zJa71YA<&DVo`gvzH2Lg6aOMZKAyU@`C-rAQ>up7 zsFQMG5)6@oK+pwO17kTqNXUXfBdr06RlNV{nrrK}_sOC0ppk1r5=PGlj~!r|@xw?Q zW$;M)=uT*R;2DHZ%+8mo#T|Q5vveU9mw=?}j?fi>UG9MjXTN#v_L%6&{PxNqKvN8k z^I}Xx#;{fN&k}cy=SV|~)@jb3%e516ZfA{hD%(EQ_&f3`EPdW+a zEk$j5p*;bh>_JuFW=%#+LtA`%{@6$V$?6bcwUD1MeYB&eJ%pa(dXFsrytZpK`CPSl zEzD-dE%}j0>b0hypT|w9ZEJgfRlDkLEo-+@ShLQR0u_3YrfxoQw5`(h^u*@UcmTL6 z(92KpRdt5dq^8!y&t5?*6`2}!Ah7+Yc=eM5`3&35M!qTv>@M1r=Ui)pZlT%6n%9V+ zLEDw}es}z?fLk9xyrf(tXSB#Hj+*k8<8`gj5qn^h{aQpsp}_uQR^)J$pIO%u zeg}JTs$s|V0A_8i4HbLHU9^$Y07(i<s5+8ojsP=-FI9*}Qq)i${~7 z&VY~-|NZ^V+-i)|L(M~gRP%c>=1aD#Mg`%s%A$A7hs!kgx28WCYTvki2c!+A%jdT- zT(sMKYo*4P#YSC99avi+&^V=Gm8L*{y=`OBg6`)MRLD1BM(~cYb7K~j61!+cWcx8@ zrOJGw0UHX3Y_6U)D!7ea&8##-mhLFsMoN{HfpK*~!U##PHDYa@7rjxw`2Eg$>03$~ zG{&zo!+CvrCdz{I^Az~TE@x+Cyzc{U<~cUN8WY78t--VbUIha`n0#_cj3wC za^0#qzxU*86O&ROu$H={4Vqt`933zpau`QgcS-gkW#-aSP7PW<0C5=#m(MoM+e*qQFMXboFgcIQD#8oSxv zCt#hc^F5_?Kh!(hu`f*?8He5cMNbf!`=o~JKoYaISYw8BO8C{Ok3rz|6TV32ho`yI z4A_fnAIJ`VamW@JJ}2i|G;*HnZVhKm@gZmJ&ti32C9vupwISHEGq2n_Wi(ij=WMsL z4s0|NivfWWI(}X-r=7C!tJKkA$BdwBA1qYqoR!Ooo(evwguzS?0xfrp{P6(CoyzKQ1U-iTzhZ6{5WapsSEO=SlC*2{_!j>-p^WZ%%Qdr@0(*yh7cw zx)siXCVl8GW_HV8@^&}F7pj)(kPB+wFm98HU@w=It{>>U1K+1Fg*7v5&*QN4qsLTi z@(OP(kFk_+v`nNH^0P3KE8%i9mc)r#7$Y@@_{CHC|iTu{DEoC@?rn~attoJH;^-K9?1tRXVk#%uC zK;tATYD`5pWSiY#15&+S;BzKz{|8f+=EPzC^~+1N@8}w4sS?V?lC7zk&A$Bt`{79s zUzr!*g)2A;Pt=0!S0hLmnwM)POBnIaC#+DhU2&caPFt5bk^j5qG}j}6*ms1)2;IKw zS8W%Pks_jGXrBU&+R8+4vGendC@&-1w`rK^bt)J;<$8dfys;xWq_8C5tj` zGZdXxN!h!?xp0fqvg=d2-MpF|Ph+78XPE3e>hgr0uL;XhvlZB3Hc}~Hx{c`B*}A^X zMVL_ZUr)(?D=-4o%*k}AQ3}H@l8-zQ@|+b5;Ra3-b_D(V=PutHqFPeUdvTI=IjfLz z9d0+La5fa-hBJyM#P?l(QBqOb>>esP;RLU(p$=TU*`#5d7A*>CpC8kpSt;+o_!|C& zO`k_?S`~F63*>MDOKqzB6*GGK?V3XvZ}2m&-JbS z?OA;FIn0y>>CDRF?;H9-rgJlW??^>)(?fpQj1X&9wMW~J-0}Cn*vh-?H*>oe@JvXK zwY@py#*=Z170!p!`@1U`(3jgO(h#n*n_norajrdz+a$%UN7k=x@Pk?-aF9`Q0GnfX zt|%H!YOYyWEt%~D^5S)En^=1|pNZC!Pm7oYkJoiHI)~+E%k<-%qtCmI#8U|SMc&JS zBvkMZ7u<($yx$QLt(}4~XyyL4?bf(o&w4o^w<9$WJr@@?x@FBOoCs}&eX})r2jW_? z7DuICoDi|#Eju+*`z~;vJ~}{FbmZjV?upuz*aZrM;U6G@*7*LS`(QJiZfT~9f-+=K zmooonc@4Be=!hB2w(R)D3`AnzK7>TiGGp`fGr*t|N1L<$oa!dzJhPI`n>G(N%sLCH z9E8~H_ujwfc~R4SO?bP+Gu8$Jmy>eTB4d9ln?X=aHt9fyvca@mY`YBhSWCEeBj@Pd zFKZ0SzK6-lrcY-#iSrFnse2}C^ZAI~6d{B}fsCeY#RCk*OQ$`UECnWr`XND9{4%k@ zg#^aMo!?YONZu zxufWELcn5Y&;tZiHhOu=*3dn-+R8qS%p!(=LbhQxd%R8jKgsi)xj>^tA1jSkYNr;Z zk?{@-T9mQ)6oEa~7?>i`$8B3An{et0yo)H|9Tef*%QqLKZl@$Vyk{(dRO%p26PPU+%t z&GQwA3kli0+=en34+PgSU<3pQVF(U7d~{AnPc41%aq)?RuSY$*7sa!bm#psr;2@Up z`{S(0Bm(BlJAQo2&!QF_YiJ0?>U>>$4Qj)*08hx}x_!2T#I9^hnjwAqJcvFxV>GR| zCar|2Fb$cJ=4D#uUzd0)-b8bBs_rbte@K6lA~iqfct8!XlU}LQ>C+Xnc<4M%ECRMW z60=;9@|=}e_wS%{0X@;HNg&;~*OjIyYz7B92y6kXePq<9eS24W`}~7>zIVEdKlMmW zI1%gerOJ#&K`g~Hli?N|00hW;|EozPpK+A2R){ zSmKqbt9IZQ0ROa6JsBvs=cLyxQ}{LoUXM~yMlYgC@}RL9RQ*a6xy{p>aq7D#M>Gy% zea^9{fs)24MYjj_3W|p-GuHOCX_wG!rQ6wg$C76cp}GrDH-)u-)H)ivN8P}w#u3@u z{*3co?uX6OwWh7H+gTd+3i~R~H`}(J(aEubmP6GuS3KKFGo*7%geSY}`zX7IAB9$> z7$P(PpAkdhvTZTjYsO|$Utuf_Q@?0qxs<4GMwM^LdK5GKD=n78>|LQDY$ixV@y>P) zrN>7q-dZZNDW%0ZE(GWaP&B2*k_w^NLcxzCx4j@PhPHL4>c;Z!O5pIbX7%Lf(fP?* zxtP!(EZG!$*Y$z>>mi>X|1dg<5&N+;?p5|VUnd^%@{NnO%aUL?Cq_N%`tMWTYJFSg zWjSvZGh%Ye-;G3(cnk{VRvL%fC&sTxWvk?=0Uad!M`wYN4)0*+)y+MX(q(DK`9h=h zOim$3& ze=f=(#j>!hFe9#ZSye)N=MYkT6|c#<#qUWyw)`?}7#!@P%0Ag+PRp^P$9`P}*Pa}#wR)lExMHO1axbnEu#>ljbu!-MwuVF#Xkz7nvV)P9xXq1Xg zHy8Jz^qDghrxWVNC#5}0#O2h6=UX6UebO6R5Jr#Bg#4s+1;|Rgy|Fx1bnhRCX%Bb=x*{?8gV5(E2TxjbT4sXu`}pMynON z?(DcGXRe+~q$i<4JGqx-$C`h}`3Cc;CplX8O-#0Ve;CA@(E+uZlHIyU2oo+pwFmNS z7ddck+X$)meH^P+%H&soz~9w#j*3TV9V|JS`#RDogq%`nvgD`F8+A>Yr#Qw+M^`GO z-0x|HQ=0=2;-^L(V@k2J$D0RWx*?^MRW zboW;Xs;xH***f@+xojHeB=0-b6Zb#E3P$r$RdBwjWyS7mjAS?Qb=g~ch#Up!z5$oM zjNwWzQV;H6;hr_$dtKYny!OP=m2BNp1u2NAPtEG?+p9z^Jb_gM^00KqH6sY@6a1oC z2?c+ z-KE{HUGL9c1izlUss%I7NCu0N4Qi?LU!Fe)M!YM5qKiJ{=dI zB=G!I)j%1wx^P9r@)(H0C z*&K@rSWGZLxy7KoKN&-Cfe)+@2QpbV-e=hi=oLnVgtKj81ndV#2#R!Y6mC*In z*daxIZyS+>n7w2-x|Ij-)S7#M&aG3Y5$SU!YxpJVUBAS_tM?4 z(YI=Cj+kjE7TInrkL0E7?i4+%ezCRM#cZbf?);Cr)JD^RFQuU-Vi}%8ziUM2OTDg^ zX&d*H^)nLbk15^bg_5C=5OYGD#|8`QX3-Oo2MU9)oH8+Jurj}W>*<9);aJI5E%!Yt zFwqHh9+@rEsb^xvw~A-%l93T5+b$k=jiq2qr8}%|kRhBZVBA><0G?*XE~*2PK*0B9 zkEbOVAqWcAXWF96TUleDHLulsU`vN%)NA?mPye;^p}orz0;7{UC#}^>e?9$mg;Y$; zBb9B0_$6|?izl16GMGSo%DF|+8N9m%ooZ`dGOJ%hy)PL3nk;1 zO2PH1Kv#8cip>=WK3 zS;!EC)gUv;of371+{x5q_-VUXTqJIyIuq}y9KB4oH3yq3StfF}2Cb;+vP ztYQu;rgtl83EZ>2S!V6dq8X@$%4A7OB#MPyx06^|w46IKJH??#aSe=H&kJB{9qbDa zTjuDuezm^E4_s(*>XZ~q_nRV(B8@-YS5sEON1<-Br1uQkBdtYr8F!qLnI5h;y1~K__G8wlpFBD>I6zy_NO{ zK(llu0zWacmsxZ};sRR};-qLMJHHCv@p__*RFWX0)4dI7Vh(wP0Or)25ss+6rr;a% z6oC{HPQ}k?M|c2ZS(s%}sd^h^t(Q0`ZM9Rb&O=Yhu;H@dN;W@vs)p#&I%*}6@bg}C z#SM(lM|(^neM_wieB=_HL^cYMu=9WbrYtv%=*O)3a z5}B{wLX*KZ@a3P4aC#?f*c+el7LVXN*` zPXWFC)75AIHnvAsb{|k2j6>6^G44UdaMym97SBmCO$Eup;;PZNvN=`zMZ8V98l_)4 zp?pk8(3)(`?<9ql>L0hCt$S~&p^$s0Z!q@jKSW z-?yyWNWE%j!HcV1^29d|GqpkgxI+MGIBS*lsB4OZnXSm@Z2t5cJP4B>v|T9}y++?z zNZB7j?M0)EhH>%}OsI_)!vINvF zh1&)%bFQRWV`@hx;2t>Ng&9t6e`EG7x!OR@^bn#nkF*c#NU1kHqg?4S@aegR+Nx}d zT~C}!3RRDralcZDCH6{YGm<3q_xh8RiQzALkyrRO{R}-ck|ol(Os=0YnVsoK(0|{T zHMK7~pIXzIM7mCmKf~I$#=U_Nd}i+dLtw*tvMJ1s_{u5P>El8!QQwm-^KUd27SECRMX9<6f-$8AP zu}4-Qt!fx1di1h4UH#m|T`w;D-ON&GiVO;#-S#kxqpy6bKH2%=!u13q)=hEYjiZm@ zpM4mymk?ymFP0p8E1uowRTjq|4CEasqz`3F+G75_}~6D?r7 zukN|b95>lj`c(%qyhDoha*q;FKOFU>Ht^G|na%%G#Rnw~dmE&gonHTSgL!!P!J+0` zE&a^>>}Gt`nfjw*;-CMD;)Rnq-IhOfY#y&Ggsr)<*hCW9l{hWFt)MGe%jQ3w{P$NT zX2D-NxNT#9?8H69Py8_>oFbgYjlTq!J6PQ*H@_pLi$d0K-34^A25{EX6f=^yvG;)VR|MTO&^_gyT76{*22@{;{Y&JpzrmY4U&H-H+wx)X*k6&D zE>yTrIf2nkkz0#tYfX;{mG3>9HE|_*W*XI%0)Rv`URnJsqM~4EMub4=acib*^A5Fu ziwXss;cZdBs~_bNg9V%u=@~D+XL7wkO#r?9!vxUV`$v3$%&<_B;27YXd=*SxpUUyt zOuNy8rh}9v<@S|B36mU?fn_Fgc4L+Q=eO!B<=z;cpXMaKp+6wWWbd?ma>4590?6np zYsj`w+ZnX&nmonAh{HDx`-k4?2k0*l>o-YQ?8 z^AJrQ==9-2g}^PG+J#}~CQv~*u@2rR)Wa+L?X8UWcmYWmz@pdWApNFL+T-#i(%?Vt zBu`zCI+0M4qB!0qaXblVZslc;86&fwWT_H9wmaW)HOz6IQKe}G92k0&mCUG2O{&dEd@x5~QYsd6mslyrObq1;F4H3qLoctZ3FbDw4L%>~_(K=$??Dw< z0-ZNVRE1l(A~~x@P5n^k2Jg6R>3sovdOL3GiPYUCKlghK7opI*aT5PRZp?WD=Wfiz zMB_nt89A$#|Dz9b%0g{X+?r2=pALUvWBKw)eSa!aI3U1U4gb>DCc?|@G5ieBI7S-H zs;6Z8D5OtRILW_ihwQDvAa?%#Uhr+JERblq1r|^deJU=@KnhnLD8>*NAV!%C2>OKO zzHYKx@2w?GwDIah?GtPZFzw^iLb=4f=SCarwjI9pG3Jp&Jyo%|uC8vxs6pZN3YW@uY6=%)pNO$6 zdg$7mp~akCIzmHk{q$EnX@gA_fB_vBm_y_2nJj z=z8Gdf<>VYzh9&UuMmtr$!+X@zs6hAYM*(eprC4yGy6~mJ!|!?8ny^!iU#2!UXk{f-A+3R>c#2# z80XyB&qr19A&m8P1O$TAiu-lPQo|Hnrb{%v8K26WO!r2DB*GKPuJ-!z(x3`Sd;t9a zisF(Pa=kKq@ruQ>T|^Ma&n21kzNwNW*o(Yg?S~qZK%oahS9yj->ba7*x-M|x}ATcREJ6ROJaU! zR+uvH;K)SdlGg8FsMY!Azcp_F1zzQ&B&~Q=YY9Upulr&P29Kv;e?hEnrf(I^9 zY$mDS#38bA@+1Df^_lGmZ)>`Px_wA}EdePi|F@8A- zy{EVDN=a+G9E~du^XAgSB$5)ndaSn~M0>K60I*3KoN+!zlP|F2QX=PGdu=11fp#o` z%y7UeT-%5K(XHKVsp4W#m}w${4boYh*EJ0EM!KfToYv4R2;;A+!ztAX*<#(n@3uRH z%zwNW$wy2P2}S_|Pp;S!2!i2&%}8S)oNfu9G*_n}AwlgXzuw9+I2m8MIWIjwc)!gq zq`0w1nJoDGn-5_$bD;IqnbB9JJK1z|MWHV{I!U`qKz~+-u&D0i2b)(wSu&?#u8MM6 zj~6g)zailElFi6E)=0RxBZ||)v-XGNy*>(a=p-sZG;S%RqA`B+Rud$=WYOJe^QPoZ zZ(J6urBEFSp_JYOW|619Y|??2;)^lfPVj!|`cmdcwNn7>PR-TmuNx!oR|2uke{x%l zWSl3JZ>D-fHL-Qdd;8!waLlZy!BvTuoWs;RJcZ*Ese8G0`?FI-S0`Tg&9@wIYElgr zV}u6sbi7K3lUq-K`iOjt=+>XG%ZF-^A0=v2zdCA6oUzAt$9_@BLh?mNMKvc)`>sff zxkD<|#AA=tJ#-V-j?MZU^8!ArbcK0E_@{f!tpkqkdnSsck5TEu!`2EX*miVsUPp46 zz0YsLnC}$zBTa!M+{8cr)2#vMCRsw1oaVZMFPbPU*-iWVgMfRnkonE|@$zKzv_J~B za$okaE&%+J&3D$b^gk$8fK*35biR93GPSD)_dZ@?lb9c=NFl8>3+&BOe+ptd8mC9G zWQ+Sud(z+ZVMs|w?QkmUB{lu74(tB<*8QbVIQ(nFZ6P4-`PlU8=oJ^%)^bil7s)!O zFc%Q*20NWzlF_Gp)Qd9hqvvSa&k@7?Q$wxD+@nx1fS73P{=(=`jv3JZu)88Q<0u42;?h9&)uumwN7h z&(A#NO~qRjN^;}vpk|4l^W<>+qnsx_>BXfRd~f=+wQoxcgl{|Z6Xa=Clud=ixP}E4 zGzE>1)x9@wRgDwEZf~Cebt(>qc|LW7lX*Y%TBX3|Cb|z_(siLf&mu`Fi)V^%W4Nzh z_4DS->1ow{Xs&4hqLkE5K!0$4s5Bw}p=Iu<0Gze)?7OZjt#5$!pEbqJ+pvNQ{w40l zN>|bRduOS(6KA;JRK`YaBKM5Tn?tK6H@-HUBBTJ_nR(6!YcS$)_4>wsEHwbj$NWsgZ zx~B171iQ(kzv$JTc+yWjL9ms=zwAx#ZV~7c$Yf=xuGme_RhO>M-_1GvgWfoY#48K8 z{Rq3Sqc_!}EP(*Wqo{ApRhS!JEJj21Z+#w_K^FV%uEeH2M}lHg^t0s+#`}m{No`M* z%YM{x1W=lb@p78goFncc;6zN06LKna)<@s#ah_F>4}T*_e$pFaZZIC{nB<3jC1Izs z2p&WPgq_;a7|QX?2vegbyH6G|YhcJ}BoP0pD_ShHQ`OT@hyDq@e>=wsd* zL8`@KJ+Q>KhEjy%8#bc!3G8pJm6{~|Rr9qFUbE$QOgj_lr+h>fdlf!N*i?2!^Z&DWC2JUS((oB&g8!*)ix zXHj51o1AUe)$<0porTWr+MgYkUbNk*dahOc^xF4*btKmRqd1b#G)T57=fq7 zDy&j+??Jlajw>RKKl>s>KvZsiaT*0zZWSr5zzf(A27(r$rwJ*3nFdDKl@A^s9WB#H zWgL+mNtxr9wWlStRnCTTHu4>n9N*<=60l_HqQi;7{OEHL6=V{wZ$F#QZvFPce3-r> zKSeKJtSj#vw?lA2>=V=j{G7`52z*<0cppdS1(( zTD5f9taPbvd1~++@mgkserrTo`@oy#i~mju+HA9)=e(`l7g$HjIuF5quIjQEa9)?_ zxEKK>%QiFPsTLI6ll&WM1c!I$T2#eM&!z<#uJ^t|>ans2%TosWduP`;8#a$_%kOG5 zATNcZ@z0nue@e$Ab=>%8oPRk5CfYs(#-kSt)xcZ)Q+L)5r#!e|Ss7eZ=agUHOL(rB z_o7d%<}G2OgB2!A>jXIslX}QZlu7@nZR`lTyembzxj)Y;K7$Bydq#hM|EVp|T4+jF zG--KX5f&CvDNpQdBExG*P=sZLEIJTF7E!fqx+?n&2BL?yo)6?c4@^_5a7q#p6%GGF zHGz6BJ^ZwrG=xcIG2$#0&v!FgdX(ApP;)npBE#W97WbGrFMq80g5}9~9kL!#Ew(O^ ze>X*l&q#WWPu{q8!9nK1kc`Nm*3w{mag?0$N%UFg27Bn?CD8;ssU?hrG4Xx>D;Jfxl>U>47LMw8XVRP zPJ2SsurE@ex)aH1VO2ys|01SK(HF1)pB}!!cib~?4AASWIuMfI=oDvun(%ld%sTg^ zcbQzSRCHBiM}lbUCTxIffH7E$Xokc{?`K?fa`w~UCKyanF6{ZdN(^8i}I-3gN^(B38WcOBeXV0Ex|Lqo|&Mn;m9k}V&srIf6~N*OQ%l4 zaeZKkr7r!Gc^X7twtLX4`!;0Z*7iCw^Wob zq~BmvV+#e|dE~R%1V1}&13}+lkcExhp>`|Le1C!7w5b}dk~q8hZIS&YzwS@U#WeHC z;$jdply8GV58#Ty(=m~Tb=e!|Nkhe;bI@A2-*~%WXEIbf%H}F>qc0V2i^b|(Gq;V_ zedM5ipCIdAL|c>~uJ={E%A+k0byXC!H)~L4EM!M@RW50Ru}3Nv#q|h`UfS<~(uS$n zXN1IkIit^BQbvbl&w&JFUfBTt3-1`#B39a2dRdE z9>S8ZJo!v>W@3d-(``gaI`!EL#!qKubK)`U5!IJz&>nqR>dc@5yFpQo`4MTs6x0-K zv6c?OzuOCBXPp7q{HDA~c65HGQ*p8RKNvf)x&p)@ePp0icYe$^Q z&^|U6vq5b`+t4(WUaR`9yZgeoR_T^|t zPgPp{fQA|<30@0;oM$6s#${&%!*Kt8VdoG?uc;d({mySo|2k>3fHLOSY)xHh2xX3S z9v#_d`&ZWP+|DE-s(ddrFYlu+y8Fp`*4p1PYfU?#5E{M>4E@&C`7_uw!eRy-JlTg}68X zoxn*iYAG&k9f5p)!>wh`L~gHpGJAU6Z6KU~-t<*d2Q1>?m$z$GLM|J7&2(v*F&`QhWy2L+&Ux;rTT?zljSqZW&msQW*cN z`PU}=N9$b$YW`Pit@oIYJ-*_(VB^=&Oer<>{!wxH`-atZ_qO0l)UmP4zs|n(VfS@o z4n=-+7kHjMS>zUe9U=xz%{0@G6Y4Q{Z`_}}&itsW;8;ocuV(E7;89+#W%l}f3i_+D|5~trFUbF{-(h3wzb^`W;lJ#cPs+Gb(c4da zsdl8KqPT7uv_<(7kTFddqM^?m=j+5d+y)H2Q@bN!Z^_wr^6vX_3u5wq`G`hvB*?Ot+=u7y)S%Ww!6eiI>UT;K?eNBO*#gas4S() z7@gEBR@%-hR_!OHi@d9``^K*udQ<@r3q3QqK;u!u8iVjXx@O}V&X^8wC#aR#>OFG@ z4MOch@$6gdC+!FHLcf*i8tm`ySjgvt34D7uAa^e}xS`W7c7sY5rAl2(8)L3s^QE4o z=4j)=0@SG&tq~WE!J@doR2JW1m6TRO0hCgEf}p0*gzhaPfy(uPKdwg4mjuK@cT29_ zI<_b>hB&_mz0ULTt#w+np#O`u4bNbU@isT8P z+(z`l9K4*?D3qck$7;U zpKTff?4hmYlrE=s7^(a2k{8eZCcZxDVVY4L{C#L@oymEsWdM!NK!&~8S({CWOw63- zSo%&7Xe*PNACBo5H}5Gx1)FO=pfxX_Kga(3yG^oppO&*K<<-!X5`YAzj=CRco+fKf zC1B0U*opS|F2(Y+g?%AW%!ND_wrTJ@2lC184AYf1%jW9`&ui`cyu?&UJl&$IYXAk^ z4}~nXgXt*(o0nKGjLY8P5;q5Nk6dt@3NP$|-oE_cC)^TrZ)+C2YK9UayY+;QXC)CH z4Q(9-v&ZdHNR{K-xal~*MFuOXNk(s{rw!B3UjBc0N7&9a=K0;(nk9t+ofZ7f8sS%k z4kh0s1l>P#4|PfBWJc!R)gh`78iyh*Q()J!f?n8|Y@vA6&9Mo26}wj5yi}6^VE04v z93-_;ikS{_&v<#ov8;eeY7Dg}TJT_iRi=i`gXBKxMWGzt_1E%!kY(Uix%Z@R#Sn zTP~>IvhX3G5DIdmZw_uYYXY$i9m&>`MXR~H*tLsRbiA?JSRFYi`qo;3Rhd{_&IeA% zFFK)T1XeX3 zTRe=&aGj36g|67>O$G6t!8w~!Yr6DtUj|Apjz|zwVyt^GmE>2W3Eajj#aHsFV20i= z0b-<2_!otU%2hU!98-A~zwHgXe8oE`f!jA-t;jTnE-kih{a_OY@H68M(pg%2F$4B^ z0{f!}iub46F40UkEX*!I1M6uhlbP})x%)OM1%239ZT91yy4)3HktslYu)89?wouN{ z4H9CA!7E+Nx>;)MCnU4C-?%SUdzFR%s_MZ$eI@Q3_XKhNQ)6-_^&*W%gyVO-`ivXC zT;K1G7NF#Z=}_%c4uH^`BpXkj)R2zdPcCJejDRBepk*tN*7I=5uW@{oaNqn*(G`>{ z!Fueng)oo0kc&ATKOzhVZkqhL`ce2fehTmi72Mo1BW}(1Q#0stS?v%-51R1S4o#oG^)ywK+V!UN z;&S!rowU!wHaqN5>?URBxUQ!4X6*>|q}lj0ax82nNAjeDnih^=e)s46~8!4l=aRZ@F95k0`VA8qjZ zxHiHsynaf%^4sXT;bi&-u3=>UPJ{%ORjliJBWYaTWW#1sC@uO|&kf_=Oz!=$Z?@%j zS%|n)n`hzFxPcyi-IhL_V!3I~8|63|d%KONh^s&p9WkJ%oC*>Cri{LWe#0kGgyo_} zF&7oKNI9XEcHWD3$Rfet)Xtl`ucvQ?aheTYC`6i~tji-(4wVnzSR3zdrr@yC?Co~y&MZ%2A|hVxpA-AU z?vEAUU6QF@?Dnf{X`{L-IUdK`qLN#_(72~5~7<9ll@s_r|?DmoKz+ zY~$9FgGw;7w$$GK4yMd*KptJWbM=#= z`KM$$YRAM_BLTTipR4>z!|v`D#cl&%iV^vVJEe|-XJ`p-ZL&W5CY(Ibdaye!m^?Lr z=tIaHhPlSBV$7@b2d~6mOi9yv+-^OiD6rF)BNl zV=yz2raOw8wHurTjHb*irS(k-Mbff18tITY{sRLD2{ohd1s0=bkL7yZ4|cTknb5J6 z7GEEo*3(v6+7=A@nm2dqoQq0NrsG>99t9kad89VxrODooVB#yY(!!g26oKbe0>=ZB zRrWp`v}+kpG}O1oUlTrsra?RE4-#Pwt`3*m%TLE@i&8nwTzeY9Uoq}$ZoEEM+=2#` zlJwKmEm^~K1TQ8-C)d!Lra1PUH?uW#I#<)rE*aB2%}+ZN+*SK>lk?l1LJfQK{x6C#khT>Dte!Rcf4dixPaA<7AB?eyUBu*sgL-khw ztUs|w^|Z=cS$nvyC)H)_9m&(})xc{?SRL~}1xfXqu2>`|q7?DOZt=1i+rGi`=}A`< z=3lB!4I9|g3B@gj4+V zc)DV<2IF^2ti?J?-gHXyGaS$FVE5#=XOA1(KV^K1cdnVZjoFibpb|G$hj0nUTh1oW zZLf}2e14D}@y%X@ru>R9vZybOoK-(;QWm?mHBwNS-ME_r<+MKcIlE-c-SbP>v6u@z z*9EWdO`b| zGZzBQP?oH%DqnQa4lpT%y8~wC+jGXkgMFm2WbzFUhiz1ku&l)N}>r_IO`+lBy zYh9_$sDhkH7j-4zOcb-!%zt%RyjAFIiQ%tbrMlfUFfd?T>wS&I=q)s}(;t6i;_T{d znGq&h+cJ>6hjsfw^<*Q5n|A*3?SQkViEH{^w_4Q0g6)IT#RT|7{JyJ!NK}z#>m|*kP(gwJcdnoprQyMG_rhGFXbRdx7reTwR`R&1Ca*TKYjY zsOX6mzv@WmI{Jg+?KvuQCUWUSfcf)U5gr`x zqEkA}A+3tV=9)SSAKm9#Bej!W6_k2v22S<5nl67U<6kK}^Rud(OK5 zWQ$Ro_NH+)_u9SEp~B7X4PbEYwOlxspapX zFl;e(rLR~oHWv78NlV8iz9fl{p2KxC(@J}ymN#akADe*V9Uwqrqt2xBM;aJ+4|i_I zGj)R)mzY2}hUM5@EhhABBo~<|%elZI`JfIp5;NQfyT*Q~;}P8No%#r!s!Ng7H?8qj zYkkNq(t)cM`ADut#+YKTn&>6rRlaYl2?l*zvun-ksvzfdXl zv-|iM7Nc7Z3=cG_w&c>DmpCl#3bj5K#OnX*C{I-~H&rw#4N}{(r?1-VNCJcVUG~SY zaw@?pHtL5Y9!;s(R3MzccX zEq}|li2oh5BK9Ur5KQ|OF94TMIVD=@$3%|I=coua)JfLp89~$; z8ZA3jQmPX5%y{F?t$KS*#js)^t#U2j>r|{UP%@6+;mLe%Jo6-XG1~r8s$#}S*Yq}v zyb@G+wxjR)PfTuOe^Z$?8zU0burTIoa*5=~vXw6`j!<;lPsK`Oq|?G$0_Qh5P^V{U zgo5WI*iMDDp1;g#Xc-O(bLo408uQidfw|FxYl_7Sk33oaf3GgfwvgYaiM`G`DnLe< ztvOlOmD2tbJ8@{xN>(zg&V*JGA3b^?yEiH%pFG-~3@d}t`HJYwoV;Hj^=loox1pR4 z>b1kIJEU@~W|)eNb+Yheg`_8n<59u(%9+2yXJG8%9Iy{ zR(D)nro!{e-l9?2Xa8O%oLXx}qw)@+jbI;8E|IVJtILMIluB>pav?9wtui|v z0sADa6pz&o-;Iv0R+wd`fdZd}N#Uy`)yr#{iu5^CE)(6nJbe`D`4Q+6@GhqY+Pj*msh7M^QCa~&RWAFu0#?Euo_j{N zb6l~8@YJ-#a6ol{+Z@l+=v~l3PzR1zv0~q=2Qq@sG6N1)+i(p)tR#+u4v`Wg;g2Oa zP6#;un-KH}G*$eO5Nw}Zr>xb#tB8g(h*EV?iG@VyaT4cvY;jsxvYczg0fCdW94uNp-S*rZE&8T%RcV}UF;1s75q z$W$0y@8(~dPje$_0YozOQRxEXeNIpyCP_haZ=3(Gq?PBt3%Ebnbn1`T)_xhB+9ND~ zB&{`H$S;(Yl??;wr}fQ+tKgF{{-#~MVwE&rK zRcG@=3~~Zgte4A;f7~ro8u6!Cp>^E;XFTsxT*o^kWM;D3F6o+` z?@5nGZ_AEb70J9%Zq+f+Q|qBUe|{0&TUu%NrEvw=e0O-1{ET@7oL6gvI<`aAk}69b*x+Jb8p5<7ko2lB-RJN zYNrw|;*Xw*qo+MUJ?8?FgnI?hm;K`gm1brD{~!F^^=BIYSk0(EeIe_rVsgFL;oMLo z1$|}Ngfmbl&;@t{EB(X)%!}B&?6rqVX{RYJtc@0#QymVfLNpQaMNfeqzTx1J`Od8W zoE?-kq6)xB_L`}6mA|iLe+lC!VXq|sca!nm94fY@Q?FPLgzRVCScS;WSuGE*$q0EJ zJXSw?cSbdDP$e&BD64_!ohN0Eh&`DVE}9i;(Ic2cdN$zzj~TsNZM#3|2zqxb9cT7T z4mvrbl?Ad(SAdd?R=C7Kk&ma0%m2JaXA;t&oQcRkjldqzKL_%1*!42rNtb;vlwgRs zQ#oaI^T|eYrlNKp6YQwpCht{8vlC@Dmq^+QElBedy(fbik;N}rKr0%mgrZfsZ!mhc zj>QcAE;x>!H)SXR6p|HK0O(YTaF}VTp1^yAS8l*ess*89K#70vmmhR$5$RzSCw8<= zW1IRviEzwQ$KL{X&;B1@&Ep3~Ya>j~Ms}vmfhR92g3$V&9`fnC@8vT$Y{yV3<5fFl z(%Vmk-N3lSsh}20HqkSGl{Bc|TpRmfeSDASxc$yElO)7B#OS#`v#6dJ;Q%AzU8DCv z-liEYDf~<%UTmbxb!9-O+-5>7);L6o5nin|w!)rz<+GLN2p2!DH|$TEp|siA3({R( z=uWkNhSIMhq^szR#i(q|^PqRKPF_I6OEVxnH)R7>hyi(kC7@sveq#1_Sbkr*TU zul4$1w{^h_^X!34#R<*Ob81G1DA(8{c0a*D571o915UYW3~s*&{#Baq|HR9IBqE50 z+2=}Tv)F$xh~%WpJrc09LU5wSPoW# zE9koWA7SA?;iLT(_{51`E~3JJyyoA_bqqo{&RvfGi2_5pgxgm-{`U*~_YeO1(|=#@ z?*;p3>Hb~6|Fsr>{P*Ae^6!56_x||fhWPh9{Wl){U)$l|`1^mY%7uU9$N%5shc5$< z&uiB^(f$9QDeAM!+4{AXxjM9+=5x+!6C8100_AAfzWwl`8QYkT4juj{C;vZxT8|-B z^R3??F+P5>g4GFo&l{Ia|7X|X|NT5^OHg#FJF?LKSqA(cPrAw>eS(igm6PI+Da}9g znty)!_x1i+i$8z*cm4j4O$D~&zx(CSUGmTU`R{)DcfTAT$^YIT|K1>-iGXRt*JJSL(91xE^!*7tBx$U?*&qGCzq0Q; zfB|tKgdjQ(aN&O^o-Q=Lz+@*I*i)uI4X*(GZjG^09se52v;Rmi{Qg^C23?;Mvtdy0 zpW=VKa_E0a2oJxw?T`&Au%|@C*_2!a?M~k95nc2Upq+i{fxyEQzB#XH?d=Zs=H?aF zJUd2h?g*5g`m;%S;m#+}^CcyXV)*B`zD;zG<-41=`JU49aWzShu3TQ8t`EE>>GAPD z=0B z1Q7Mf1_sU#HcAmZ;@usy(*N{FU8G1>&~H`-VtvWtr5^xCzu2KmT+8@v0J^fV1eTfy ziCADANH!t5H*|J4_{xQsr+jzMfQg*S6x5ZvtTBwCNLyRazB>2C-gyBSXbk5Z!{CqY z!B-}3R{SCVb%Inp_B$1m^bw$e4t)uyQbWY^YOjqzBbMd?-u&TkfoDk#AP8sx{5dLr zYiPwx$YOxg_7ETzht9Cy*jfx(ZCUxMt&Q|Te;9)v-g5r&p7r70)3bF*08@CHA~rSK z3Cv<%Is=sYl!R7TlTy30<6`1cjXwai862Z?M3}5*!QkoXF`h@)dEDY_HfaAZESjdf zZZ%i}yelcfxI2d1Gd~xI)7%BDyE%qfUgOSjWPXenO}XAuG;^6 zl3G==T+f#*@TK6H+dTR;Vi)9&Dy?qv7#xLJj+K6x7?!1$l&dFTUzG#mLXBw&Tfq6N zSwJVmOsjsU)#{j3qE||OjxJZ&rjQQ}Tr!_IuYOz_;k$S#;cvD@6VSoR37}l7JxT5y zW8_?JS+3-+ZQx#AgWuQHA4}xtd-KZ(jFL_}-k({!7in??;JX@gt=icD@02_bUM(+6 z1=_3xL3cgZ%|e395fO!P5)e2AEmF-S~RFP z9s(%i|A7zGK1B7|4=7xo^ioT?qt15aq&}R0PxrV(KE0)FkeDy#RMj6V;I@5YR6dBS z>vkRxqsjr$@eT&7WMS<*lW@t_Zg&2g)V#xaOAYyvyEJ{tVtd2w`9M%gVYm=lp{V}( zfha=)tg#!>0wB@RPfzss-frCTC1dI*>9)a{j0PgE13)&FvF!v!av0xV1y&+Y@#}tv zJpgK~YUVN|+;`q?{4sU3|M393#tn=!%L^apOlf3xcGyZy(N<2m1SZ{^ss3&Tl-x3V z?tgh?q~o^u8>(#>*PA4y&)_^1$yE8n2@qMTJ)d2;x^;2AoiwjjBEWj zCxu5wh5-)7iB#{FPJN#@=>rqx(byCYT55^yPaf+fk&AE4dQ&fAnya^WY{yC;^4`tD z-Jeth+6s@HXZ#zjGR(Sp*)JBq3{UJ1y^5~NRr}&G&xVA$`q%6ZJYrKuFDmctjc`X0 z04}<3=~gx7F0c^O)~}t8TOzvv=Q`}${Xx%uszOu4%&f~zz1((cdVRB*8lxGn_iS{d zhA07@@n2dCpU2fYKQx`qD{6QK+~e=n>H;kfJO+oX6}fZQY@Pl0>N6KNpSrMs=(H;A zzX^e0pF1?g`ziQ?0Vq(O77k_7;Ay{VGrC7EPhP{+XG^O`!tbqlvg zUr4!;f0b7H{F{6K!H^fzp6g1jf4VR~f8RGrD9;)*9$4`v$N#>0&n-Z`Yv24nPPaT+EQRIlSl0>+Xk+it+ zcW@(rQ-3;~uq&~E3KnBeenHyll0Cai%ZYRe2=D;b#4kZS>4L{A0eJd3^kBxrgsYbx zw^3=Rx5WT`czk+c>hF;Ssg<9~6fO{4z|BXSOy2C22WX#f;<6{yUR;bIe(hk!$3M|# zd}zb$^L(i13hpn}u`*uPn*I zn=+L@m*(70+#AIU{{;>Gm>6YKPFnjlOj`CLNtfx~FEzlAD@r^(N@8U7xuWWf<7W3s zfScx$w1^--<|MP@H7O4F?{!NWK%FPPw*u;1dhLn(>iGSMqE&F43F>?( za9c$HB>~UFL3Ij_DK}Fk2RSwR81)z@0=J*w%5DdH+MThi^=L0&&T>|yyLqsc)@l$- z*gdH3F?Hoh!{%hw4lT$ci2WAf{m}Mde#7L%rn3j(>vQ3d@oPc2<;9fFTAEBqByjMm za?dH}FtqF(ZALG0KTQ^aWrc4U;fD>cPNlUTMjG1HJ|HBXX4(QC=eA)AjA!Y%Jtj`V zfKs(M!wIR;-GZehBA7=H$8Z#J=15|J$#EVC+!~{IBn{HwW##<_-UAYYs2`~(&t?i> z09rypVQCfcI<)|Njvw&_E3MSJ`i@rRUZeR~YF%;E+7dvNX)7n`Z5EQ%I)eP>)BKD9 zRsdi*AVU}BV~!bB#tm$frt$jLr3oh0adceIGvc^((tF#M7L#Jox1k=7+0lT8nulOl z4(lAv9%Mh_LT1wqxkEt^9xaWF`B~gITK10vlt1 z5a{U+Ji~L9ldZZ+L9|-E7OdLZX&DC)4(XHBK!am+R|W%8jqE5^YVBv|fDp-mdj%(E z0SKK4BdEpKi>t~r<_T(W6_J(F($X%xZq>cRpR3?|6Ar;3i6@}FUXSkwC&uGYduxtX zW_S!7x8nJ!**GiOQ*#VVoV{mbp)PY-} zAJs({i8y#Ca2fDse4I}$=iU>sRDhY*{gNzq^a5k<{;s+gSk0A2;t9cX*uCpRh%(sd(#EXl)BMgvtNTnxR+)CQjhARdGbghF1#@14!H2E?t814 znB72jp^h-AnwY3@GjC;$WRd<7L1iw?CQazU{=`<7vu?$D$)af1^AcnC_mLap=BM1f z_tK(|j`4G4FDlmc@e=UKZ_jVTH91CAd3*ICgaf~7f;FYtp_?fClHFAJyl;)&n4dEz z1d=ePUt%1q!!r@%$hhUYyd~>Bs0L^6&`rRh>_8CwnZc`T5vCPzijqeYRin`5GPYl8>7kVn~47g+PAdY{8Uly+k9y8%C%igFbIC7jW;Y>@iO zKqjej24YJFs}%dPJE=U^q%FDY+pf9$;lhXRZ%2;cUSETU6YbGTzZ*f)i^mkm$gP{Z zvbsH8iRf+^zy41@?}N^cPBEVCw1(>7S{;V@j(+MOzERAwz2T>ist~-Os@N1CR0XUf z#lYbN(>M*-`SAxB-skcrvTYZ!{E{Cg6NN`H{ z6%zjvP_AuLtaXv?i%zRffZH|2-jT=DieHZ$oSEC`|6(5I%@HwJJhR}7Vps9vd)m_; z8BQ@Wv4N<1;IHmT$D?=Y@m6m;LE#!CJY__rSQMbYpBYVA6)EqOE!{TR!>$cFOnZCU ztAGri)U;E`m;&z;%`#DryTFLpcY2Co>lK(TeknV@=hDGS>99-o4nc29KV5;i#P^x$ zBN>hdmyG;`D?B})ql+1k&;dyc<71XMwPteqA<7JTdf4NPy02-BOm|q;`+=$K4Se4m zJ>s{l+e4M_(}5HAFPVI%!WiiH_l;vAZre{WM7JpiUxiy396|DBKF&io&Rb^_g@A@a z>JAsVDpfXFOeoc}lqjtc@V2UjI37_4$g=8>>!N>a0Z`l3m241nQz?uotwsx-Ct^`R zTaXe6Ep}wCjc!FgSLou$+ovkBW5P>LG4!3i5m?o7?Z~qp+=#We)wI;stVyuVB9-9{ zAaH0e9!wl7Dci(-f4)iGj0vr{QEfQNyF<3J5lU$1{pzq_$ydpJ2!y(#zL3F$tFPnQ zc0ov7SoX2|rSkv<^om9^;~g*95dno#bculTtcCXZ>c#W+pJ5nsK3uqg>veZXOYUS| zXtxhCbSqw)PZ%lGk6-#(P*3%GvAsNY{S&0`yk2;P#8m{^pGs?ygMYQsnijJ~%yvW} zV&L=++{cNqsJ34JP-Y3-li|P>0lbR|Hx3zaFcWK?Ql-Mcw^W2ZQ%qx=&QIJeSp52q zT)l1y?h7Q}WyM6_O6uzX7F-7{UYp%S_rVY>qzUf@~E&eGTaOO73fO$jc!XAP|U!cG^^%4AU*V0M;i=e=m5F6O}=3K2b3a}2Kr z#p@?{(vbm-ySkwH&Ed5n@0%+$fo#ArNB0-+72rYic)`2z<3s?VD{*jAYnSpfPE40> zu~HSKi2=N^^IjH+g?L(i!blieF@Sq0G1^hn$Cue&) z=|A;CAONlULb%u%5ob3HvX0dTk8}B<0xU}pd%_u_N}?7ucFA6+Iv(4TERToDC-a8d zRWAr{2zAggIdWwIShDbTY{l)Eq#UfjLMqDSGEm~21I(w@tmKO*<*Eng}OjJ;ri=fGK ze6u0e>Y!ty+2yd&1WW>fD)FXzMQh9u*Ih%xrLq^6wuhKS;oiQ_;VVh6pd0y?J^G4^Fn56+&Z2bOoHblbWZ*5+h#lf4FWq6Zd1H5{MJs z#gk5E)UFkZYlP5scR6WrH)lIX=}>Tx7IY?%*26Mf5}>J(aj!Pv5LcXV2Z&>tzy$5A zE5px)nVpE6;kjbYJUGLr}|ZAt2}SVIoN?$YBWCiPzIZc#xe9NSXtci0A6HfQ$F2CSdYO^e$l3CM_y9 zAEqy6!rVG|3tRxfmos9hFxnsGWtJ9+w@ChcT!)np_w_7_r;0kpp^}ZeJMY?{9#h^C zZ+DqbI3nz%dn2CVfX<%*)*Ys6^QyXV{^|gXE&dvU;9jcCjMzzWdS-i$hfp1b4|eXF zX~3;}vy1H)@zzoc+r8Whp=w!OJLD@9#eKA>WU%5YSK+M}#9#oHS{=~!8kgMh7yS#% zwHjZ2SS3@VIm)zBG<4QHH%J=Z1=$;Vtv|6vd^K>sBgPc{vt-LC(t^julk0d5l*|a4^SP>WE(?rVooPTLgxtmWDRsGSD z_}we|wO$jRCEFg0zk4O0me2l0Vgb4PB^7*ZviJS#^x9gsmY}InEZEZo>COyjm={@*~*+lcyId5Hu4X2Iqhaz8N zGU<4|<%t_tfDt8?#P)Y6R>ne?meV)I_^%ePFN5AOMQJG9EV; z-i(DG-xvTpwjcWDm>cRY6^ix~h?3tlq~rN`{UC@pQaWQQH54BY81NJNBDHYt=Xchi z6p8UOd!y{f9WFN4#WWaRI)m6~8lyQRrZ27m>(d?;ee(3#=8yfKCe(AIm0PmDJ}UQp zvaRN`!8gQke9VSO0HQFOsR_U@KHh3c)|5)P&ka2#w*=AZRWVI*^9vqW2_>v2^lzDVFY z(BV~2Dk*DN!z6g8)Xy+?+RcFPc$g_I?kUTo^L(|tpr!up$KGn3uI-TTCh?*0oMm_; z)5IG1M$+daPdu))bv>r}YYglz`%&-P_b!*9a1&=#Czfn?Vwm%tkj#F9oOgSl3%}-J zqJ&^y!{Vpl0p{IFg~`Q|0|h|FuiCqzrmG7<2xWrw6i`!eEqGC1*^XEtl}*Zv;SoIg z6vq9{YIMS>I4{^DOs()MAdYK747r5!e$<-xG74J^m=Ljmfj9oPa;f3yI*Huj6*;LJ z(D_J`EKhn-9*b%1x60m*@e`arucuz=6;1zmk~AL!7e!qRI9YkSc=D5s3FhNZK;Xzy zq_No+7QxfJf;|8%)@}#!-Dx4LH1}v+N@F=3$0-)><_sxo7C3LQNS^_)0Pi2VJM2Z z**&?ty7$Rq{9#hz(Rx|8sKfezVAxbZsL1M~8U7`(d-dHT*khIV9~+3$*PKdAlax<7 zg+j!yycK+Di5T!z9Q@XQz2<5%hq^)U;ohjBqwq3Y%(}#q30E}^1Ln?>+`qzu`8N=(2N8g8Bp8@8hn0*b#k z5+=Hex!Z*=vZab8JQm2J3;0i`S32?%kb!a0`$_S#(xlD5zW&W&O@&`AMTDm$EW(o) zzETYog|K;3P8wx_YTc(h@rm_zJ6mZsjSk^!5GJp6q42wyPr_Ru*pG3UaUHIU5eaTU z8PwOwn>IFFg8yDI&q$~fs}Uj=N=>~#J%G$A{7nBi%gVBvKSP%k7F7EF1qSeu^%HAG zq75?=<7}|FOSVlzy}aEi0kwG%i}DNJQ;fCs?EA?)!+6uhu|lr)9;x1iX!I;Kflpz~ z_%7}m)>9gZF*P469 z_|rBH{V|++sVVVj^ZI!V1LM4?K?S>MNHRnYAD79Yq+3X+`rd1nh0jvX4DQ%wUg}L)qB5=Bsd>Fq3wPTeTOwU(53O4(Cc@6RqWeotue&m| zScv1+o5bLo@E0S7|2;o(1%z@nq}{I${z0CJVZA6!oMFvv;h~>| zgn!_+vJ?9hwTW9qpb8AK8}G>zOr&`<(Bc_9i&5rSISUx;4Nr_IWdqKiThAlB6;&;D zN7J1g_}Rd`a1#kpsm{uT1#B~qzL$93aTvT8bu*eY2=$CTw+OI~(|7q+JUi_SvGjXn zN2mv?Z-4&2y)QW*%LldjLnU0q!ET*EBdC!A9rFG&jnsD@!zN2kK|V0eM}ONfn1$^P zYi|;69ie_A1H3-I+LApo3S7_AXY@X^i6Jf^Sup4J?3ZD0=(bp^;Jp0YB*L}&M4iWP z>0PQPqsNyh;Nh|zyuRm?Rl>TNW_1*jvYj1}c8E~tUd}`OMgUom1H>AQW13fx688d) z=omoABd1I6B64u`%-%IR6MU)8@Fzdso}0!3(WzI7ke0Jeo!D$- zsf6|fG?H4X+3#fB26nrg+oHMgs~fy}eSPv%*$qF)J0$y0o3RqTHo`AM!$a~5(Npv= z@sg}_;y$m#MW>&Ki5UFy^gMMTlQ{0?N3?%(@a1H38*yTRqJ3kJb4zNLl7QTL3%L4o zR~q`69{hAxCB`?If0%W2n7>KRgqctSPXLHYf?j_CU&ut?s8dL z)SKwk%TCczpqcRt)&6cBHdZ=;<;elIe;)f|Hr-{Z-X43>j`Y4=#IA4FV_kwwCZnu_ z?|^+ofkr$}<#@gsS9jnOw3182A{5NXdU{$J0>iawdeR_mLgK6DAMd?Pui2<5B&2(N zEi0`MdYEaIwX$e+s#aoWd^lc;H)HI&_@#_9gQce7nnm^d4xczUbPIQ(2S;e$@v(QU z-Gmj0Orf@@xyxyhHq-bek&%>yTC@1u8?>sSeWP!>k4ni$0+a7`caEDeK3?vAjQju; zTPCBPkGfZNsKwtmevovh2HjIwXvhMwsH%I}d}_^`w%0}B2L z@o8C|x4~~Yo1YiSt)p+cAa5m8l?0!?2PO?i`mtI2;x2U^T$X*mo=tJRvV)Z;#+eQA z)D@>D#%0X|YmW?3G8h^Ff0fC_Au{?cBod>dMR6$PD_5i4dx}P>rE%e*W|^8qXpY$d$wH) zvnQL$uddwt^>V6Z(ckO^g^07jCUw$^>$FZrYv@(0B$^m<0>0Mf#j`jZUb)jtBJXG< z1?s}U+P$er`!<@rZBu*_67Ef#HDmr201Ik)NM6+jQ#}jwzF27BHp4oIN%HnhWnXIv z?p?KIS8+k$=VBMdGp{y1Z=UZ^cS4s}Lvue|;Tv*NxWJi$Of$U;xcqm^J|SpqnlzF| zCFdh#QPwJ;hXZwA9F_l?Hb%5*cc?Zm(kTnD7YS%pHq#E7gLHcUY}IuqU@r9Dh9zEzD2Z7gF%nY^?1(d|wLSWHn-Hk(peX%v!v z%wXsfSxF(a+(fv!h6nLy($XMBdJGG`XJV!o0vlD1w|%2*+KI}~AHXKo*B;N_yG{i_ z2#@O^KcpW?&ZWs#XEk*{z^yD0ucKV$Pr0jv3xVcf+~EZ4nzi8v0=i6a${n}S=*n4f zgCRq!f_DrY|J?Z|KzBXIa%kD1EDx100pzS}C(eX}c5h~~rj5}6(q5i^D#Hr#4jpyj zHxT1};a~LkSwm{()>)uzRDfmk4qF}FdN~?UuUzT2<29u!YcBw_1Y%Ck{7RW@AI)(L z+pL|vKGoZDhn0HXB_eDsPeXL7C&~W)C#_l+31Db!b z^9~`>`zl`e0RJm^H&(=PQ83;awA^cyu~w6HlfODY6haQD*GnaaLC5`91Abou{#ry5 zH16;N=DN}&1?}d>!?~tes_*lPZ1cjc;uIxKS~UhED|NSf-w~PFLeA1ihhoy7blMDL zxPL05(}_beLnS0YYxxQGZuM%ejmXc9@$!+z;hsa&-ZF2`lNS~ig7q+j?U_KUWBkPt zC{3qsN37XB*7`YzqV*^97MXmLvT1b-gGq-Y`k?Gy1x&5N2~xK=>|{^~zjBE_MdBsG z1s>iT(jS3d-{#lZYnWOTdfb8oH*PgiOK;&K`di@Sebljt0PerkPF^>|v$v721J7y${#QyRQ-b zfI9|{dQCvgiir2_a$C220wlQ=oeo5@bhovP zA_-kH7nh2)THdF{sr^E&PDoUBqd3GTJGpzXS5HU@^&InY#=*E7LGcwOu?vXW6k3Yk zbxwKmUb`$E>LC^7J?Tt~&B{Mk%S_w5o3OGC0cumZkf#i<>Dtb|@gMwZBGrGwxbAy_ z1j`zoZdV~2t#l0X!^r`#V;Ru*GU1h3%0)xHonOgEvZBIH#iY=c4#y;5t-*o+GJNkhE_YPOe4T?bGUUoN=m0pQ4IV*L-Kw|~u9a+#$gT{xm%-981 zu@t2fJH8Z~K-$4@PP!k|VQ#Hghhys^f`pbA&0}rv{wRo|s03ramhQIXh-*GHN;NY9 zDyzfVhK?NoGxKnCo@;G!XB!w~-!n97?Y?6EWB)XB0DLu0`Oxrl+xTuvFz<5MOl_4WDs~%~WVxdG zBMrazvQ1a*n4tH*5HGs{z0bX79hT&lP+AUE%SWB(s z+=Uq}R;TEonmBda6D@`4G!by2n#f+pym{|s(6bbpDCNz_d`M}bBOCCyf|5W=JLJPP z{m)=PX&hB(P_MUQCb@@x$}M0=mWYzk{&3Aa=~EKr`ifMRCV=xKIsy6kKB0R=`k#6d zoC=rhQf#p*6sH}mJRS}^?dW>$kOdLYu>~bpf980ZWei`t9u%8_deETu1zS@wadmaO zSuw$=6*R?-o&X(=ab~OhZUs(`ffw)3msnPeN#L=2+J2`NDzcw}u{vt9!ua9l-mCB* zVW1j*;yq=ZlO%;#X6?13`GiWNus)o@?Sr&$=)5>)@|>RZa{}E*`$v?lqrX6y%yUKR zTfF};d@DSE+fTKG5cU)Lo?iOf8ZAQ`uKO{eX*&K4ZR#LX@$^d_!l|nD- zn*}v&10y*ZoXgDkJ3sdqY_4w{n>%p{(Zauu0|Pg^DfcC|0&Q4&7O`r!qi=pK~>$oK`E49jGOGPjnNPVC|YBj)<4>s&OA z@|lcD(fnV?^nIb{F(Gs25zs!Ny`zV=FCgvYox$d_{4I$QR;>`?0NiAV87g_OVzNhj zb=dr6m38&>5h{Z>X*)v6-2vY?FsL1dcdj&23*0>d;9`mbKZ`fJjCsmZW&p2T&R z^{V1Za|C7!KX0H-Q87zkhjQRBG&A1YPL5Dpd|Qp-D_GDd^_cM#Z!$Nq-%dW_^0ino z?9GbCHE%QHY(^2@3Of90DNqCEoQ);b&9Yqz1JMdYC(->^6e~qNw(1mS4o6b-&XfF z_s++#!*}EmXI~8o1lK4?;apeVb0!6Z1b@UJecO?JZhnOq*TPtar7nCalpV9pZTp28 zbsyH`o@ID@xB(Q2d-+CjureU?cUge)-0v1MGx{WrJMX9YVp@eNNw@jMY3i_!4;hmY ztGpT}bi51`6UoZramg&=a-g;P=;vbOr&PlGA&I*7eE<$fM7Oh$b#*(`QBaKqzyvr< zFG_zyiHg?1pDb|-4_LT^f>_qwCz75G^`;7R9OGMH-=mIF#F3jLV`tnCim4m-_2_`4 zAdHV?0%&kuuDx!{#Tz{Te7@BHCXk4wPf@4_qLim9y*(`>F=v`Gt z%iGM{y!G=(freZPCq{S;v%Yw@xl;v@|K6q2zjx{AU%M1k{7@gFZ**ODm)$nx4QM=@ zlXZa8m>`+u<@Jh!`0(4hVx4E;%jOE`JP_MGnLPDjfw6x&@BpKR$4br)w72+>`00sN z7nQgJ$R*?B`1Vk*Hx_7wq$@Zzy6PrXv3^W)+Zgp!s4?YE{%xBfG50gJ82PtyC8wrJ z0~F^dfRfF6Wj7NQYOGWW`}&IlORxQY!x4c0xgO4v3$34X22vq;ZS?6~fe4@tj7w7qp2I-xo1}Axpi7?q5bp3#>`p)p9}+QGT?zNxn$N)@R0iB+$a6a)DZ#%AC$ zH`UOw)8+{`Bf^>C?8N${8%!i}YDRxSw2hW%AJ5lIzX1J&v&RtEsmXTbq$76fozlq> zY}9qBGpVo4?L7M;oKVHvKm(*&;3^RRg6oYi7$gT?Jwyo~rA=D@0E%&~h4vQpO-pUm zn+n~{pJglSeIn89B4t3rHKc=wI=-}v*xSoXo$lbcRTOUl`EY5TB4a@eML}G9*>PQy zjPFK%oRzX=*SJg}d3FCjMxIf4<5M#7On&qc>J6Kp#LrIctv9VOI zehw{^VO#(}#Q52n5#lG{9*w^-im6i2|9QIwhynW8jkny>- zyUUG2h6|e0LN!{&-RyI3D4!5B!}8;;eEv=h?j9!wRmI_;zXQ%p&u0YjEiQYaUGE6y zTJU+3)=)?H+X>6mYBGV@%#pAy7L4tTi~$c0!#tA|9yLvEd}tBR3ln*&ci?w7d1RXD z6b<{mIf^N39hL{>a<{WtF%vHJ3?4Fy}jd&a8UlaOAyej`wr*qOMWbP5Oan^PShIr(zmJu9eHHX=;(l_8(hTpRW6{rzq=4#}mi#gXA06DCIg&o{- zg#TadgZcfO8Jd;gXqDV~tXXzGZU%HL?Fp+HU!zyZ`dtVcc5?8eK4{ahW61FkMa-#v zgV@3y$``r3GoBC7PD1#pG54!!4xJt2+i$=W^@h3eG2W$e)9vVqc-|3#X1(cs4Wtu{ zK=|=zSEWn!*ISRpq=6o7TQ~hs=j9Cc47en;Y@PSnKqUfT`LCiq_QZonebqn_rCV;) zm@Z8K#Wv7JXF~0g+=4dLZ8tgkdW$qeYfhdW5I8Omp}I~~ykVk6$=;e+Q^hW4wR1pu zhJ1@a_CioP$=MYvS;$Mzm)Vox7He|VudvVa+qIk^Pr zzZf{LyZLb@|5gG5Rp5r>_f5xFNHSqHl4BXm^<2W6#|nnM{F6!2M7^j@fG!)sED3=j zt6jrk&0Wphn8dJE5$;?j{_0Vip=EwdXkV5xXKV^Is{teN6zSRU1?R&!yJ-jxkqjUl zccZL+8)7qIkhdFXX}cps!H@w4WqB@Rru3CckViFAXEvHNut#O?nMAo2`FZ@`K8*><;dy1d0uB$$Ee6@Khp#_-{PLyf_HZBZ-1!mAV^fDj0pmuXUfHcA zb~yDBz&kC$nAG`j{hc0@sWbe`0wP`;M3OXp>sAyONAC*1;0V4%5x!n>s+w-$@Ik=d z8Ztcg_GeQyc%suf0^oeUsTUi%9d2}#^ETHwGrh(sY+_gtM@u@1m41XPG7K;;a)Iko z=E5H!Bg)7cN3JC}5YV1neT8tix+j-z8)Xh4AA9Z%llTpbq3q8D@j*Sz2ahW3e6*?t zvH?FSP_HN8uwJ{FBiK)`n}JGq~#MK#!7lJ@W(g4QooKbymU;hXIaKB=8PAkB&QO4I{?d@Rrv=}Y?ml&MWQRS3O>Ex9dc@wVa4ZrW3lt)^mpqi-A1`1Q|7>@#w>SUFDxw z)67zetJL7YKP>R>w-0u&I+uT7o~2*gLKbAqNiBvd<%pDFqyb{x?UGmCp>JQ8-eqdZ z%!*mF7A&^K3uX zW#^e97O_&5dAfG&w|^}mL!37}k}apOmbGFzCF_;8Hq8r>xQ-lkLua(h1I*Z0h#0~- z$`r*n2}~z8R=?jrjz5=5L1KtU9n4C*6GH2rj|h2nrN23$Mz^YE?jFZ3krOh-2c^Z#dthKTyBjWgEa{VyH+*j2d4HUK0YL z?V(8sH;q+sYd`|?Eu)ZI!k4U!Z>z~AJq7}((~()z#5%{13*w$Gy;JI&(~i_z620}c z=3m#l>^f5gw!8(N=qifNy#08d@#yWv7@MD;W}|PIpDptVa)4u|9|LhaSo!H$#<>rU z!SB5<7&Tt(;l9t76xZd{C-%)=tH|JsH##}wJv&5eVA3i{f+{l`ckb8uj^wCovs?GkdhvPf4c}zDKV4{p zY$&50-ul4D!R)d&G8`14l5tzbNb)sIY3|(g&k{q?94~bDbM6|3%f_e9FBbuU!0hsy zq<~tCYTb4p7w(QTh)4f{77f|ls*hJfKE zfy45aZ0oS1-<26E$aLL}X(#wv;e=*K6l;Cmp6*sM5FD5K+8UmAWssfuyG)Q`i}J_& z3aX`bpC=+Px~&H!&K<1uD`MC$gRXT0fDzX=oNO2NrjB`hrrQ{@w3!UYO+CaG^tMlQ z1<~HF7j;~;RQw?ibnMX*c7u7N!&v!g$p$V-mYrMwS@{!ZUS09EpKEA~BULF>kt|mn zI;R~be@(@xQONt~5_{&YhwrloRm3|EKQghSYV+cUW$Qdw@#%Ya0ftuU*}XwUHcP9< zc%`8;v<<-?k*}NH1;027334*v^}0#ZV2)YCBqRyjycpWp!PDlsv|m2h!13}G1Ofz=aVU_Lpk7Qmsl3{2+bG+FZLUfSQx5QH1IyfOm98^sqr@e z3ia*rfu+WcN%~s?Eqe8s%!&Yy)=g3;i5Qe8j*HWe7^9QY;W9(Ba zWH;?SeO?pzYz#b;p1c+cn)L78r4NiA~LD+0O`$V9bVV`=^XS1#kW zzv5Vi))l-=!e@XxE5Li<6C-r@3=#>u;0O3GYQx1Z$qb|1O3D?Y<64>MnwX&T_=yp= zV%sSXJlD!g2oQ9o6YDLJ8+4%QXP2q&xDSM0`|$fRy)LKoUUhwVZsl^7dMSb8yPf?C zVD#7;NWY${i7mrM;l^lq({_&Lq4Vx{O~2o`Ztx9*rTpOSLvLT#B0I^{B$-+Z0GicT z<*ifiQM{$!n91-to$7arwP7_f$gm4-WXxMW$;Pt$k@pCqHMV9&=hrjOGHZVF*318W6r5 zHQa?g%@*OJ2w`G#v50U}Sgn5+GyPQt_-D(h3JZSF3F9!BJlYobIw&Ue^C3@rBxGl6 zv=1OM*R}{k zUHoY>4LC&dgU2nX5Kqh|A5v{?C?|T!)iwz9>$v$8rxxdjL^wU$SznoAb5iFplyR_k zenBTbC-$VBFt`A~_th4(v7BkyfD3b}tS%e29uz*Ut^vR8nRs}?j$;^91KAf@riYtU zTRQ0;@SQXf%Y=N z8sK14IQs6mr7+t7;L!$*CLlF!DS4{p6{W)OGD8O{^A5^`22Y=1*H2RM#* zDoU_}ec?;W-dFADT6U58eV36?xj z=2s`DXUzegyp0Ct#pZ*{A-omNM zc5NRP1SJ#}jUp{24bmM7ND0!hXhFIgL~h^3YgxZu&TI{cz_vaSsV8-$|gcV^wT1qN3HJzfi_SLN?b)g&na-+_Lr@{dpnn|UL82gjG^~`j_tbd#G5EeA!JmL^%hIsI zVLE|-TH$)xcjO z`=?S!ibpJNpp^GNPU|1{4_w8+e~P{CA+~O3iA8(eKUmVi4j}hS{=Us zv+4fz8p`_#sG(g`Uo?iH$6y{%FJ<#vC|KHn#Y+PLrPY;Oar?Q|o_6|35>7n1; zmo{)l3Uf@nl~7P%U%##f{Uef)IKkVjF~|c`bQa{1@9H9odGe}JH1E_Fg5t6>Y-FitZB_&a1-EL_Jm3`L~ z#$mKKy`AH`(#!u+vK#9Bant;GW4 z@EsHQDo_H^d=|t=thwm_Sl1*3$lKEuTJ$P~qJ*55;yxezjp)${l=z@^4tn&X-`%@R zTjQ51pQl1c!SZH-mJ8jYn=WL>7(|MC&7sEHfQK=M*ciN2>R z^P8iaz=UZu>*E%bFnC?(&A{(}CA5E5gJ3-*mc^hPD!l5EdaP6b(;)R9>*#kb?&(n` zOb43Qn?o6Sza(xy4Erf%QN*Lssu4dhG|7X+D=_Bc3Jj7swmuh@7nFj3n)c6cP(f%9 zx`gWJU4p`10*A)h)PjT<*JjS|-|tJXBK+C4|NPhAFXtP^YW>>J zb%f>%!l%)YXtOdr+zOH%kXn_OF+~6CsipjYARYMB{lNBpLh&Dwq|*^z=Nj#?BvOy# zPvrMZbml@+aEPr4v>{FZSVbh72%fGwvg38JX#ai);8J@Z^Sq=ZrJFW>dH4T9uaRMQ zv}<6f;W1U?klgG9NXvjQZ!ogof!9;c-gxkjyW@d!;FB2pj|couwgA5KuXAHyhzpnb zeTf1g5a4@}IhH$_($+%EyOjk9-`Vf`vH`$xYO``F`0$Zez~8aqhLERR2`KmMKb8!! zLgoK0|EGsHx1K31p}C^ zw)ad&%3N(SZp2W$%|BS#A)%aD!Z*;kfRq6y(&NfhyGjaJ(otu8oz0Pd1B^{*J^(_t^vO0!--Yb(Jq7lUfmx0$Va zn*_3q>S7-m`Csz$PK3Yj5JKigugLGq0FMI!j;&N+j3r!VF<&p}8i30u1H2f7)hVKn z9|_p3x|RVV-_m%!o1|jBE7ySKJ_CTVDGL4YzT>LCc$cYA`|}7@T)REcsl6Dx37l@m zR(lQFeM&s)iO)DWcGDM4uzuk7dK_qT_6y?}OhSURzRPC-n6nvQ3t<2r@N4w|o>@^e z&ZF5>n-W{uN%%v9d%Rd^`?{g!o<)cTKD2VFm77y#A~3F_2E`xF!kYP74PDO_%8U(0 zYn{l!m`DWvIYi_q=z4Bm?iqgB^5c;$!gF|=Oe+o zp*{I7TLGE;;u(m!>J+=mRaI3iFknOaHCrj~%?O*TxL;_Ib`xfq-46;M^_bjS{5n^saV04|@_gb6BR1kD!;KWT= z=V#)0c#|G6MLYJWtWOt7egY2GHpBVa(5L>b=}q#P@x#4Yzp zkf0)AmwFgs?9SH6F2udi1hdAY)70<-%mGx4Pb69ssB%`ZivJmTA5lTZ_I?z47e4Ap zv^;5E#7!pz{dbs~`?j^lT_o|eU ztH=?Tm(g-;#tLlF;WF>Bc$@0qtGvPKp==(yhvgOZLh;D9uG`>mEr4Dxo)6nL)HuL(NpO!=C0&1S1Rlbk^C1s3X~wyDawDngSKC$$%Z z7Zd%7JbgrwZ2@>3F3?ngm6cFpCQ+`19|A!5;On#%@<3K0iYWiWkNze-Fqt%-WN)0HkP z4_XVkEuf#bK#SK!ei~f9WpU|FrT2I^Mb(b|EbM){?Dpsib^ecXOHJ;xG(y3nCVX$+ zk?-$V%?U~Pr?3`N(MgG-M_y}*RMjTbY~!<%Y^E}V$p*3kF!ZCNKoR#aq5}=!(DH6? zfAJ0b$+`DgCC3Hk#p7LKiz~4>(6-f0;;6CTVO|qjB4yZ(d1TmnY~z1~81HL3?;niF z{@A4z-;@7QeU<}nMeyPOI$ZWwHbpEp|rImc;f1F4Q2V_XQkAZ^w~v zV}JGcBsv@88Diou0OqF!C3fAS&SI6*;cKVQ=1{9y;qQ;Ux(+}i(V4|77K~wd>0vi? zX|ab(!8|f{+Y?hZn6Ay=CtAOiO`?yc{#HN zA#OyBICKXOq*}K7H8=BI1f}$_u%M{;@$~Ir&kO9-t<}CT0|q@z9K5G&*%qP%8sXPy zX71+)Y-*0dJF<}6x2NvcnrgS7)#Kj5jKrsR-YTmMp`!_-iV`Eun?JPru3GY1Es$q$ z|4msQQ0N(x=t&ZcOyIBxGZYXcddkty%!oJf>jd;Zxiy3BPV{;)npL;P`VzM8mYom3 zKesJUMMlMu->AbknLNFVhHVkZ7g^pSPYYs@15_JNFrCyN@bB78)^ppNrd_t2#2(c+ z>@CAK@M|(<5_fpm)6i-W*pBdKBpLGP4?Xt_G;3DngfnWkacHCML>->s+0Ex`N|LAd zY@IyYkV@w7m@FBEc3@cTCwPYL+J{SnLm6Fc>){<1oNu|VqZ_%~Y%fjqKR_KW94WOK zwI?l1WA(YF&)hpiFFmEj%J)P&;Nu?zqvA2JE>_g~+VBofmwtFZg~acY=jpNKE@SQM z5wcck3rX6y#z~mR%w*y1k73aK;4PDomj8>;C*KmYaDcpF&2E7X$L61f#N8GV`u*?J z?t4N+pYrdV<_BWytAOYyTaOxB(%)B@6Jjj_=Tnrr+W}mxhqyMgHN-)TRPUI%{3N(H z-uG*y_~gRMF!QJVfu5xye!!C_V_P#QFvuNGK`&Rbwt3Pd_UzU9OtkFS{MLFuIVIw( zE>S*s44YK4CSEd~13e_xK!{O6)sn6}rYC)+gdw7+Q{UdRXin)b%c4%?AAz}>2x<*R z(~IumB`nCE_4W-G8!Q5Kep$xlHL zjeA&N(;(q+THL}m*1CMBY$-mDW4g=Q(@<~6b zZpgPXEaKhxz9$X_z%TSPWmic=JfKu@M}iezX_T8P&X_umCSwmSzP@S05{^k$%vE;f z%5TA(k|c?SUo8D#Q`WZVPDOLdi4>xg_ocmdhLnB@**Xezd+iXs61?H2x7_0g3yDfG4U$!WFlP3;8CSyhbPexwEjeC0xrAf>1O= zHPansQid}OwLxo2mCt}N+00nI>ODyM6^ivO*3!r4b(wLL^fQ&IzPOS`j03Q9rP3!q zecDiaqeJEd(rrq7$^so`Bl_2G%~jGSk##?W`Iy9dlq+c!!h2>QcuT_6fl#L{gk|cwGB(bD&tH$);v~-|G-7lx{j#TNIb@aGcGAy;Y@D0*-l- zMa)3Z)gubXaJ{BuS{u;XM!E;N$)$zz7QZCncR{{q6qEH1CMJ%4l^gnVu7FkXgk5$w zP5rIcI)bFgFR>)UKDD1JZ*(M-kg$d*Xi3P-kaR3nJV$mFKd)S-uY;`pi5M*68g2gv z<6uK!I@wv{oA*oYuyyoXG(t5l$<9SqC{kkp+*s2PWQPE4EELj{Ycf^(2`W>VeQiWH zn2VTNr_$hVW)`Hmn8-T6@ zSDsvvVA`+$7)tkZ58=P}wU7IJ`_~Je+~du$yu4u=gH4CsnODJVg*CGzec#`$tsDM8 zr*scsf5|8y$UEOx+zo zeDb_mw9d|6m@s)ov?P(b4S}Fl#3anqUr*)T+0Gh8jGQ;jyDnr#M$K2~nYTD~BKF4_ z&xl8B{4u(Ic=6Kl<;FrfRdGLZ6?1%XBaP#qFE2NYr&Hoz%PeUfS$DXXE2Js#e zO5sqXdBt4E7ZRN;B6|zdFHQdlp_aAp_OI3im9#`pSj@zJIUQZg*Lmz}*c2f3CVmGg8WxENLTg;FR773o{MB?d% z*`Dow%CIuivK{w%v}Kn>3{SR89%I)bUoo*3k^c*$(5-u~=FI{vhK0($>EjZo2vS4!a?^1oCdnX<)`u$RAe&-;!AY4r8?gw?G_6l& zn=$=jIqi6=j&jr+!{Ad(O(Q>u=SY57Db(vQH~U`v1L61!Z^Z|33{e!2Y)xho?8RnL zdzYr$g-NNeYL!Ru`2lMelNPOzb)FxmrMc7`S3iMc-MAK;j*< z6onnZysJy+*>VWC7>0fDV}Pl?Cg!7k454tn^4xPCuGf-1jxj#h&hTrJ#n5(Z&uCRu}gZR6}m(4fr=Zq6Yt5gBnwOOfdRrPK1qvH;#!Oc zb%!$w47!Zk!8VO|oiy%~hTTi?A6+s*9n!yw&*o-6Bk-7l;?tXfq@l4qe@V0K+pDjD*_5Q}aA)p3e&f#?Bv2*M|AoMYPC0(ADgh_A>qgzP z(qHq4iry{*7A-|=J>PMyTF$8AEh2hsy-rR|{;a(VKu#PHde|hrUN>X2kzw_(1UzEL z^j??Gc(Y}|M@#eT;Hk2~BnDAxR7}Kh|^Ai@$eT;t1rW+8Bt&LBaG%nrl6O% zsph2ly1X3+1?Dj*h7M}($q^f<#courxD$9!7X8-&wuYl;nQVUl;4blHhL+Kh)@pDLJ=NG_>>?@iIZv|TbFkIwI?Wl945pI$Fa@~>8j6{+Uy;SdEav(CpZ%ES^ z>Zg5_`+|TyH|JE3ZqHHANv=jL*@n5_KjBJ@iH76?fPkDhXu$eAu8p=Sn*=B&|JPLg+)6OAJFF1F-+q{qx&F!i;2(+jAeP-^iZj=E=;N{uvew&pa}P11^?MPk2Wz!}3TeS+~aR-e3pJlM|N z4gDtT9i8-+yV6vpqms5nFPJ$^N;-$kr|DJMFo$4uafPI)^-AY6rnBHSP7&zwCd}i* z38;J(?)B+GfWtU`e=7Yt=?Z$!lUVaTR^dRF=*64&G1Qkf;|23+z@XVdN(hi!v_Kbm z0*`EfbBXR+_lFwmF3=bsEg~9l=cXeC4K!|KOFI9I^E;hipf}s%$O{bAH-VV}>~ZTI z>gCUm)!(JLxen@A!@nvbQ7JTG(NdY+hXw_0Ja2Sb>G3$>>NXoK~nvy1VoXR&dOS?ux3sm*kg@PX3Die{BdrBKgM&7UZ1)G2?AGSF;Wa z@#U~$0dzE>gEo1a%H3|$5Xbm1GSFka!u4A+j1*DyNLFfYZDp#=QZ!Ml0XJq**L3aJ-fr3oF9Br0zRWKdx4j20vb8p3J}Y%eCCPo0J`JQFq97N|7n zK{Kq{gAEMcTFcJaTJnFfK2m+*MjBNlxcl&l#pHg`7lM z;tV$GcG)U2FCIDpLw2~!zylYkq5W=1-Hgu6_RxwxU%AEjd{_1s9;80#Qz4+dkEV&q z+FS#p2*m;3!-{{62aD3{yET{Dvi#@0j=_YNQF8|^E;!HkCTidOokZOGyZ2wN_BcR2 z)E-g|fKQQ^K0?@FrjodJV7dF*L0wM=6E~u0tgA~wLuBf1=6hkn2nIb>#WZBwg#&SyAy{#FRAlyEA&ZEF|@6s1%@+l=p7= zMn%ZN0I}5(D^$KU6c=U<&w7KIIh^P=Qws{*aT0^*h3o!z7(O!}E|6j0iyh`W+;h z>|H8lFx=r(%Z~&H?-VLCcHdh><&7j&TN(%pD+gcjS!KDu*VWe9Fd#x`q7iWvuP#Du z?k8h$siNHw)9QjO z80=q+-pO*7*ux%*28Yq}G3|O{51}hC+1+X7_!shoA#BI+JkbldR)3FyK7DU~2;3UP z0<^{Q0uL|eCPQzUeF8?#*KWtKGn5N*MqAJYu;1k_w1{Pi5jo9h5VPoqZcZL1fE5w? z?2|th)twQ0pC}N`G<-Q-5+MmIlBcxXDRVorn0xYq>RS>p91nRX6N21|NxiMtwcItI zLttJxiM?I5mL$`o0^3{PnX+T26=E}=5f37|-czw8h5DaL1u5S+x|6;aSWs`HwP^Nd zhX0+J2!6su+Hx>b5mx!s-0K(b_g7nfkRAa{V4$e@^oyy{5f(_)h%X7`WHOtrAHtK1 zGQ*hXKMD-T83l0ct4>gVm(rd zCFFTcXJX3xY_=y?qUgmWOV81De+vYJp}iOf8hICKc4AJ;x2Q`c3G2HOVygB920I{2 z(V?3*g)f%bX5&)5o5vucQZZ6pdL=hv$+i{>XKCOLyAjisAnOn=eE8|!{dFvg)5f%EwqftuEoO+PYrznHSZAiVzIGUVf!qm%U`2p)>?hk!bKCcMS#i~px`Fk>C-fg(x z$B+_P%++dEn5BeQuDWTcUd($9zSuBIw3p(g=yR^!&~j)Uus zpYBs~D!l-n9rglBRlRwp4|iQ0-J<7pYTJ)Q=^!D`&IjrIFTpb8tSCNS()cOyl}vj& zWB!n8*2n2&63%%2M~rmwy!&MtG`rGXzQXZ*MY->0?D5l=zh>^grTikU@`Mpp*q-Q+Vh* z5)5WR@U&lNA9-Jid%MU7*?B8nCCW6;Ol?27IH;++3;?&8n=;M_Nv@O<>kbYPJrvgi5+}cUgOf*?A6FT36{5@QMo4 z&>Iho_YCi{5l3J^NK=G(J#v%_%CofuoXN0oD{WTkm~PIfrd6_2KS{qFEiblQknf{q zF;B5bt!w%s%k32M8K)x9*9k}@ue%~)=~|iqQgxy z%nlq}y%4IVynXu8$ha8jkbKJ-*9t)jcXyI`o~!jV&idM+;j#{QpPV*`PK8Nb06nci z3k(XC1(wKNjFYY3H~@zq0al7F)i87vSlqiHY{+2+1B)9SGE4rNpZLku;r-D7#(Mpq z;Bg6_3s2FzQ4%Hk-r|(#-yehoCgq>Hj!EF2R)2sI=34pZ zG1{x<>8-BzlKpplzQ02&6PS3GPl!R3AY(eh6+jpEeK=+`2?Pi5sM3DDueSveXIROW zZJEL*Xe3ea1 z(zezXU5qi=#z)0$ZLQYS;I`npU4Fn9b|Gho&S@c1Q<3jidlos~BPyKx$;$^xRFby2 zu^HJwtV6pzjh@y6%a+YKOkeYR98_+}o%0zD8(On7mxk+KuyNn()ef-?v$PgT8*<&2D7H zL^d&b21zGbPvH}xj>0-ax<82gk(!ZmDzDOL3Dl9}GtCdZGlnwAQ&j-zG7{(FVADZ- zK(^KLa)FiZk;!QG*PeGXrL@cAYC;nG4|Lw)JPv)Jk!{v?y?F0J`*>4P>Xr}CBh;#_ z6ZK}jFI9yIR+=T@#=dTY+0ck29kF3B@B&^A z{w_B$~=h}1e%VN1%J&X(KGGmB_O_|*(Wy#BLkJZ1)JRVIE z;ybiz4?LGiZmta3fA$f~Fo##>b?9p4IxLM&@edc4&vY2pKd$?&o8Y{NrT{GPDaC1{BX%iCm#`h=KWm;GXN}+jYwr@JVIewgHM@u9&%J7i^$e)S4AjLRN}>Z&4cQ5*a4WgL49PDj8oM ziaPniE4*xl`e{t~aWii&&x&_NV8G!}h9r+?X^o8Ogg@3duH*Lw3rIp&%Mm>Bl0p}W z$uOPa$ex=ilU;K6XS%5~YV}P5Ai%`S#Lq8%Xp zr4uJl3-a1Dw`wi>FsoV5sG|sj$nhPnB$~o%2X(m~$1auIY9B!tfzu)Y7csb? zpQV1>36wPjCuVzw4fsx@e$zu^?L}fRXGhX|40DIgzA+u5h@wx%_hxdSSIq~`pwQi6 z={#+d@ll8}#LOKF*`sOsIUH|aY!<%eC)=00q z0dh~9F^bl@&A)^X08*ZEfMFJJ5;NnCY@OryqY<8r57UQ(T}P&%)+Q6qZtxXe_FD2D z@#DOE{B3`QS9t2zKh@i5Eq;pPhyF(A0d~wEOiwRrHV!;GVV5FBFk`AG4A#}UU+s`R z{s+X7!@$jB6cOGbfWjXV0K+?MzNM(ng(Gk)`fSwlS@a+m_a-pYj)+x@B zwLR03n(mOVteCoxW^ZCmX9>Ee>Qei0kWWkb_~!W#phwURH((O~nLag-~6fbr0sMiacpcXlX`u z`3VTYwjZ>gAVot}?8iR){fdAmL&!)-m`b`LQPP=d37~+YIlTrlv>c16j~g*(cRUNb za`u{tB`IBy=YRUd&@+uyY8EHVZgdXp5B26SB7B&yREFxoRMB#Y6>4=;f28FK^QE)Py0ZPW3#jnbVsFm|;Z-^B8oZFu5RW30cpRZ}fZedXc zkK(Qk(x!UI)z~Kn;W*7!+l>G&Z(I5&RL}!v&c$PdruNj%sr0M?HeK&N@4hvUeIv`W zo(w%xbM124#)%@=bL(qDW1lp(%i2v*sJ$2^fqkkv?f7C+iUy)Y_LNE zrK_245|Snb2&)5H+KcA%^&;agCM8m&N-g3bdz(*~cjtjt3`VUQRDU)JR%f6IJMpBF~^)5s_JLS7Hv z*9sNx zkU?t6d z_pj9NN$BJ|U^R6w^y5V6!I#2pI_RzI(62rC`LHQp)5We~D$Q%<%Y|UwPcaKh?nn$t zszpD}XOsdYGa{liQZnUUx^181BxD)k#G{e1P-7K5@q?JrR~WLKnVE`w&8> zW2YATtd;MG;8GU_0Vp>Fj_yC3BDs)Czv>>*Z2P>zyHthP%Ah&*C0))BvfhM_MI9T9 zvS^ybjN#!w`x%FJ{j#H;4)b}QXbZ^zevOp{om}7vRi4}RP>GXFWQmq@CG>|I+6&z+CfzXC;y zsX)D7t>IQG+zgBkr?U?0mTL%TF4JZevCmiZHw$$bH=@|;m2a=s;+ZR2k^)+MAD>_o zL)Lr=%-P|Scyx1==to!wcnv7eGm^_vo62kkcD_TMHxM^=5|&)-{LrZ4N|`aP+whXP z+)wmDMBc1usB3ktUGUu+y*|BdbB-f#$BWzmE^_|W<^lA=8QZW$Pf1OnZ^R@{&zqwe%bsBhOw9Beoa&c?Z+@PZuBq=)LzrLS`nSQ zuiJ~Gtbx_iR*rMTQ&F-nmmERqwspeJNO(3%almG~9`Jf1#2^1>bBYk-#s%^E6U?3E z`pb(id#t@C&C;YS>W*c} z75ZYC-yLt-xk)81Wa8s7@weKXb44HRx+S{PXs0@@@S0c+WDkpP>Yat9ENk^G_v+Bw zOAQcO1K`1u)ClZM$S*nv0$;(Huoas=zWVBGz-7+96y0`j-cNGU>u}HM=(*(U`Ft>E zGoZP-8h^Wf8P5`DrFMeo;AADsxh+St<#hII{CL96x;5hVPkqeddq;O@!E9dq^qB0? z17v_Z-Z(+MYi5Va!RuBFvGV-|9qMF+hYV;ZK8jfnk3Ns0%9I3%?z7)dO}~6smjLX3 zp`%B{+KD?*+xU*ldGns1lEo<@+XLo*1}jVKlQ`N3?qmu;L6mWmj8eX!X7}})nv9H_ z4cgY5t+(y60)z&+tlMQN@MPXpNOYDCb51q5q~>K3oMJS_f)g8abva9ZG1b6uCS>Xb zvzpQVi>(-~n;w-7uCQbOT#lJ}N&HfHGN|wQ^n6kmM0DKW)O(9)*iSE)gdQeExVN;^w0ZBiY(erJH15&yLN%Z^X?wG;rJ_H#9* zfZ+0E>4%7_$Se<}0DPp{;=YztOtWcjhiTynpouY1wPsTA_+7)r=o9wJxLRViP4dGk zt%fugsBS|qDr0S{zu!HhkB-ceV{o|`w};6CY2I4iWs3IsPTf}Zb@PJiX$=l^(fE;i+y!*9O9NiZ)f=(-%%G4gA{f@fjy!5u}p?KVP| z|0TdGr`)50l#^Jt*+7&9u6|upeMxe2X@e`!S$Q1GPyYQeA;NOYyY4(6RIH+59m z`{c&!ciDZC&|fr3pH=lr_}tU?D8j6pc5l}TSqJKVuE=+D5`wZD6+CY}-4#51pt{V! z$ik}KC`kW6?BwBtEsi-ywhcVQRFn?%Kh3}of8)Gs@3mneEuF7KnY*8Z9`{?GA6l{2 z9l_WAR|^UMLtO`?^(o^yCwKST>)cv$CWN~Nxg!MW(&Y@q)0p^(;nwq-pPm8MF?6cN z)|sbv{e)Zx_dC!Rj*3%*=K<-%W+z#CDO~dAr-oN@d3ErEw_p8+~<}r|W)OOfYe?QO=K#a*@&wAlEexwTmet1J(!-tRra45>7o;g}D!yMnY^ zk4et27;n*Gy+$=&t(MkX?nJjKBZ+od?hMnY!#3Ep_;|@f6$Db2+i>gULWjLMjjjb? zxcKt;Q}Jeolnt@VHZm2%=9>U`8P}1DI;sO;F~1}g7s|Lyu#hJZJoRSKKYrXFxVHADWQYJZ7}bH5SG3{wUwCLmZ-u?sv!?G}&Ltz`1csY{?$ z02Cfjom@_2d$sMaA6RUx!r3*T7E0|UFaPPU(o?RQ_LDeOpblaaYFr0xaO*3G!+8Ge?s|Q) z+#0*)v)k*v+hwu>BL~hHyY1ENn0njAlU+Br-R(&msYIPX$DOi|-0nt(&L4)ecq=cH zamj23&42zVKbp&uagA>P{+QE8@#3*NRcEHxjSNW<$ML&G>BRGSbLBq*yfLKLcerwG8bnkIs#~Teq)!7oB+nC%Sdv1#U;PFl{1m&?yt&Zf2?!83~wDdR}zCYT_|c_Qk?F&+aT?}OQ#uF1TYD_f>*iG8F zyy3(u)Ju(uY%IJ#rL5RP=y&+QK0#UG5D9ZS9F2gQ2&?IG!7h{AQEJrUypsgTa zSvH`u0IAQw&d#|iBR$dRZo5&_g;l~QQM#?O*RdRiofHz%?4dj2>=p<)7S6eQIxYDxP^aJyEsIeTuvtp&{i%);d( z{Z!WNiwAUEZFxrGRzixGGLR~5gZr%MM5l_c0Fd6F_iz~%2kJ(Ax;sdI&@0TR(;Ln3 zd$p)TOAS;7@UfO-srUM9es98`djAF~rRdLkepP?_=!=ZI-|x*D=tne|#Bco7)}sI_ z3#zJlblAw=GI#63_Wc`~n{(9}iGW8HI3azKpiU&&{Q%v4R<9!3(RCeb&{ zjmtl<>nBB!j%6A=55z)}hMc5RB*xs+YvY=^Z`RVI3Pti_-&-+_CF1M%-;X~1Rf64Y zr{1sA0`-5>((Zr?1EM~sL3&soZ8_2$Nk;lDOJ~R9dI_9Rvbx8Y$+xZ_@OzjA_g-zZ z9%IoB;?XlcGvEok9*|6Et~0p&B)GZ4R6c2KF^O5EZR^*wSCkHb`$of=eP~z> z&aX75Zr02#epa|$+EYCqNpxecoD>#sKl^sN8+`C)e8LowExytWvs8~l9+i0bp^hD978IR|O zjjk9h04C>wpI{2#!y!kLs*i)Aq+Ulvwrj2F`BbW-z@^zdzsGG>U$Dl372w0dNTq z9#!bJEoN;M&jB$_t7AWbEB^1ij@SiEQ15z)P~gW09eR4ifgPg+c%z^$*hLe zFVxHFWYT7Q@SwS?ed^lz=S91qJzvs6eiB2#bCQ#Fc0dH|Diy)3BIKasq3Sn0=ovxX z>FPbIz@WXk`*Nsm?+b(wTKVIH2*fV3u;h}B_{2A2uzE9qZ)g@sk|mv8lF$iGvgab$ zir1#S(C5CLV%LqaCXBby=2xmoC-Js9g5%?UFCa#+2DE#)EBG^il4g79oBPa@(5s3VQx9XfV z^N8HfNY$m*V0*(sPuVz<2e`MS>MG_XgOmA$=3f!}9=;u`{`=X9W(6;6vQ@JF*ipX~Ppqbx>?EN~^s52n0$Mz$HD zZjxi89wqTS&h;_DU$pCbB{r-(?WF(x`Aq`9?2wreleq!%g-D|rl54S|M|0Ynivf(R zuMMWhl!Ugm zw>PWPTU-JA+q7rhrMCJ)2W-0?{mV{fh27e3oTm8-x4rCn6L!SpFc57Sosx96}!&&s;u!e zQ{r`Si1mzZ!0t)X{RfW}ydH|RGM-y|9e3=5)}G>&?zqO6mmd-UDf`Qjl1?CnJ+H{^ zsR;)<|Ag#@!AI%2AqtY4^8tr!kePF3*PeLckY6K+(hxQ5x94n@4$G9u^~tG6Wyf38 z_a!W?U?ExiSK9{mbpdQO@8R3}^FuuRB6lG9B-i$8wt3?kyH5@U_C0V|TRqDq&B7@66W;gv3<_5B-;9$w+F;0${GGmuj=J* z-a=wUWW-01dPu52WDAz4PeBx|$nfp`>9QB3;M^c=voWgdmrcayzT(&tz5;ZC<-p`` z{0-wJK1JuGpuBDA_#)x)(6=dJMqLSg9~K8k8h6mW(Y*7Gw0_8(oKfh93Sncc{OL+> zY%dSSoXTz&Gp4yCKi6=B^y}KN@*sF)u7=6iCWZ&Vx$2`?D{h@>GOo6Ob?#5|+I?rc zdAGr6_rg?f@du|d4Ogmn;*zT~*A~D*P^~pkYBAY}Rq zW)3gs2Zj!qlk6rdt(Fw`=G>&X0rIuZG}U;JOxMlJ-ww&%scNpSz#k(XR4Hlw+*>nQ z_QyaL?9P6Zmy)Xx7%tWaE7I+PW|EdM!X9W5XN6Npa^q_3BeXg$e!+p*SYIJFlECW% z#_A|o%@^+6JXT7O2S`vE~f0M6=1N=#`b&ks>QJ00kl_#JUH`a>AM+1`d- zq>@!Z9lO-^rm~?U;$f}*Y8)To))0l{Y7Xd2I{CW%Rbvx00NAf`#TweG#)$xcuwsS5 zFFMQzDFa2lsJT^MMmXlcqUQj0;&Wh&40Crw)IPw3FxDMev+2DSeV#1vF&aqtDPXIA zINoH-e}#B*7Mfqgr3%L!XE=9<2m?X6o#t^8c?{IKs2q4bsg!Tl+4rFW*E+;ju<@r; zT*C9W=(v{XQnW_^pZB+l-{mP5yKetf3QzvV9}5ZAw27z?Hfz3g)NJ>M0q)iaS|QXV zv`psEMN>KRZ-u-+|JCvIQ(=Q&%_-x-V$gN?_MC+KWsT~sNkLr`?f+x%E!e8uw*PNI z5DfT4sKIc6D7x23-^;#RZ zYpwgf=NxmycYKClma#x{*X4&~*J?Le=hJzD>xWBA;>s2;3u3Le^1Lr`4@q(`5YDwV z`SLzU1cXVb@L^3Dbs{l=2{--e;3-1i3E&?rnTxS+;pr+?@pug<1bWrAnkcD>Y>}>& zNscks#U$T%>%g<`CQ0if->9oFVXvVhshluouReYhfwb1CcgkSj5v(eq^8LWtF?ALx z<_o^BU4KD0T|*b$I(NY=NK5wB>2iR!c}xuKbF2w^Y!wTsZO1ygI2$E*+Y6`TdNs#5 z>xN5sU4BzilR@7K`3#z4kQaTLCOm!nv=u)DsOU$=t;QV7=P&krU3g7#3+&7m$sJqO z5?H5!B5(soRe5(ApTmlzYj*qG=cGHDCIrMhOEb|xw_{oBdC;($eC`v$w?(?KTq=}} z(QcelN#Bm%*=$Z-YKyD}qThW=f0^L7{kJ&sYmRS4`b{|2&A(4r*0s*3SxIzqI3*5s z)>Cz{-HB!1f{(;L$9ol%qHSHfC)%Ks>`)wB8dX9a&M~T0*<#urs2J=!FZTcr% z{hytF>~E0XUGk^^=O4+!ig84exdg*|7gUUs?jTWtcFohZ)j6MobO_>;d!ATivutKl z4>08DNaH5cI;1Qm?%rnbP{l4SOJjOk?R@1rxJz*=-WoC$ z8VoEN>XZ%00r(eB~_)tEk)u$_<3L!t>x2YZn`Z#|qtHJyZ}(LxK~*f_b?{zj%K9%BK!XjUFF1?XrfqUsU>6c6X%vfFcSU%x zO5IP!^;5V`!?c5X*x)QCSH5-}O7{*Utk!UQQDzDkcA%ZH((glSlJI6R(o`Kuta1}_pR)-*whp_;{3mx*D`REDLy4^ z%>G)vnnJK9hOezP)_n+dYQVTNqCTQYgo&kf;`N;jua)1|^Giq!Ja%()d}lOjvv*2M zYsGzpurBC9vSlz|a}|6k;o*B`dnl*TI(X_neWvLT7gP>7!lLaQtEyvPLncfVEn0c< zyuRpMf=eRr>VNNn8Fn8e3Hi2`Ri2tSUmiYOt>!u1>%QjPh0M~xdF14EcdJIf2f6lg zjAP&d6usmWQRx1HWM3DVvxu1~m{%6K*=G>;3*~ z=*jU{Xlpg7igDQbR=HTu+slHUs4tiRwzecMj~salNR67Rv940!@w~w=<9j zuca{|EssS(!1Bna4b&vQXvJGol`E@($X=ksw!)pv++=52SJO8JKld60y*i~?)*P;L zLbs0}`o|FK@U`#5(eicZovK*JsGogw2FZZ$i~+g}ZR)&KXl6iTDU~bz=+ojM{F3O= zz^K}}{;ka2wI`=R*&BQK~LKA_!;u4SGk2b zHIO+a5IPYaZ8_8rMqHBJJ#sf5NUh?a1K^wLtzuExFru*g>y`{J#)`B%K5ypNG^J}^ zPdlta_4vrQP}xC^^Y9CCh%(*b)wy{Nz(vIMgPgIPYEAOOiK=yPl%9F(P4_wok0`e= zz3V#WCQ1!6cu#dAF-ErQypdOvCuQmm29bkP)PiMqWKOvDPv7I@%OB4Y)TtA4FY(G0 zR3GWspbwI3z@u1<(QSZn6^`}u0lmcBFkmJxo8HftKTeH{96))zz167xH7Y8xD?cn%6AbuUW4ZJkcfxId9HYI%878HUb_Q-x?6Ia zxJtW2Vw(1PE^>WPly2a3E924VYjAeR_4BatgZlnJ`hD85O;poIC{B-ziUt#jB|(T~ z+6VI4*^@oyJsek^JVLV$t2`O-x{E%n_WeBeRD}v)qBqc2TFCM?&G)<2%Txpma)!jj zv%8)nKkiq*8s~w8%?_kp)6^yI@AuWbHf)mrmZvN-yHB;sDY3<~)dn;ZYsIR%XNYH! zYP4gYJDB>|RqOV;AU=M9J7qo#U~FC_v|y}bcR=L)GYZ~Bg0i8d!sTH>$k=3dlb8DI zhAUgieZ^p&%I{x@-Q>V5t|$zw!vM$f?O-l5CT&6BEFI1yTiYf$^o*yWFLr=jqLEiN zDBRtQ{xRb9QcU3;jpIQ9wG54;?AEJni@EP3$^|0O672MAAkz6H7MDdSpKmf4alOPF zj6v>dd{%jgYD_(lnmZ_pJ2@F&l;w1szj1@DE?Og2?JwYPW#65=4XWlmGrVqgxV%4g za}7&V>H%ZfF6Xq#P8OUVt{jR$s_`rZbZOR9SpjuZyo+7}vtjP}3Fo(l>)9li2Sw_0 zU&6xbX!os6qWf9wL8X>P^s9a{mWbSBc~YVa!cJX9!TNI$s>?5SgvcMyoPQ7>#4SQK z1Mq+5Zo9iqahZ$~tKYDvTn>PXEcYoX`nbMC>e0$i#+b+JA3HX;?xOaC1JvQISq`I` zK$3B1t0mE=U6Nn+oB0iO1kt}DR6rvW{83?h;ppJqYrKcLyG-KR;EI|(Q%CF>DdzNj z?#V0VkWSIQ({R%LG6T7uQ(ThVL^0?xe zimb;aR2Dr!Q|~E0!-6;u%;YyeeaZaV;uzxMo&+|TC{SN_6v8`9$a7Y z^DN0}@2Uk1QEnCub$qS_*nY1kx`fFRR;wOZm}ng9)B-5KnPtb>`ZC-%f-8$CwEQiW zg0u%7dDjR8frAmVsAC}vBIB&+Xppe48{dqF&0Vd-nAY(lD(i1C?0~cdOT^aq>x~?S zjdmP!R)Hs+wddR4DbU2r(GbfCTn;=8&o@W%MlsH?PiBxk@yYi2E)T3D$}jbWB&oI? z{H$3`s^J790qSH0qz?^Q*RzQCzJrt=J~8uZREK`@IstK@2x%_g-12ST0N{=$?uKil z(!MP#s+~Yw5P0jh+vXu<>%BSQ_-b_>Gtsd+G4ZVWYSvoN6o=x)wWu8bJ+SnmzbPH? z^`i}$K7yio?tq?-w1d6kmGBgIiplyQ8ni`bovttK0cA^0{NMsNVKE9A{kmZMOFnzc#D2AY?x(_BFm0U#*KePT zDf@f{5%dwm$8fw)UI*~C5U(p#pWWplNs)EVK8BRjke?x_^^MnI`XW8@bC$%RXzrc6ww%3-}p*Y^f#dWHTCplG2nBN7E%o@qjn0_+7J?&JAI6SVx zd3hjHW~*9?`SD)Bex1{CHs9qsO2=UD8L>Hte%Q37{5louYSXmAg^?2&1r&}@zNi~N zNDtL4ShjUOht6j!S45Bh6nI8BA|$N^oS< zU?H6wZFXQW`dfppTdEDA6HA$4N zZc0J6DHRbbLT$SlnT}Pr_eWaA(MA?BS>J>;65+W1O52b8_7hXYG9WGri|KVxb9fge z3#JKh3kgo`gTalAu&H7Sz$9ORpt|*4lWiS$fyBAPCo_xtD2FJUstR@0=TsMApFr4S zv{&kO?W$sh+qw=U6!fqWpgAUj@KSPTJ?jn%u8x*i0xO5`yLUbhtBpt3*( zSR@Ofo;z7m#D{Mu-w5w>k9ON`*zL2KhH=uh6L_*r_+X-%x?ep0u@^u-jb8io63A8} z)s&|6x^XX)9b7MwUFN{}1Ny~KHC8$0S-!=C2n2#!NmaMYGKsU!z#)M%Sb)zDEE#l? zh<5^d3i+GNwA3teqS*JJck{D8FDFPqcB|{pHB_SIqsCHn6j!lJsHzR&B@8HZgyD7oUEu9P<1CTO_%ooCy+T4~VC#TFB2HIVhFr1u1qFfE-6VXXdLY z-`EHPrQK$2B0Q~La4A8$T9f6bvVh^RAcWK_dDj<+WsJ9-PQGgbQ(!{FzD0ldF2irx zs+yLA!(s&t18%|f;Cam(z#9|BS z#<-i}!5m$m*1Ea}F994Zb;e;euX27pP8F^eu62u2bN*=%wBn0pW*{lNxH-sOZOAup zcIe!8iUwJ;2>2w@nUr6Sda?g6Ax?3&m5t^P;WU7smyfjl!Q*`J_y^1{zBxT~Knw!{ z4o9TJc^@=Oh$!Dg#vNTWrz384)3HJ+7L(Yt8>jn#vTz^3?h=+aG>Z1yaSGz=pn&h8 zQ3jf9l>7F=74RQ)`w!xA0XevygsT36g7jCL`jZ7weZFC!{VwR^J@4`CL*Mx;rKBkI ztBp%hj-#6X*YTpKys6~?%1VF#x_kcz_Ewrt>0hbbw|IF`UxnDic*HR{VVJ5LGsHQP$R)T!Xy3V zcLkbJ_Tc_@Lh`fzNYy5En|GI6E(#xl!_Jg9*>2(~K$o431a!b5U4R)6MhmSk^&Gyn z_@Fwqg9&PtMpKnxzya|CHJ@DlhiW}$uzSwC zje#fl>zRArX(Rfb!TLbr$20E>XBhq0ZThP*J?_P?M-umiJ&OBBzXrG^uf!Q?f4iPr zNa_4HC;o$Qec9hjAirMw68&OdWHswH`TR1AB_9Ne5P$zX?+E@-x-IV#ogS7y-umXN z@{|45uu8@#K`Qx|;~^ap2xMY^eO2({=?&|a$9w5f)W4R?-|HszuSb(@q4@pDuSY{| z@S6H(Q|wRH5C1&L4UBc3`q6&^m-+ow9`y^9>6H0V6$P36VUoo^2-o~~f8cVTI0*lf zxJ&3k@mGR{QU%Ky@kR*;RuS^!2LZob2gz&pn=?XJOOjFTCzQmxfN6JG=chQJHoJ?T`-O(B%Q+9|7M> zF8waW?MjhGt@arhH6;Tk*-A=O`D62`fe?J)ROO*WFz%_8csz5jPMOJgakh5H8}BER z-n=1T%I_P{`O3VUrC1VgG{sK7*v0H}#tn$Iyb%SUsVC=E6s3*|zL%cC4e1f|jFPSf)>onhQW`)uK9u~a6 zzZqAkHvcwHi=WQzX`^#v4HhUfYW90(;>J3a6Y`flJ)0X##a&e3qgKGT% z!YvP!`7G-oV<{a)r%nq!rpSW3;WM^AKfVN$Z?b`(SaNRV;qv|kxCN*;xMGP+ z*((OkyYktxPeFxHx!(vAuY1bko?6fPzc^6x?V* zxqOwU_djEap+5|uCu@qLkOEcYiH{nL%{7@x{Gpb#U&I;m7ndR2sJklEh}a~kAmuC% zgzGHhIdVDGKwq!1rv!1HI_9=nD~#uMcwdEkaEjc7)=aGONy1dS{uwpyI>X169>pvG3oy+G{)+Oq9C!sa?%Q(6uSWCo;Q$(u zA3Aba32(E zqz<9nulc!^tU+V;GhAxvrL{|oqy7FWVS{M--Eb5^!3q{)c)^!Xt@}+I_Ro_et2p%g z{x%kWzqm{M>%72h=k$$P{bjutzsbHoYkH=8!D$7 zVlh`_-$L1=$H#>>r-K2LTmxIk3enco*NnYCQ-Jywo`7g>MbaBhm}}cSH9d z&w1*1d7|4@k?W$acj6R2xhv2ILC*EHs-Ce^tNZJUt%Hk2Cx_@HB8O+Y7|8J7dq&07 z{2E4cSo~sb=P`^wit(b zI4nc20f@f>R(%|+6#E4g(MH)$)%uRjtTo-r{kNx8tK3vonPhfc)q4{v6cn*chgZj* zf4=Ebs*TT}ZZ%$>IWaYu=Ww#9}+SY@Nr(q%DI zZcNmO9338LM!k6Dl z#Dj_!r1O+?XDR7+kfqQumVA)u*XtzQF>@k)23`(Y6Jpnt{Ox$oSM(UJl1rvw*gKuYZagSb|71c+cHRN zf8+ArReaWvka;gykG5SQ=k>?hq@~2PhKDt-%SmDzaL4aCu>0|%C*DRMZjOFiwd394 zSTT!i1D$^WK^)C#Yt&Xp@VU==#?B(hGQeRP&Q}1@pu%CFht|8Pd1s$2scO1ZH+cP^ zso(o4?`#uJKlWmdmXLG%H;J=HKF63NUnxSYr}Tp!pCj~DWX^u>BhADO{_L7Lsav=D zu=}*?CLf-M|nB$CwrLi-giJXaOb)CZbruoY`KY4_dW$cN(rgV+Ymg z7QG(NlDe&K?+!lA$@0E!8Z9+rg2v7_gM}W@`+2$i!3%{srf)z^#T3BP)qLBV6WKoO z6kaqhI2OALjTdQ?lXa6-RUKvsc->BPcDrCuM1phB%EYKTPZti_7)%wyFfSEV@ccRs z+9SUm!C|{0)*}#15#>`8^)&8_PvIeF{5Z)WNX zLA~BC>k7lJIAR5@hF1E;k;o>7B+i!F79c2yya7CjbF6}aoYAbNDAM~Kf-Jmy$!P5g zJ}3w^?Hq#NHoj3I1lNFRWkCAHAyK!U__l-NOv5uNn!u%q1K&;ohx8!Vzh)Hh3plhS ztF=*&OWy7>E-P6ESFwzu?UPQ?E`$)$Uu#Z{HwB9DiBd+OZYMl>dlDsrpB{wIqlWC0 z8R*hVpmO3j2jOWl?2fMGyHnUbR|{BWR$rbVaP0W4=dh)?nD$%S)pGb4Xy>|%C(aH8 zYID(m^+;>pH?;0``3S)h)9vAdgGWC4!TpaLLJcGuohI^CjYsWaWJFS}I#)i~@D7RB zxOUXh5Ki=?YF&njlv~>lT?I!($VfhB&0T%fp+8z_8R5dm7RG>MosuJ&C;lz#83ZlZ&8R^A)n7J`osx`Ct$YQbZrr z)+Pvpyj~7Zyf>kBCs&}5-)NR;Zz;yzw>rWbbl<%AmhFI*Lq>;n?RIXOi`6HFixtk` zDn_B^%*S!@{dv?2=|MqK9z9AzN9@x&muCo&(IRc-m@*ZzV{K$#h?a(jE`l=u#o=ht z#`re6uL#*&c#c3RxSfXv(`O&j-#m*OiQB?EauOL(Kd)+$zRxZG9Rnh+l&Vd6yeS`Y zsTRi@>ueYACu$>BM#n1RE9I&cf(BuF=e#5q*H=a9p{RM4$byK67@kq4MvDIW5ChhSgl4VCCW!-Y_OZkSxJ zIs2^H2^Ht<9<5hLiIiO5_9n`)6JCBs-&H$j%&9Yoo~dUd%P5E^>$-YnS3AtDZa$c6 zPIl(C@&aD4^1hey%Xb{sSQI&V_s?*V?QT*rs|vM@X4q&9jt41S(+bJR&kadkHHBaTp2Jn{zB87q2b4 zJr(&PbS3?VsL_m@5>&*wju^fR?-SJR(k8EDpr=~ zDWb)6UOt48VkoeY2wfia{jw|}y_h!O{H9I-F!`7KavRRVf zjHWLa#6_$y&wTzpdjIryA$f&lb>fU_Lbrs8B|~k^NTIxy%<4ANb3wZez=3K zVxyI-h0>}LwmtS7k68F(yY^G1hytA#OKU1kj~8?VUvz5#zVyswh%)JtkqKcziy#7iZev})6meEqe+ zc5{zfJM3UJCBhcvpiZ}2Pr|G+=?=y_u@$>d&o)Vtcu#=XoOiJ{6<1}W`PFc3%8L$t zu6DSo;;Y>bIFVMM<&A%w;54QFi7Q5DFV1Y3$j zTf$qSdo~DVtdQqH*avd%DsN;5*6gijqS^k~W(5q*!QC(K$2 zAAHMKZIKvR6^*7I4N$0TY$nR>=poAPJe2*>!}n91*c;qp(FOj`H30H641A=glAeGv0idUA%4 z^wvv1__y!Gb~>mCGqrfM0<6?NV!3#+EM`e9T4d8yZn@CdruVzW4X+=gB#;&I5zG+7 zZEcQLmz~owny~rIFa$Hk1#{KvQ_0^^K6#c^c5GcXxgRXUb>Btw5RX4juxZ8D8MLqu zZ?iukns^dXq~Ff8(NZ`0CeOFU36X82QXZ~BUyHYN5ECNqFonGWxoC37GwH6o(^Y3< zW_CO29$SMRT{D0YZC;*lS*`5f?T2@>m@l(KW$GV4gilJidcL+Z{PYSHk1=TyaK#oQ z4q+VXV70PSEMt%9)gTvLTFFiN^svdKDh$Lrj5kXv9|!e>t^r=KmV8Fzojljfjy3+e zADp1GB%&30V6mRcB9}yWv^Z`Pp5eW5(pwe%Cw;FQJgkP2H+M35YG{gC|KX5%xI{H> zadd4iT#z~jqzDR=jn573^N9j7(B{%gzTEPzSy{1^ZdgIuu}}As-$u+`ZjDU@$ z&C7V(M#AYkK_W&T2D5pJp+YL5?gq+869IL0JcY^hZ`6E!a+np&3CF-8x<>gX_XEx2 zUVM3lSTApX)3Pk&?CU_Trw!f~5goUV?kdf+$!#4Xh;ni@dRq0w^4ZF!wprB#a!8U9 zHlLxn@PGGD$V9<~@;rs=&tWooj!oKvy9fUw9pS#9xl?;{hw>)}vYBd=09?c}h0vq- zhC_Ne@SDq=oaCSCClEGnnPbgviovxmcgsJ`$$V6;7Wi_jZFQOe?RXoLgRzc4ng75V zX3Z(>P5Prz-@WQXp+xl*v=oYT44Y`V3|DMd)>V^wEeDV-@ zs5zBo8zv=>7>omOPrq=h(&RxRhFC^?zHnR|y&wEsbnPg)7vH>0ziA>+ec$NmS6HBT zNIQhZ;@nt`N8UwTDOj^K+qtht7oT=6s+{34}P~`o?=$Sm(wp`61`5T(?G1q5KmYk;-npe4K#(`dl9 zg4O-XUpl#ufhpGE)yl1G&|dNkZ>G?6l9lEb;f#3-1*nVT4QIaW7%-bnep__n&tQT; z-Ky2ov`_}{+4Z#ZTW5n+HxIoex$Jz=-VC{$3@=(9qC!~P3~!MAWSSFdKc;||P(CmG zaQhwF#nde@ba}=S2`PY~piC!ok+uP@)n*2!g=EdZ`dt~@l}00-4&%{Q9fIbi55ppM z4?@JOOFT1Q)95hfQu8p@=$uiJjbX!)cGsD6Nq*N%%2%v&ajqA1%H@exPRimTbPWz| zOZS=j<7)kaSFXI^TE$k61Wgl4bzJ!C#rPI`m;_Z%0uPWaRL!WNCIXyERU||GHVs#C zl^0l$-8jt|V=53gJ|mfi z=xI)yp(0;Hv@c<&%Aauy6(X=4HF+7=6gKtq=)4}MhfVFB#DYkDg1#RuxUbL#5ET5- zbH&b=eFv^4FbrR+I}CeP;hc;wug>399=GYhH=G@AZlFHKh}nuYOFH7(0uCDt>f(b` z%-jI6LUI~7Ajhd`@9 z0L~*dNk*ec5K0%(&t4hcUK{_a@EQVs4xZ?Npi9|K2N#)F*ElTY(Za#ilZZ>&@sXHJ z-cmo8^rDm^q$2{DICfm@v4Qkk||G7 z+l^*T%Y@c_9w{Mm5dCkfod8l2l+LCYU3o8l3EI~(kETaek;T}!X!+wkEUx;9o< zoEWB}$#}Ij*6WE!8T+w-+HfmUrF*jMJ%kjwS`AoU!`W}jMU+(P*n!S@8>9n>k3n20XZO&2wmyD%ya3v z-Bs@Xu(ibrwM3uFtphoF&0umlg+?!(sN7+*!#392wx&)nT0{Ug)%5d5Mdc0@n;POp zU0CbTg6P|DOZ`?fzM{4sKfi~u+%tRSuVFU@0(}HO0)mUtl{vHv zmMsf7`2bpK)E<=tvz^Ul<2$R&(8H}iieY26P4j}k$HolRXf#+7Soq#0roNgG?O2W) zkfTs>D-7LG318Q*?(Nu_qJOTGx4$l%CLjD2MAu7_vTbRen+8L zGYqKl%JbXwCZ`oCw`s-QKorb3>Q9#9z(0-!A(u!b13CDCUJ@&L13K!72oa)>EeAYX z3_Z<`V@IBFf|IZ;cmXwn$66sxAFw;Iwl3fVYh0=On#;(h+UHXgbIaf(fJb*gZXhte zEjea=k#Tp*n^v*7*%CgWSU{|K7$;NW9Pj9^uYvZ)a9p;!8Md?CBm{L=1Xc~1V5!q) zY8?8GLvmOxmAPj7gMDM)qJ45;+S7>6v6AjHMY)gn1ML^9$IYGNIT&8>mvA_APtuB+ zirQG4f5v}8n6FYL9C8k75gcvC-zL;ArM`Idu}FZOoDU0w-MP&-RK@c_QQoGQm(V&h z#j+i?-;Q{5L(m_)b0u7iZz#gH1FL@*;rU6+#}DrPdfX!KKJb|pHVl!VL+YIPwNu zYz|#m1wJYnGIK7PTzT1SS}^QbE#ICMCsI zFF#S9)~|lVE>}blD{WT?PuOXm>V@yV64O||1XRd+i*%^mlBBM*{^(9h)QqZ^QLYmi5S8%^SZG6jl0q`fP`Ls~y8S9$sF{YuKYk(EV(H%@* zmtaul7fb*{n4-aCw`_4k<;tU?XE4+d5K;-Mj$l1!tV;pmC-sXKbX`yt>3YS)W`;_J z$ggn0*8GWPj0?g|9ye^*(4q!2fE7sC+>)2FjtxiQOsK^Tr^v3$xy@Y>cfcs+Pg^@Ma?7BY_nt9pfPWd^iyHbz^4Eb zl@B2+F7KBybBpFlzfz&3n#XQp)Al52U#ti^CBHcE0Q+doV4Lp+fA4Rb`hW?=tyOzU zPwQH}wqeG-%66up2$No-%5`EuQTSxs_= z0lXbqrDU?{3V#_B0?_6e(rl1DwZ-yYYpXN^zZ(H_zy?^r{K}!nMh;qZ8D|) zsQ}o8drb?N&syFiypWK%WN!qeu?Uc0WZ#9{D$-BH+iI%>rAfn&?djx7%*P`^mjhdu zUBbb*o0a?^#h$#O&Lfk@yHL=4ePLaUfzU1N8;KZ4o6i|_Cy@jLQb&Y3=2?W7DVdUo zje>?iCFYdUQmFM_RsCyKT_L_s)PwRm!rfM^-8rp7nNN0>+h$Pjud-7`L3mtJb0d(Z ze!M)MtI!TC2vle;p5<5`xyCGqbcrosqWod_`lSvLVK4!oB2x0^01HiR(^(tNmXq1& zwniDBjG{T#s4bZ-nWImy%NY`9J_?+Q4(B#(_WeMKAN#%e*M9d+OX7m5tQ&whh?U_qly*;957I|a3FK7;S#RIlMdqLWG9ASXz0j1S^Y@H`lU=rO3$;$**nslRYulj5i7+Ri&&b8VZBN|I^uU;N?E6aM5 z61ILSgu?rgzeOpe@Ya0_+YSQSiHU048a0yXN(MLGM_T9e0Uc* za4ZKR%islfPM*1azc;6a9j#!2V5HIt+QOv#LL36tzyXgo63DN0hP8qiFu|bR&ZWpu zZak8kIokmW_EZCIWsCUOTBfUgc!{+Fz(71YKzXjwNK}IadGabcaCRTX`cC`9BjJOq zU4fgd6W&p5R-K@s-KD^eds4>^w{IGod8T$hmSK+*7{2SkGfS;jJ7tL%WOv!V8q46e z5tJm(Iiu~2Z!%S>Sl6*%8ufX-MBU+hANI6RQXka2zbT3-L$uVGKZ2UbV3yQ#7Y@5E zl0#baud7+uDSRiO430!|(%Ae6mcnz?AK4N_e@miRCNO27qSxhLUQ6rA%VIk5B1NCE zg4~#3UGpq-U4IJ5?{yE>cKh@x-W5zuIiBHpD+D-TW3?f^^$Fo-+zENc7$?ejDmIGu zaxdl52lI>nZb%X8aVKai7)rrBs*f+9G&27NFwQ`QDnBcyw@G<1<3|XC2H&F(-?w{- zye$a|V4rzK?Xw(Q%gTjC9>dnFH))0c?3DOTm-MS-{J{W1@f9|8bfZOhd)of1+C}+( zoHccPQ(d}!3R6wu=;g6O0C;c|w=EwK zy-EF=tEgS3u{KliG&eRZOD*EPw|%A10W#3^51S1&m&d%|_Mie&jIUphh&u#axI&-M ze!(<*?W)kI!7%FE=6YKA)%+blI6~BvU++>GFDR87q_zoJCv{%N3TZa9>BzXHrpdoU z>Pqkzt+Ck@J3yIyCi0p9`qnqFoNC-8kgMiOXVq^)G*WLgVGXZv-=QDF@?fP`XKS99 za0REE!F(FxB^Ez>qSD~%1+?4d9M<>4v`6z}UHdP)h*N0p#9Pe}&x`|Y9vA5H;eO0w zq|$thYy084aG23;OKam-UX~$7oAj?CjAy$M1FXb6&~@9s>n4vT%YI^Y?i5#l9<1lL4P1 zIwBKgXQ01$Ai?K9h?;(SeBJ^49#{|$bTdJqM<7vnQ40T9!Kln^dg@~kGqq8^Vo9GL z6M4q)%u5<2=5KFQ$@*|j?e+a~`wHiR;C0}rUul2D5`lA)q{q{|L=Z@|-OMy%7_W1S zfIhy5MIVH-s*rJ%7>^KX(x1*ly4R->=W&UQ7EZI(G>GBkH0~^gf;?Ziy*qjQ1&gBS z>rA~1&V!d+8nd*M6MeEeQx)E({dO2@`fmdCeR<%YXuZ9SWunQkWZy7TJ7c_+_g+Y$ zfF0?*h3MUGEQJR|r2rabCN*Z+#NHN8Y0ilj7gfXDpoHj(!&6$0s{vy*+Rx)i8fy7 zn@J9rzeFDLF>z_9QLmR)?mz8iD*W1Vp;Wbpzxj5k!)66thBc|*Muv49O&fK@y*wkQ z;$AJxT>bY`vMuOY=<}1{b`gm^J0*q&vP}NGt zsq&}A&Rd^i!+HqR;7yvR&R^da5MzXfx2{m?18IQ-^OI?# zTBE33JR|W@79_5H)IC551L5(Ct_TI;;R z@_;}8fEjk^Q@_-kyl$I{VBRx}4lVYqlyYy0)jsGUtix-y_EkC$dRVQiBloiZPy+Z{ zAlFzg?hd<9e|h0>O!8U>36u_IiV*X``DrJ7Q#^{o&|^Uho5^JP^HNqcIa}&gu<4sO zwCl1$oGi3BhA9+;!u|3z29 zRk!FbjRd31=EUB$NBAAG(J{2y7~`t1`2+8<;QZN1cYp*WlX74@bq-b5?1$jt+@c0B zlu_b4cG|uFaMwa9VcwPk`eO;TdXY3%k9ukV5N8T0RhT6k$vs-;+x>jXXfvp%+R>|; z({)`|KyA7a6)jDA1f8v!v~OSP_nut39N4_6Ix(l@({y)+;yl)A#O;af#8qT zv_%TrzE@~6J_tlTGM`_6yinUfO894bo5CLsB#KEQ$R%TFl*?a6M*D()W!W3PrxtQM zT~#73)t7D2=`oXiP9mE{SdUWu^VDL&boba@ZEg5JE-%y#u5fi8BvOa(^w z#IyXnwQl4UzeTjL(;)fv9tuyXT>XxpFAr*nvre*^xlIbLzt2ly0>RcJ)V^<3?`LP| zXOC|_X~}~=?hmcw5gClO3#fwxS>bH=<`O_-j zCS)0}z8{-C904mM{XlLgUC=TNkB$XZh6KtDK3llM^N-ICy@qRc*6hDlD}?{&9dq&K zmu+|+pvoXe9CW`+{Yh>XPeoRx_alV1$OT;Fh@vbWxEa(xmzOE*1%7=+%B{&idYpjO z^l%Bdd9yV1X8(zy0Dt9JOMaN5tRUJy-hlt(6UYj()5oeuc9DNP-5cBWp%KG(!`?s^ zjHyCM)p=ADTJx-Hg+R`K!Nx@p+D0X+O+j1L+x zHV$yfZm#amC-WLEyPzlR)eWrNfmw4U&IKPG=I<>kC_$J#`%wx>GwgQH>YZPkyJyv> zcjJ#f3_{~!BsH%Nk{?gU?P8c%Xg}#cn{j5t$?coR)r|Xwcx^%-K zGE0Yt0zi{4GB>6j5(Cg%FHZNC!Bm3*>DsruF8JcH4tgm*g17#*iD7@)n5A3$RZTyP z9T*99s#)Urhj~OKy0KHQ72EZN=h|_cx&ezXlj?Nd#~dhV&+_kT7scAFS)}JGmAwKa zL>i#PT6?wHR{v6CG+*@ zP$hiBj<3D~0B4S+&?BWR~}gP?fI2r0v$*cT;xC=7z%* zqk;(dJ}8&MckaEFy1KAVr9$1PTJY~)P7R<91+yor2QO$rnBjvCJj2cZ|1BYOl@+InLJ761m40)?_PVBwbJD$*aI*Wm~!% zC}kPjlA%NhdIm?_-8G~sk-lUeWyPZ?Sx?28%ZwqptxfyQJbM!AyD*Su#nnEMvlXvc zI1ebGUmS|Fg+XpH%vf1jiA8Q!TCYk}0jLq~Xm!5eCT#Nw0A4-RR}7#oAJfM~;s!(_ zwx}15Z#6j4Y3olIPnToRI8mo!-ei~BQ$M-ne58lc6=I36m%O`a?%GCL-C;Adms2pF zRgu*@G_KLi`75Ysg#0|3W2*{Mtpfi#Spz9&5PrXLABlh7I7(&YY=Ckcn&vzj(_Z=} zzYo{>!8J3SEi2fh<9KsaJ=tj+UKI)&uZf`(cvz#Yiuy@8N0vARi{z5{bRZ0{+|vn2 zlbKiJed@me6MD2}83g+C?c)KOd1x?|TdTFe$gM{-sn~R)G~DfC@d0W5Xv`xfy)S^8 zoO^u9^r-&&%GhwM@MD!t@R4i0F9?Y95<$16bdeURt`NH#m}f{se$zY~CAWp$gWG8a=3edB zRGx414%Rx%aP7pP9DfFac?ojc?WvbYkp-u^lg1#K{XuYtzZwsS+|rfpIhWC=wW|%+ z4~VlFg-nx!{c$FL#=B!|zZwCk*FN8mt9(hGG?wlB8f*5%ru+0ww6M;ND*N_bUqfzw ztUm%|4-*LDzBdmJG?uft?89R{ftk{{S86h34Iu!YliHdR2b;s7Ac{$+Hua6<=u^v; z4`K&_TOzvdUaN4#Q)OIn7pHTD2W_|Md*8dC%z1@Sgk9KP`buytlQ=B+)k{~~7`g;q zgZe8S7`Pk>&^;TK=Q|A|NWs@OgO*y#sq<&moB3flW^2pnN1zpkmOROZGTfFOjKyA_ z_t{tRjAzFMlWv*gI9Vj`L5EiDbU7S9C*=;&RN)1N2I78hLV8Q$Y4$jb2&A#C#Q#j~V?^|C{fT;EtTjt1Uz9PD4ay;?GC`Hx7M z@HwnzeWCu?yPUV>Sh6kDUlQKSbQ*Upty%&~yHyWE$2wUb`y%uD=+18C%GLR7e^jG& zNwC|&fX~vDb-(3uCzgnOhSP2;g+D@L+Equ5wNZiu$s_+7T zbapzXmuG-7Lrz0)w>~Iz=lJZSbzqluJvxT89CN zm?%@Gy#OkF0|ja|G3qRtYxCdx!z z&rM4=CT(mbsnS77WUyE_pmEhm9wz52nEac=N{+noS^4-D3z^alhIq?_5uEs-NyjI+ z=JKcpubaOTjs+(A6Ua<_izy@oBaGr+hEGkY#8m3HlIwt}C8_x85-Zc>1|^_x^ekI0 z2kRB#MQdj<=qhW=EU~^kT}@eYvitwod(WV#wrvem*&s;;L_j2ifCLEwl4%eSNsUOB zBp@O==cr^*5Xl)N3zBnGaz=8_p-D}qo6MVd6g~G=oqG5Ee08e!u39!7=9+V?Imh_M z7~e;ogI{d`tS#5dkWap4%ChCFRTDuZKQEnZOiuG!^wHg12|xn{fyTJ*fLv&nAj#%{ zyr8pIaO;NOzjHc^Pq>m*%3-c051?k!{6y}pxnoRHDbze(PU?xonj zRV@)rj&J1FZ7i~9{au4cubEj89rSUq?ALOIdEVrEdQ%|3ppDQ$CR}O6@$T-TwmW

v)q##M≫gwMV+-A*5ADULf=#ME6S00_m<;}6 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

E?+P9ym|ccEV)py{-d>;=Ts8N+_Lx@wU8JdS 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 Q1O7ZxR8uIGGx7g_02|rQ1ONa4 literal 0 HcmV?d00001 diff --git a/docs/assets/okta-auth-policy.png b/docs/assets/okta-auth-policy.png new file mode 100644 index 0000000000000000000000000000000000000000..dbf99a88ed6e3cfe22708af63f6befa06d170f64 GIT binary patch literal 85431 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 LDHO{+2>gEl!89X`!O0vSyhYj)tbj5Q!?`- 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%0AxC_scxWE6#{bNQQyg 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%0DE2kP(qbSnXvmW4nM>C`)4NImm@ys{1{3JR#i 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

aly*&Cn?#_uN}{$vcv6miP9Ymc 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�(^ZCz9mBq<;W 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%0A&~5PT~7 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 z

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 literal 0 HcmV?d00001 diff --git a/docs/assets/release-action.png b/docs/assets/release-action.png new file mode 100644 index 0000000000000000000000000000000000000000..cadcda53fca0913b03d37ecac9b93180102a5150 GIT binary patch literal 42174 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
b4>|*Ys2r)3Kd{ypUW=sG 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 Okd;)BD0^%4@jn0zTi&Ap literal 0 HcmV?d00001 diff --git a/docs/assets/versions.js b/docs/assets/versions.js index d3befc5f91dea..7a2392a392dc8 100644 --- a/docs/assets/versions.js +++ b/docs/assets/versions.js @@ -9,16 +9,6 @@ setTimeout(function() { caret.innerHTML = "" 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'); diff --git a/docs/assets/zitadel-actions.png b/docs/assets/zitadel-actions.png new file mode 100644 index 0000000000000000000000000000000000000000..db0b37245c49ec73ed0bb92781563bda374a02ea GIT binary patch literal 57176 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 literal 0 HcmV?d00001 diff --git a/docs/assets/zitadel-application-1.png b/docs/assets/zitadel-application-1.png new file mode 100644 index 0000000000000000000000000000000000000000..f69acf9b6939ef8db5d91a3aa2ca5e1ea9d5c2d9 GIT binary patch literal 129320 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ⅈ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 Yn6>doPKi>^J&->=UHx3vIVCg!0LqFy)Bpeg literal 0 HcmV?d00001 diff --git a/docs/assets/zitadel-application-2.png b/docs/assets/zitadel-application-2.png new file mode 100644 index 0000000000000000000000000000000000000000..f0b4ff34eabed2c053f73ef225db787345501457 GIT binary patch literal 116260 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 literal 0 HcmV?d00001 diff --git a/docs/assets/zitadel-application-settings.png b/docs/assets/zitadel-application-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..cbe2e62aee73867312ff35f42e32378773390146 GIT binary patch literal 70144 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/assets/zitadel-argocd-login.png b/docs/assets/zitadel-argocd-login.png new file mode 100644 index 0000000000000000000000000000000000000000..b4f2e0b75ae7701d0c73103325caaf1b48796664 GIT binary patch literal 7236 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 literal 0 HcmV?d00001 diff --git a/docs/assets/zitadel-argocd-user-info.png b/docs/assets/zitadel-argocd-user-info.png new file mode 100644 index 0000000000000000000000000000000000000000..88b5a2befa8f270ee7d62ce2ced9ba71faaba37e GIT binary patch literal 33256 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 P0N*4<P(Ln=acP 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%0A81TPZ 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 literal 0 HcmV?d00001 diff --git a/docs/assets/zitadel-project-settings.png b/docs/assets/zitadel-project-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..ac0c3fa8036c785639741ccc2eb5c26dab1e932e GIT binary patch literal 17052 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

p)6O1mcY?LZPFI{kW85_%DOU&wn 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 literal 0 HcmV?d00001 diff --git a/docs/cli_installation.md b/docs/cli_installation.md index 639a9317639fe..5a314d4ce6be2 100644 --- a/docs/cli_installation.md +++ b/docs/cli_installation.md @@ -37,6 +37,17 @@ sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd rm argocd-linux-amd64 ``` +#### Download latest stable version + +You can download the latest stable release by executing below steps: + +```bash +VERSION=$(curl -L -s https://raw.githubusercontent.com/argoproj/argo-cd/stable/VERSION) +curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/download/v$VERSION/argocd-linux-amd64 +sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd +rm argocd-linux-amd64 +``` + You should now be able to run `argocd` commands. @@ -115,6 +126,11 @@ $output = "argocd.exe" Invoke-WebRequest -Uri $url -OutFile $output ``` Also please note you will probably need to move the file into your PATH. +Use following command to add Argo CD into environment variables PATH + +```powershell +[Environment]::SetEnvironmentVariable("Path", "$env:Path;C:\Path\To\ArgoCD-CLI", "User") +``` After finishing the instructions above, you should now be able to run `argocd` commands. diff --git a/docs/developer-guide/api-docs.md b/docs/developer-guide/api-docs.md index 7b4b44bf9269e..63e3cd901e3d3 100644 --- a/docs/developer-guide/api-docs.md +++ b/docs/developer-guide/api-docs.md @@ -1,6 +1,6 @@ # API Docs -You can find the Swagger docs by setting the path to `/swagger-ui` in your Argo CD UI's. E.g. [http://localhost:8080/swagger-ui](http://localhost:8080/swagger-ui). +You can find the Swagger docs by setting the path to `/swagger-ui` in your Argo CD UI. E.g. [http://localhost:8080/swagger-ui](http://localhost:8080/swagger-ui). ## Authorization @@ -17,4 +17,16 @@ Then pass using the HTTP `Authorization` header, prefixing with `Bearer `: $ curl $ARGOCD_SERVER/api/v1/applications -H "Authorization: Bearer $ARGOCD_TOKEN" {"metadata":{"selfLink":"/apis/argoproj.io/v1alpha1/namespaces/argocd/applications","resourceVersion":"37755"},"items":...} ``` - + +## Services + +### Applications API + +#### How to Avoid 403 Errors for Missing Applications + +All endpoints of the Applications API accept an optional `project` query string parameter. If the parameter +is specified, and the specified Application does not exist, the API will return a `404` error. + +Additionally, if the `project` query string parameter is specified and the Application exists but is not in +the given `project`, the API will return a `403` error. This is to prevent leaking information about the +existence of Applications to users who do not have access to them. \ No newline at end of file diff --git a/docs/developer-guide/architecture/authz-authn.md b/docs/developer-guide/architecture/authz-authn.md new file mode 100644 index 0000000000000..af32a9176eec9 --- /dev/null +++ b/docs/developer-guide/architecture/authz-authn.md @@ -0,0 +1,109 @@ +# Authentication and Authorization + +This document describes how authentication (authn) and authorization +(authz) are implemented in Argo CD. There is a clear distinction in +the code base of when and how these two security concepts are +enforced. + +## Logical layers + +The diagram bellow suggests 4 different logical layers (represented by +4 boxes: HTTP, gRPC, AuthN and AuthZ) inside Argo CD API server that +collaborate to provide authentication and authorization. + +- **HTTP**: The HTTP layer groups the *logical elements* that + collaborate to handle HTTP requests. Every incoming request reaches + the same HTTP server at the same port (8080). This server will + analyze the request headers and dispatch to the proper internal + server: gRPC or standard HTTP. + +- **gRPC**: The [gRPC][4] layer groups the logical elements responsible for + the gRPC implementation. + +- **AuthN**: The AuthN represents the layer responsible for + authentication. + +- **AuthZ**: The AuthZ represents the layer responsible for + authorization. + +![Argo CD Architecture](../../assets/argocd-arch-authn-authz.jpg) + +## Logical elements + +The logical elements (identified by numbers) can represent an object, +a function or a component in the code base. Note that this particular +distinction is not represented in the diagram. + +Incoming requests can reach Argo CD API server from the web UI as well +as from the `argocd` CLI. The responsibility of the represented +elements are described below with their respective numbers: + +1. **Cmux**: Uses the [cmux][1] library to provide a connection + multiplexer capability making it possible to use the same port to + handle standard HTTP as well as gRPC requests. It is responsible + for inspecting incoming requests and dispatch to appropriate + internal servers. If the request version is `http1.x` it will + delegate to the *http mux*. If the request version is `http2` and + has the header `content-type: application/grpc`, it will delegate + to the *gRPC Server*. + +1. **HTTP mux**: A [standard HTTP multiplexer][8] that will handle non + gRPC requests. It is responsible for serving a unified [REST + API][3] to the web UI exposing all gRPC and non-gRPC services. + +1. **gRPC-gateway**: Uses the [grpc-gateway][2] library to translate + internal gRPC services and expose them as a [REST API][3]. The + great majority of API services in Argo CD are implemented in gRPC. + The grpc-gateway makes it possible to access gRPC services from the + web UI. + +1. **Server**: The internal gRPC Server responsible for handling gRPC + requests. + +1. **AuthN**: Is responsible for invoking the authentication logic. It + is registered as a gRPC interceptor which will automatically + trigger for every gRPC request. + +1. **Session Manager**: Is the object responsible for managing Argo CD + API server session. It provides the functionality to verify the + validity of the authentication token provided in the request. + Depending on how Argo CD is configured it may or may not delegate + to an external AuthN provider to verify the token. + +1. **AuthN Provider**: Describes the component that can be plugged in + Argo CD API server to provide the authentication functionality such + as the login and the token verification process. + +1. **Service Method**: represents the method implementing the business + logic (core functionality) requested. An example of business logic + is: `List Applications`. Service methods are also responsible for + invoking the [RBAC][7] enforcement function to validate if the + authenticated user has permission to execute this method. + +1. **RBAC**: Is a collection of functions to provide the capability to + verify if the user has permission to execute a specific action in + Argo CD. It does so by validating the incoming request action + against predefined [RBAC][7] rules that can be configured in Argo CD + API server as well as in Argo CD `Project` CRD. + +1. **Casbin**: Uses the [Casbin][5] library to enforce [RBAC][7] rules. + +1. **AuthN Middleware**: Is an [HTTP Middleware][6] configured to + invoke the logic to verify the token for HTTP services that are not + implemented as gRPC and requires authentication. + +1. **HTTP Handler**: represents the http handlers responsible for + invoking the business logic (core functionality) requested. An + example of business logic is: `List Applications`. Http handlers + are also responsible for invoking the [RBAC][7] enforcement function to + validate if the authenticated user has permission to execute this + business logic. + +[1]: https://github.com/soheilhy/cmux +[2]: https://github.com/grpc-ecosystem/grpc-gateway +[3]: https://en.wikipedia.org/wiki/Representational_state_transfer +[4]: https://grpc.io/ +[5]: https://casbin.org/ +[6]: https://github.com/golang/go/wiki/LearnServerProgramming#middleware +[7]: https://en.wikipedia.org/wiki/Role-based_access_control +[8]: https://pkg.go.dev/net/http#ServeMux diff --git a/docs/developer-guide/architecture/components.md b/docs/developer-guide/architecture/components.md new file mode 100644 index 0000000000000..e073751da4867 --- /dev/null +++ b/docs/developer-guide/architecture/components.md @@ -0,0 +1,118 @@ +# Component Architecture + +Argo CD is designed with a component based architecture. The goal is +to separate the responsibility in different deployable units in order +to have the following benefits: + +- **Modularity**: Provides great level of flexibility. Components + interact with each other via an interface. This means that as long + as the interface contract is respected, a given component can be + replaced without requiring the rest of the system to adapt. It is + also possible to run the system without certain components if a + specific group of functionality isn't desired. +- **Single responsibility**: Helps to determine where the different + types of functionality should be implemented which drives for + better system cohesiveness. +- **Reusability**: Clearly defined interfaces helps in functionality + discoverability which benefits reusability of services. + +The default Argo CD installation is composed by different components +and different Kubernetes controllers. The controllers aren't +categorized as components as they have proprietary interfaces (CRDs) +and therefore, miss the modular nature. There are more resources +created while installing Argo CD (ConfigMaps, Services, etc), but for +simplicity we are covering just the ones directly related with the +componentized architecture. + +## Dependencies + +The diagram below has represented all dependencies between the +different components used by the default Argo CD installation: + +![Components Diagram](../../assets/argocd-components.png) + +There are 4 logical layers represented in the diagram: + +- **UI**: This is the presentation layer. Users interact with Argo CD + mainly by components from this layer. +- **Application**: The capabilities required to support the components + from the UI layer. +- **Core**: The main Argo CD gitops functionality is implemented by + components and Kubernetes controllers from the Core layer. +- **Infra**: Represent the tools that Argo CD depends on as part of + its infrastructure. + +The logical layers also help making the diagram easier to follow as +dependencies are represented in a top-down relationship. This means +that components from the top layers will be allowed to depend on any +component from any of the bottom layers. However components from the +bottom layers will never depend on any ones from upper layers. + +## Responsibility + +Below you can refer to a brief description of Argo CD components and +its main responsibilities. + +### Webapp + +Argo CD ships with a powerful web interface that allows managing +applications deployed in a given Kubernetes cluster. + +### CLI + +Argo CD provides a CLI that can be used by users to interact with Argo +CD API. The CLI can also be used for automation and scripting. + +### API Server + +Defines the proprietary API exposed by Argo CD that powers the Webapp +and the CLI functionalities. + +### Application Controller + +The Application Controller is responsible for reconciling the +Application resource in Kubernetes synchronizing the desired +application state (provided in Git) with the live state (in +Kubernetes). The Application Controller is also responsible for +reconciling the Project resource. + +### ApplicationSet Controller + +The ApplicationSet Controller is responsible for reconciling the +ApplicationSet resource. + +### Repo Server + +Repo Server plays an important role in Argo CD architecture as it is +responsible for interacting with the Git repository to generate the +desired state for all Kubernetes resources that belongs to a given +application. + +### Redis + +Redis is used by Argo CD to provide a cache layer reducing requests +sent to the Kube API as well as to the Git provider. It also supports +a few UI operations. + +### Kube API + +Argo CD controllers will connect to the Kubernetes API in order to run +the reconciliation loop. + +### Git + +As a gitops tool Argo CD requires that the desired state of the +Kubernetes resources to be provided in a Git repository. + +We use "git" here to stand in for an actual git repo, a Helm repo, +or an OCI artifact repo. Argo CD supports all those options. + +### Dex + +Argo CD relies on Dex to provide authentication with external OIDC +providers. However other tools can be used instead of Dex. Check the +[user management +documentation](../../operator-manual/user-management/index.md) for +more details. + + diff --git a/docs/developer-guide/ci.md b/docs/developer-guide/ci.md index 53796c77500e6..921f9d69d4c57 100644 --- a/docs/developer-guide/ci.md +++ b/docs/developer-guide/ci.md @@ -23,13 +23,13 @@ git push origin 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 `Ensure Go modules synchronicity` step, you need to first download all Go dependent modules locally via `go mod download` and then run `go mod tidy` to make sure the dependent Go modules are tidied up. Finally commit and push your changes to `go.mod` and `go.sum` to your branch. +If the build is failing at the `Ensure Go modules synchronicity` step, you need to first download all Go dependent modules locally via `go mod download` and then run `go mod tidy` to make sure the dependent Go modules are tidied up. Finally, commit and push your changes to `go.mod` and `go.sum` to your branch. If the build is failing at the `Build & cache Go code`, you need to make sure `make build-local` runs successfully on your local machine. ### 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. +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`. @@ -66,7 +66,7 @@ make builder-image IMAGE_NAMESPACE=argoproj IMAGE_TAG=v1.0.0 ## Public CD Every commit to master is built and published to `ghcr.io/argoproj/argo-cd/argocd:-`. The list of images is available at -https://github.com/argoproj/argo-cd/packages. +[https://github.com/argoproj/argo-cd/packages](https://github.com/argoproj/argo-cd/packages). !!! note GitHub docker registry [requires](https://github.community/t5/GitHub-Actions/docker-pull-from-public-GitHub-Package-Registry-fail-with-quot/m-p/32888#M1294) authentication to read diff --git a/docs/developer-guide/code-contributions.md b/docs/developer-guide/code-contributions.md index b57d9df6d8ae6..2d28aaa956b48 100644 --- a/docs/developer-guide/code-contributions.md +++ b/docs/developer-guide/code-contributions.md @@ -103,10 +103,12 @@ Design documents are usually submitted as PR and use [this template](https://git Our community regularly meets virtually to discuss issues, ideas and enhancements around Argo CD. We do invite you to join this virtual meetings if you want to bring up certain things (including your enhancement proposals), participate in our triaging or just want to get to know other contributors. -The current cadence of our meetings is weekly, every Thursday at 4:15pm UTC (8:15am Pacific, 11:15am Eastern, 5:15pm Central European, 9:45pm Indian). We use Zoom to conduct these meetings. +The current cadence of our meetings is weekly, every Thursday at 8:15AM Pacific Time ([click here to check in your current timezone][1]). We use Zoom to conduct these meetings. * [Agenda document (Google Docs, includes Zoom link)](https://docs.google.com/document/d/1xkoFkVviB70YBzSEa4bDnu-rUZ1sIFtwKKG1Uw8XsY8) If you want to discuss something, we kindly ask you to put your item on the [agenda](https://docs.google.com/document/d/1xkoFkVviB70YBzSEa4bDnu-rUZ1sIFtwKKG1Uw8XsY8) -for one of the upcoming meetings so that we can plan in the time for discussing about it. \ No newline at end of file +for one of the upcoming meetings so that we can plan in the time for discussing it. + +[1]: https://www.timebie.com/std/pacific.php?q=081500 diff --git a/docs/developer-guide/contributors-quickstart.md b/docs/developer-guide/contributors-quickstart.md index 821fc36f3eabd..68cda35b6d08e 100644 --- a/docs/developer-guide/contributors-quickstart.md +++ b/docs/developer-guide/contributors-quickstart.md @@ -9,22 +9,15 @@ and the [toolchain guide](toolchain-guide.md). ### Install Go -- Install version 1.18 or newer (Verify version by running `go version`) + -- Get current value of `GOPATH` env: - ```shell - go env | grep path - ``` -- Change directory into that path - ```shell - cd - ``` +Install Go with a version equal to or greater than the version listed in `go.mod` (verify go version with `go version`). ### Clone the Argo CD repo ```shell -mkdir -p src/github.com/argoproj/ && -cd src/github.com/argoproj && +mkdir -p $GOPATH/src/github.com/argoproj/ && +cd $GOPATH/src/github.com/argoproj && git clone https://github.com/argoproj/argo-cd.git ``` @@ -32,16 +25,29 @@ git clone https://github.com/argoproj/argo-cd.git -### Install or Upgrade `kind` (Optional - Should work with any local cluster) +### Install or Upgrade a Tool for Running Local Clusters (e.g. kind or minikube) + +#### Installation guide for kind: +#### Installation guide for minikube: + + + ### Start Your Local Cluster +For example, if you are using kind: ```shell kind create cluster ``` +Or, if you are using minikube: + +```shell +minikube start +``` + ### Install Argo CD ```shell @@ -71,7 +77,7 @@ cd argo-cd make start-local ARGOCD_GPG_ENABLED=false ``` -- Navigate to to the ArgoCD UI on browser +- Navigate to [localhost:4000](http://localhost:4000) in your browser to load the Argo CD UI - It may take a few minutes for the UI to be responsive !!! note diff --git a/docs/developer-guide/debugging-remote-environment.md b/docs/developer-guide/debugging-remote-environment.md index 7fe7f18260205..5548d3444af8c 100644 --- a/docs/developer-guide/debugging-remote-environment.md +++ b/docs/developer-guide/debugging-remote-environment.md @@ -28,7 +28,7 @@ telepresence intercept argocd-server --port 8080:http --env-file .envrc.remote # * `--port` forwards traffic of remote port http to 8080 locally (use `--port 8080:https` if argocd-server terminates TLS) * `--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 -With this, any traffic that hits your argocd-server service in the cluster (e.g through a LB / ingress) will be forwarded to your laptop on port 8080. So that you can now start argocd-server locally to debug or test new code. If you launch argocd-server using the environment variables in `.envrc.remote`, he is able to fetch all the configmaps, secrets and so one from the cluster and transparently connect to the other microservices so that no further configuration should be neccesary and he behaves exactly the same as in the cluster. +With this, any traffic that hits your argocd-server service in the cluster (e.g. through a LB / ingress) will be forwarded to your laptop on port 8080. So that you can now start argocd-server locally to debug or test new code. If you launch argocd-server using the environment variables in `.envrc.remote`, it is able to fetch all the configmaps, secrets and so on from the cluster and transparently connect to the other microservices so that no further configuration should be necessary, and it behaves exactly the same as in the cluster. List current status of Telepresence using: ```shell @@ -45,7 +45,7 @@ And uninstall telepresence from your cluster: telepresence helm uninstall ``` -See [this quickstart](https://www.telepresence.io/docs/latest/howtos/intercepts/) for more information on how to intercept services using Telepresence. +See [this quickstart](https://www.telepresence.io/docs/latest/quick-start/) for more information on how to intercept services using Telepresence. ### Connect (telepresence v1) Use the following command instead: diff --git a/docs/developer-guide/extensions/proxy-extensions.md b/docs/developer-guide/extensions/proxy-extensions.md new file mode 100644 index 0000000000000..c53946cade95f --- /dev/null +++ b/docs/developer-guide/extensions/proxy-extensions.md @@ -0,0 +1,335 @@ +# Proxy Extensions +*Current Status: [Alpha][1] (Since v2.7.0)* + +## Overview + +With UI extensions it is possible to enhance Argo CD web interface to +provide valuable data to the user. However the data is restricted to +the resources that belongs to the Application. With proxy extensions +it is also possible to add additional functionality that have access +to data provided by backend services. In this case Argo CD API server +acts as a reverse-proxy authenticating and authorizing incoming +requests before forwarding to the backend service. + +## Configuration + +As proxy extension is in [Alpha][1] phase, the feature is disabled by +default. To enable it, it is necessary to configure the feature flag +in Argo CD command parameters. The easiest way to properly enable +this feature flag is by adding the `server.enable.proxy.extension` key +in the existing `argocd-cmd-params-cm`. For example: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cmd-params-cm + namespace: argocd +data: + server.enable.proxy.extension: "true" +``` + +Once the proxy extension is enabled, it can be configured in the main +Argo CD configmap ([argocd-cm][2]). + +The example below demonstrates all possible configurations available +for proxy extensions: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cm + namespace: argocd +data: + extension.config: | + extensions: + - name: httpbin + backend: + connectionTimeout: 2s + keepAlive: 15s + idleConnectionTimeout: 60s + maxIdleConnections: 30 + services: + - url: http://httpbin.org + headers: + - name: some-header + value: '$some.argocd.secret.key' + cluster: + name: some-cluster + server: https://some-cluster +``` + +Note: There is no need to restart Argo CD Server after modifiying the +`extension.config` entry in Argo CD configmap. Changes will be +automatically applied. A new proxy registry will be built making +all new incoming extensions requests (`/extensions/*`) to +respect the new configuration. + +Every configuration entry is explained below: + +#### `extensions` (*list*) + +Defines configurations for all extensions enabled. + +#### `extensions.name` (*string*) +(mandatory) + +Defines the endpoint that will be used to register the extension +route. For example, if the value of the property is `extensions.name: +my-extension` then the backend service will be exposed under the +following url: + + /extensions/my-extension + +#### `extensions.backend.connectionTimeout` (*duration string*) +(optional. Default: 2s) + +Is the maximum amount of time a dial to the extension server will wait +for a connect to complete. + +#### `extensions.backend.keepAlive` (*duration string*) +(optional. Default: 15s) + +Specifies the interval between keep-alive probes for an active network +connection between the API server and the extension server. + +#### `extensions.backend.idleConnectionTimeout` (*duration string*) +(optional. Default: 60s) + +Is the maximum amount of time an idle (keep-alive) connection between +the API server and the extension server will remain idle before +closing itself. + +#### `extensions.backend.maxIdleConnections` (*int*) +(optional. Default: 30) + +Controls the maximum number of idle (keep-alive) connections between +the API server and the extension server. + +#### `extensions.backend.services` (*list*) + +Defines a list with backend url by cluster. + +#### `extensions.backend.services.url` (*string*) +(mandatory) + +Is the address where the extension backend must be available. + +#### `extensions.backend.services.headers` (*list*) + +If provided, the headers list will be added on all outgoing requests +for this service config. Existing headers in the incoming request with +the same name will be overriden by the one in this list. Reserved header +names will be ignored (see the [headers](#incoming-request-headers) below). + +#### `extensions.backend.services.headers.name` (*string*) +(mandatory) + +Defines the name of the header. It is a mandatory field if a header is +provided. + +#### `extensions.backend.services.headers.value` (*string*) +(mandatory) + +Defines the value of the header. It is a mandatory field if a header is +provided. The value can be provided as verbatim or as a reference to an +Argo CD secret key. In order to provide it as a reference, it is +necessary to prefix it with a dollar sign. + +Example: + + value: '$some.argocd.secret.key' + +In the example above, the value will be replaced with the one from +the argocd-secret with key 'some.argocd.secret.key'. + +#### `extensions.backend.services.cluster` (*object*) +(optional) + +If provided, and multiple services are configured, will have to match +the application destination name or server to have requests properly +forwarded to this service URL. If there are multiple backends for the +same extension this field is required. In this case at least one of +the two will be required: name or server. It is better to provide both +values to avoid problems with applications unable to send requests to +the proper backend service. If only one backend service is +configured, this field is ignored, and all requests are forwarded to +the configured one. + +#### `extensions.backend.services.cluster.name` (*string*) +(optional) + +It will be matched with the value from +`Application.Spec.Destination.Name` + +#### `extensions.backend.services.cluster.server` (*string*) +(optional) + +It will be matched with the value from +`Application.Spec.Destination.Server`. + +## Usage + +Once a proxy extension is configured it will be made available under +the `/extensions/` endpoint exposed by Argo CD API +server. The example above will proxy requests to +`/extensions/httpbin/` to `http://httpbin.org`. + +The diagram below illustrates an interaction possible with this +configuration: + +``` + ┌─────────────┐ + │ Argo CD UI │ + └────┬────────┘ + │ ▲ + GET /extensions/httpbin/anything │ │ 200 OK + + authn/authz headers │ │ + ▼ │ + ┌─────────┴────────┐ + │Argo CD API Server│ + └──────┬───────────┘ + │ ▲ + GET http://httpbin.org/anything │ │ 200 OK + │ │ + ▼ │ + ┌────────┴────────┐ + │ Backend Service │ + └─────────────────┘ +``` + +### Incoming Request Headers + +Note that Argo CD API Server requires additional HTTP headers to be +sent in order to enforce if the incoming request is authenticated and +authorized before being proxied to the backend service. The headers +are documented below: + +#### `Cookie` + +Argo CD UI keeps the authentication token stored in a cookie +(`argocd.token`). This value needs to be sent in the `Cookie` header +so the API server can validate its authenticity. + +Example: + + Cookie: argocd.token=eyJhbGciOiJIUzI1Ni... + +The entire Argo CD cookie list can also be sent. The API server will +only use the `argocd.token` attribute in this case. + +#### `Argocd-Application-Name` (mandatory) + +This is the name of the project for the application for which the +extension is being invoked. The header value must follow the format: +`":"`. + +Example: + + Argocd-Application-Name: namespace:app-name + +#### `Argocd-Project-Name` (mandatory) + +The logged in user must have access to this project in order to be +authorized. + +Example: + + Argocd-Project-Name: default + +Argo CD API Server will ensure that the logged in user has the +permission to access the resources provided by the headers above. The +validation is based on pre-configured [Argo CD RBAC rules][3]. The +same headers are also sent to the backend service. The backend service +must also validate if the validated headers are compatible with the +rest of the incoming request. + +### Outgoing Requests Headers + +Requests sent to backend services will be decorated with additional +headers. The outgoing request headers are documented below: + +#### `Argocd-Target-Cluster-Name` + +Will be populated with the value from `app.Spec.Destination.Name` if +it is not empty string in the application resource. + +#### `Argocd-Target-Cluster-URL` + +Will be populated with the value from `app.Spec.Destination.Server` if +it is not empty string is the Application resource. + +Note that additional pre-configured headers can be added to outgoing +request. See [backend service headers](#extensionsbackendservicesheaders-list) +section for more details. + +### Multi Backend Use-Case + +In some cases when Argo CD is configured to sync with multiple remote +clusters, there might be a need to call a specific backend service in +each of those clusters. The proxy-extension can be configured to +address this use-case by defining multiple services for the same +extension. Consider the following configuration as an example: + +```yaml +extension.config: | + extensions: + - name: some-extension + backend: + services: + - url: http://extension-name.com:8080 + cluster + name: kubernetes.local + - url: https://extension-name.ppd.cluster.k8s.local:8080 + cluster + server: user@ppd.cluster.k8s.local +``` + +In the example above, the API server will inspect the Application +destination to verify which URL should be used to proxy the incoming +request to. + +## Security + +When a request to `/extensions/*` reaches the API Server, it will +first verify if it is authenticated with a valid token. It does so by +inspecting if the `Cookie` header is properly sent from Argo CD UI +extension. + +Once the request is authenticated it is then verified if the +user has permission to invoke this extension. The permission is +enforced by Argo CD RBAC configuration. The details about how to +configure the RBAC for proxy-extensions can be found in the [RBAC +documentation][3] page. + +Once the request is authenticated and authorized by the API server, it +is then sanitized before being sent to the backend service. The +request sanitization will remove sensitive information from the +request like the `Cookie` and `Authorization` headers. + +A new `Authorization` header can be added to the outgoing request by +defining it as a header in the `extensions.backend.services.headers` +configuration. Consider the following example: + +```yaml +extension.config: | + extensions: + - name: some-extension + backend: + services: + - url: http://extension-name.com:8080 + headers: + - name: Authorization + value: '$some-extension.authorization.header' +``` + +In the example above, all requests sent to +`http://extension-name.com:8080` will have an additional +`Authorization` header. The value of this header will be the one from +the [argocd-secret](../../operator-manual/argocd-secret-yaml.md) with +key `some-extension.authorization.header` + +[1]: https://github.com/argoproj/argoproj/blob/master/community/feature-status.md +[2]: https://argo-cd.readthedocs.io/en/stable/operator-manual/argocd-cm.yaml +[3]: ../../operator-manual/rbac.md#the-extensions-resource diff --git a/docs/developer-guide/extensions/ui-extensions.md b/docs/developer-guide/extensions/ui-extensions.md new file mode 100644 index 0000000000000..8d3d9dc4a3882 --- /dev/null +++ b/docs/developer-guide/extensions/ui-extensions.md @@ -0,0 +1,160 @@ +# UI Extensions + +Argo CD web user interface can be extended with additional UI elements. Extensions should be delivered as a javascript file +in the `argocd-server` Pods that are placed in the `/tmp/extensions` directory and starts with `extension` prefix ( matches to `^extension(.*)\.js$` regex ). + +``` +/tmp/extensions +├── example1 +│   └── extension-1.js +└── example2 + └── extension-2.js +``` + +Extensions are loaded during initial page rendering and should register themselves using API exposed in the `extensionsAPI` global variable. (See +corresponding extension type details for additional information). + +The extension should provide a React component that is responsible for rendering the UI element. Extension should not bundle the React library. +Instead extension should use the `react` global variable. You can leverage `externals` setting if you are using webpack: + +```js +externals: { + react: "React"; +} +``` + +## Resource Tab Extensions + +Resource Tab extensions is an extension that provides an additional tab for the resource sliding panel at the Argo CD Application details page. + +The resource tab extension should be registered using the `extensionsAPI.registerResourceExtension` method: + +```typescript +registerResourceExtension(component: ExtensionComponent, group: string, kind: string, tabTitle: string) +``` + +- `component: ExtensionComponent` is a React component that receives the following properties: + + - application: Application - Argo CD Application resource; + - resource: State - the Kubernetes resource object; + - tree: ApplicationTree - includes list of all resources that comprise the application; + + See properties interfaces in [models.ts](https://github.com/argoproj/argo-cd/blob/master/ui/src/app/shared/models.ts) + +- `group: string` - the glob expression that matches the group of the resource; note: use globstar (`**`) to match all groups including empty string; +- `kind: string` - the glob expression that matches the kind of the resource; +- `tabTitle: string` - the extension tab title. +- `opts: Object` - additional options: + - `icon: string` - the class name the represents the icon from the [https://fontawesome.com/](https://fontawesome.com/) library (e.g. 'fa-calendar-alt'); + +Below is an example of a resource tab extension: + +```javascript +((window) => { + const component = () => { + return React.createElement("div", {}, "Hello World"); + }; + window.extensionsAPI.registerResourceExtension( + component, + "*", + "*", + "Nice extension" + ); +})(window); +``` + +## System Level Extensions + +Argo CD allows you to add new items to the sidebar that will be displayed as a new page with a custom component when clicked. The system level extension should be registered using the `extensionsAPI.registerSystemLevelExtension` method: + +```typescript +registerSystemLevelExtension(component: ExtensionComponent, title: string, options: {icon?: string}) +``` + +Below is an example of a simple system level extension: + +```typescript +((window) => { + const component = () => { + return React.createElement( + "div", + { style: { padding: "10px" } }, + "Hello World" + ); + }; + window.extensionsAPI.registerSystemLevelExtension( + component, + "Test Ext", + "/hello", + "fa-flask" + ); +})(window); +``` + +## Application Tab Extensions + +Since the Argo CD Application is a Kubernetes resource, application tabs can be the same as any other resource tab. +Make sure to use 'argoproj.io'/'Application' as group/kind and an extension will be used to render the application-level tab. + +## Application Status Panel Extensions + +The status panel is the bar at the top of the application view where the sync status is displayed. Argo CD allows you to add new items to the status panel of an application. The extension should be registered using the `extensionsAPI.registerStatusPanelExtension` method: + +```typescript +registerStatusPanelExtension(component: StatusPanelExtensionComponent, title: string, id: string, flyout?: ExtensionComponent) +``` + +Below is an example of a simple extension: + +```typescript +((window) => { + const component = () => { + return React.createElement( + "div", + { style: { padding: "10px" } }, + "Hello World" + ); + }; + window.extensionsAPI.registerStatusPanelExtension( + component, + "My Extension", + "my_extension" + ); +})(window); +``` + +### Flyout widget + +It is also possible to add an optional flyout widget to your extension. It can be opened by calling `openFlyout()` from your extension's component. Your flyout component will then be rendered in a sliding panel, similar to the panel that opens when clicking on `History and rollback`. + +Below is an example of an extension using the flyout widget: + +```typescript +((window) => { + const component = (props: { + openFlyout: () => any + }) => { + return React.createElement( + "div", + { + style: { padding: "10px" }, + onClick: () => props.openFlyout() + }, + "Hello World" + ); + }; + const flyout = () => { + return React.createElement( + "div", + { style: { padding: "10px" } }, + "This is a flyout" + ); + }; + window.extensionsAPI.registerStatusPanelExtension( + component, + "My Extension", + "my_extension", + flyout + ); +})(window); +``` diff --git a/docs/developer-guide/faq.md b/docs/developer-guide/faq.md index 0a8d936477bb4..5d9dda31949f7 100644 --- a/docs/developer-guide/faq.md +++ b/docs/developer-guide/faq.md @@ -6,11 +6,20 @@ 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-contributors to discuss your ideas and get guidance for submitting a PR. +!!! note + Regular [contributor meetings](https://argo-cd.readthedocs.io/en/latest/developer-guide/code-contributions/#regular-contributor-meeting) are held weekly. Please follow the link for more details. + ### No one has looked at my PR yet. Why? -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. +As we have limited resources, 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. Kindly ensure all applicable requirements have been met in your PR checklist. + +### How do I get my PR labeled `ready-for-review` ? + +Conventionally an initial review is performed from a Argo member or reviewer. Once the initial review is approved, it can be labeled `ready-for-review` and then added to the [Argo CD Review](https://github.com/orgs/argoproj/projects/28) Github project. Details of the project dashboard can be found [here](https://github.com/orgs/argoproj/projects/28?pane=info). + +High quality reviews are extremely encouraged from the community. A member/reviewer may work with a community reviewer to get a PR labeled `ready-for-review`. It can then be added to the project dashboard and marked `Community Reviewed`. -### Why has my PR been declined? I put much work in it! +### Why has my PR been declined? I put so much work into it! We appreciate that you have put your valuable time and know how into a contribution. Alas, some changes do not fit into the overall ArgoCD philosophy, and therefore can't be merged into the official ArgoCD source tree. diff --git a/docs/developer-guide/release-process-and-cadence.md b/docs/developer-guide/release-process-and-cadence.md index 71e3072aed58c..3bedd35ff4b3c 100644 --- a/docs/developer-guide/release-process-and-cadence.md +++ b/docs/developer-guide/release-process-and-cadence.md @@ -6,12 +6,15 @@ These are the upcoming releases dates: -| Release | Release Planning Meeting | Release Candidate 1 | General Availability | Release Champion | Checklist | -|---------|--------------------------|-----------------------|----------------------|-------------------------------------------------------|---------------------------------------------------------------| -| v2.6 | Monday, Dec. 12, 2022 | Monday, Dec. 19, 2022 | Monday, Feb. 6, 2023 | [William Tam](https://github.com/wtam2018) | [checklist](https://github.com/argoproj/argo-cd/issues/11563) | -| v2.7 | Monday, Mar. 6, 2023 | Monday, Mar. 20, 2023 | Monday, May. 1, 2023 | [Pavel Kostohrys](https://github.com/pasha-codefresh) | -| v2.8 | Monday, Jun. 5, 2023 | Monday, Jun. 19, 2023 | Monday, Aug. 7, 2023 | -| v2.9 | Monday, Sep. 4, 2023 | Monday, Sep. 18, 2023 | Monday, Nov. 6, 2023 | +| Release | Release Candidate 1 | General Availability | Release Champion | Release Approver |Checklist | +|---------|-----------------------|----------------------|-------------------------------------------------------|-------------------------------------------------------|---------------------------------------------------------------| +| v2.6 | Monday, Dec. 19, 2022 | Monday, Feb. 6, 2023 | [William Tam](https://github.com/wtam2018) | [William Tam](https://github.com/wtam2018) | [checklist](https://github.com/argoproj/argo-cd/issues/11563) | +| v2.7 | Monday, Mar. 20, 2023 | Monday, May 1, 2023 | [Pavel Kostohrys](https://github.com/pasha-codefresh) | [Pavel Kostohrys](https://github.com/pasha-codefresh) | [checklist](https://github.com/argoproj/argo-cd/issues/12762) | +| v2.8 | Monday, Jun. 26, 2023 | Monday, Aug. 7, 2023 | [Keith Chong](https://github.com/keithchong) | [Keith Chong](https://github.com/keithchong) | [checklist](https://github.com/argoproj/argo-cd/issues/13742) | +| v2.9 | Monday, Sep. 18, 2023 | Monday, Nov. 6, 2023 | [Leonardo Almeida](https://github.com/leoluz) | [Leonardo Almeida](https://github.com/leoluz) | [checklist](https://github.com/argoproj/argo-cd/issues/14078) | +| v2.10 | Monday, Dec. 18, 2023 | Monday, Feb. 5, 2024 | [Katie Lamkin](https://github.com/kmlamkin9) | | [checklist](https://github.com/argoproj/argo-cd/issues/16339) | +| v2.11 | Monday, Mar. 18, 2024 | Monday, May 6, 2024 | +| v2.12 | Monday, Jun. 17, 2024 | Monday, Aug. 5, 2024 | Actual release dates might differ from the plan by a few days. @@ -20,8 +23,8 @@ Actual release dates might differ from the plan by a few days. #### Minor Releases (e.g. 2.x.0) A minor Argo CD release occurs four times a year, once every three months. Each General Availability (GA) release is -preceded by several Release Candidates (RCs). The first RC is released three weeks before the scheduled GA date. This -effectively means that there is a three-week feature freeze. +preceded by several Release Candidates (RCs). The first RC is released seven weeks before the scheduled GA date. This +effectively means that there is a seven-week feature freeze. These are the approximate release dates: @@ -38,17 +41,6 @@ Argo CD patch releases occur on an as-needed basis. Only the three most recent m releases. Versions older than the three most recent minor versions are considered EOL and will not receive bug fixes or security updates. -#### Minor Release Planning Meeting - -Roughly two weeks before the RC date, there will be a meeting to discuss which features are planned for the RC. This meeting is -for contributors to advocate for certain features. Features which have at least one approver (besides the contributor) -who can assure they will review/merge by the RC date will be included in the release milestone. All other features will -be dropped from the milestone (and potentially shifted to the next one). - -Since not everyone will be able to attend the meeting, there will be a meeting doc. Contributors can add their feature -to a table, and Approvers can add their name to the table. Features with a corresponding approver will remain in the -release milestone. - #### Release Champion To help manage all the steps involved in a release, we will have a Release Champion. The Release Champion will be @@ -76,3 +68,21 @@ The feature PR must include: If these criteria are not met by the RC date, the feature will be ineligible for inclusion in the RC series or GA for that minor release. It will have to wait for the next minor release. + +### Security Patch Policy + +CVEs in Argo CD code will be patched for all supported versions. Read more about supported versions in the [security policy for Argo CD](https://github.com/argoproj/argo-cd/security/policy#supported-versions). + +### Dependencies Lifecycle Policy + +Dependencies are evaluated before being introduced to ensure they: + +1) are actively maintained +2) are maintained by trustworthy maintainers + +These evaluations vary from dependency to dependencies. + +Dependencies are also scheduled for removal if the project has been deprecated or if the project is no longer maintained. + +CVEs in dependencies will be patched for all supported versions if the CVE is applicable and is assessed by Snyk to be +of high or critical severity. Automation generates a [new Snyk scan weekly](../snyk). diff --git a/docs/developer-guide/releasing.md b/docs/developer-guide/releasing.md index ed8297fb0e5c5..bb51ebfa8d14b 100644 --- a/docs/developer-guide/releasing.md +++ b/docs/developer-guide/releasing.md @@ -1,113 +1,86 @@ # Releasing -## Automated release procedure +## Introduction -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. +Argo CD is released in a 2 step automated fashion using GitHub actions. The release process takes about 60 minutes, +sometimes a little less, depending on the performance of GitHub Actions runners. 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 built. Also, +example want to create a release `v2.7.0`, the corresponding release branch +`release-2.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 committed and pushed to the GitHub repository to the release branch. Afterward, the automation can be -triggered. +triggered. This will be automated in the very near future. **Manual steps before release creation:** * Update `CHANGELOG.md` with changes for this release * Commit & push changes to `CHANGELOG.md` -* Prepare release notes (save to some file, or copy from Changelog) -**The automation will perform the following steps:** +**The `Init ARGOCD Release` workflow will perform the following steps:** * 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 a release tag in the GitHub repository -* Create a GitHub release and attach the required assets to it (CLI binaries, ...) +* Create a pull request to submit the above changes -Finally, it will the remove trigger tag from the repository again. +**The `Publish ArgoCD Release` workflow will perform the following steps:** -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 -to trigger the GH workflow: +* Build, push, and signs the container image to Quay.io +* Generate a provenance for the container image +* Builds the CLI binaries, release-notes, and then creates a GitHub release and attaches the required assets. +* Generate a provenance for the CLI binaries +* Generate and sign a sbom +* Update the stable tag when applicable +* Update `VERSION` file in the master branch when a new release is GA -* GA: `release-v..` -* Pre-release: `release-v..-rc` +## Steps -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's comment char -to something else, the `;` character is recommended. +### Step 1 - Update Version and Manifest -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: +1. Ensure that the TARGET_BRANCH already exist. +2. Visit the [Release GitHub Action](https://github.com/argoproj/argo-cd/actions/workflows/init-release.yaml) +and choose which branch you would like to work from. +3. Enter the TARGET_BRANCH to checkout. +4. Enter the TARGET_VERSION that will be used to build manifest and `VERSION` file. (e.g `2.7.0-rc1`) -```shell -git config core.commentChar ';' -git tag -a -F /path/to/release-notes.txt release-v1.6.0-rc2 -git push upstream release-v1.6.0-rc2 -git tag -d release-v1.6.0-rc2 -git config core.commentChar '#' +![GitHub Release Action](../assets/release-action.png) -``` +When the action is completed a pull request will be generated that contains the updated manifest and `Version` file. + +5. Merge the pull request and proceed to step 2. -For convenience, there is a shell script in the tree that ensures all the -pre-requisites are met and that the trigger is well-formed before pushing -it to the GitHub repo. +### Step 2 - Tag Release Branch -In summary, the modifications it does are: +The steps below need to be executed by someone with write access in Argo CD upstream repo. -* Create annotated trigger tag in your local repository -* Push the tag to the GitHub repository to trigger the workflow -* Remove trigger tag from your local repository +1. Checkout the release branch. Example: `git fetch upstream && git + checkout release-2.7` +2. Run the script found at `hack/trigger-release.sh` as follows: -The script can be found at `hack/trigger-release.sh` and is used as follows: +```shell +./hack/trigger-release.sh +``` +Example: ```shell -./hack/trigger-release.sh [] +./hack/trigger-release.sh v2.7.2 upstream ``` -The `` identifier needs to be specified **without** the `release-` -prefix, so just specify it as `v1.6.0-rc2` for example. The `` -specifies the name of the remote used to push to the GitHub repository. - -If you omit the ``, an editor will pop-up asking you to -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. - -: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 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 the `master` -branch, this is a limitation of GH actions. - -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 -of the step that has failed. - -:warning: +!!! tip + The tag must be in one of the following formats to trigger the GH workflow:
+ * GA: `v..`
+ * Pre-release: `v..-rc` + +Once the script is executed successfully, a GitHub workflow will start +execution. You can follow its progress under the [Actions](https://github.com/argoproj/argo-cd/actions/workflows/release.yaml) tab, the name of the action is `Publish ArgoCD Release`. + +!!! 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. + same time. ### Verifying automated release @@ -118,119 +91,26 @@ checks to see if the release came out correctly: * Check [https://github.com/argoproj/argo-cd/releases](https://github.com/argoproj/argo-cd/releases) 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 +* Check whether the image has been published on Quay.io correctly ### If something went wrong If something went wrong, damage should be limited. Depending on the steps that have been performed, you will need to manually clean up. -* 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 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 committed to are: - * `VERSION` - * `manifests/*` - -### Post-process manual steps - -For now, the only manual steps left are to - -* 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 may be automated as well in the future. - -## Manual releasing - -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: - -Make sure you are logged into Docker Hub: - -```bash -docker login -``` - -Export the upstream repository and branch name, e.g.: - -```bash -REPO=upstream ;# or origin -BRANCH=release-1.3 -``` - -Set the `VERSION` environment variable: - -```bash -# release candidate -VERSION=v1.3.0-rc1 -# GA release -VERSION=v1.3.1 -``` - -Update `VERSION` and manifests with the new version: - -```bash -git checkout $BRANCH -echo ${VERSION:1} > VERSION -make dev-tools-image -make manifests IMAGE_TAG=$VERSION -git commit -am "Update manifests to $VERSION" -git tag $VERSION -``` - -Build, and push release to Docker Hub - -```bash -git clean -fd -make release IMAGE_NAMESPACE=argoproj IMAGE_TAG=$VERSION DOCKER_PUSH=true -git push $REPO $BRANCH -git push $REPO $VERSION -``` - -Update [GitHub releases](https://github.com/argoproj/argo-cd/releases) with: - -* Getting started (copy from the previous release) -* Changelog -* Binaries (e.g. `dist/argocd-darwin-amd64`). - -## Update brew formulae (manual) - -If GA, update the Brew formula: - -```bash -brew bump-formula-pr argocd --version ${VERSION:1} -``` - -## Update stable tag (manual) - -If GA, update `stable` tag: - -```bash -git tag stable --force && git push $REPO stable --force -``` - -## Verify release +* If the container image has been pushed to Quay.io, delete it +* Delete the release (if created) from the `Releases` page on GitHub -Locally: +### Manual releasing -```bash -kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/$VERSION/manifests/install.yaml -``` - -Follow the [Getting Started Guide](../getting_started/). - -If GA: - -```bash -brew upgrade argocd -/usr/local/bin/argocd version -``` +The release process does not allow a manual release process. Image signatures and provenance need to be created using GitHub Actions. -Sync Argo CD in [https://cd.apps.argoproj.io/applications/argo-cd](https://cd.apps.argoproj.io/applications/argo-cd). +## Notable files that involve the release process. -Deploy the [site](site.md). +| File | Description | +|------------------------------------|--------------------------------------------------------| +|goreleaser.yaml |Config to build CLI binaries, checksums, release-notes | +|.github/workflows/image-reuse.yaml |Reusable workflow used to generate container images | +|.github/workflows/init-release.yaml |Used to generate manifest and `VERSION` file | +|.github/workflows/release.yaml |Build image, CLI binaries, provenances, sbom, post jobs | +|./hack/trigger-release.sh |Ensures all pre-requistes are met and pushes the tag | diff --git a/docs/developer-guide/running-locally.md b/docs/developer-guide/running-locally.md index f4f5dd646da20..25f4510e9e18a 100644 --- a/docs/developer-guide/running-locally.md +++ b/docs/developer-guide/running-locally.md @@ -2,7 +2,7 @@ ## Run Argo CD outside of Kubernetes -During development, it might be viable to run Argo CD outside of a Kubernetes cluster. This will greatly speed up development, as you don't have to constantly build, push and install new Argo CD Docker images with your latest changes. +During development, it might be viable to run Argo CD outside a Kubernetes cluster. This will greatly speed up development, as you don't have to constantly build, push and install new Argo CD Docker images with your latest changes. You will still need a working Kubernetes cluster, as described in the [Toolchain Guide](toolchain-guide.md), where Argo CD will store all of its resources and configuration. @@ -179,7 +179,7 @@ For your final tests, it might be necessary to build your own images and run the ### Create Docker account and login -You might need to create a account on [Docker Hub](https://hub.docker.com) if you don't have one already. Once you created your account, login from your development environment: +You might need to create an account on [Docker Hub](https://hub.docker.com) if you don't have one already. Once you created your account, login from your development environment: ```bash docker login diff --git a/docs/developer-guide/site.md b/docs/developer-guide/site.md index c1ff0cac6251e..33106cd5fa939 100644 --- a/docs/developer-guide/site.md +++ b/docs/developer-guide/site.md @@ -2,24 +2,19 @@ ## Developing And Testing -The web site is build using `mkdocs` and `mkdocs-material`. +The website is built using `mkdocs` and `mkdocs-material`. To test: ```bash make serve-docs ``` +Once running, you can view your locally built documentation at [http://0.0.0.0:8000/](http://0.0.0.0:8000/). +Make a change to documentation and the website will rebuild and refresh the view. -Check for broken external links: - -```bash -make lint-docs -``` - -## Deploying - +Before submitting a PR build the website, to verify that there are no errors building the site ```bash -make publish-docs +make build-docs ``` ## Analytics @@ -27,4 +22,4 @@ make publish-docs !!! tip Don't forget to disable your ad-blocker when testing. -We collect [Google Analytics](https://analytics.google.com/analytics/web/#/report-home/a105170809w198079555p192782995). \ No newline at end of file +We collect [Google Analytics](https://analytics.google.com/analytics/web/#/report-home/a105170809w198079555p192782995). diff --git a/docs/developer-guide/test-e2e.md b/docs/developer-guide/test-e2e.md index 3d46fd55fdba9..477723016bd75 100644 --- a/docs/developer-guide/test-e2e.md +++ b/docs/developer-guide/test-e2e.md @@ -33,7 +33,7 @@ Some effort has been made to balance test isolation with speed. Tests are isolat * A random 5 character ID. * A unique Git repository containing the `testdata` in `/tmp/argocd-e2e/${id}`. * A namespace `argocd-e2e-ns-${id}`. -* An primary name for the app `argocd-e2e-${id}`. +* A primary name for the app `argocd-e2e-${id}`. ## Troubleshooting diff --git a/docs/developer-guide/toolchain-guide.md b/docs/developer-guide/toolchain-guide.md index c529416a2634e..9bba72b456f71 100644 --- a/docs/developer-guide/toolchain-guide.md +++ b/docs/developer-guide/toolchain-guide.md @@ -53,7 +53,7 @@ The following read will help you to submit a PR that meets the standards of our Please use a meaningful and concise title for your PR. This will help us to pick PRs for review quickly, and the PR title will also end up in the Changelog. -We use the [Semantic PR title checker](https://github.com/zeke/semantic-pull-requests) to categorize your PR into one of the following categories: +We use [PR title checker](https://github.com/marketplace/actions/pr-title-checker) to categorize your PR into one of the following categories: * `fix` - Your PR contains one or more code bug fixes * `feat` - Your PR contains a new feature @@ -138,6 +138,14 @@ The following steps are required no matter whether you chose to use a virtualize export SUDO=sudo ``` + If you have podman installed, you can also leverage its rootless mode. In + order to use podman for running and testing Argo CD locally, set the + `DOCKER` environment variable to `podman` before you run `make`, e.g. + + ``` + DOCKER=podman make start + ``` + ### Clone the Argo CD repository from your personal fork on GitHub * `mkdir -p ~/go/src/github.com/argoproj` @@ -157,9 +165,9 @@ Make sure you fulfill the pre-requisites above and run some preliminary tests. N * Run `docker version` * Run `go version` -### Build (or pull) the required Docker image +### Build the required Docker image -Build the required Docker image by running `make test-tools-image` or pull the latest version by issuing `docker pull argoproj/argocd-test-tools`. +Build the required Docker image by running `make test-tools-image`. This image offers the environment of the virtualized toolchain. The `Dockerfile` used to build these images can be found at `test/container/Dockerfile`. @@ -178,7 +186,7 @@ you should edit your `~/.kube/config` and modify the `server` option to point to ### 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. +[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. @@ -205,10 +213,11 @@ you should edit your `~/.kube/config` and modify the `server` option to point to 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 +k3d cluster create my-cluster --wait --k3s-arg '--disable=traefik@server:*' --api-port $IP:6550 -p 443:443@loadbalancer ``` -Starting from k3d v5.0.0 the example command flags `--k3s-server-arg` and `'--disable=traefik'` would have to be changed to `--k3s-arg` and `'--disable=traefik@server:*'`, respectively. +!!!note +For k3d versions less than v5.0.0, the example command flags `--k3s-arg` and `'--disable=traefik@server:*'` should change to `--k3s-server-arg` and `'--disable=traefik'`, respectively. ## The development cycle @@ -303,7 +312,7 @@ For installing the tools required to build and test Argo CD on your local system You can change the target location by setting the `BIN` environment before running the installer scripts. For example, you can install the binaries into `~/go/bin` (which should then be the first component in your `PATH` environment, i.e. `export PATH=~/go/bin:$PATH`): ```shell -make BIN=~/go/bin install-tools-local +BIN=~/go/bin make install-tools-local ``` Additionally, you have to install at least the following tools via your OS's package manager (this list might not be always up-to-date): @@ -335,7 +344,7 @@ The next thing is to make sure that unit tests are running correctly on your sys ### Run end-to-end tests -The final step is running the End-to-End testsuite, which makes sure that your Kubernetes dependencies are working properly. This will involve starting all of the Argo CD components locally on your computer. The end-to-end tests consists of two parts: a server component, and a client component. +The final step is running the End-to-End testsuite, which makes sure that your Kubernetes dependencies are working properly. This will involve starting all the Argo CD components locally on your computer. The end-to-end tests consists of two parts: a server component, and a client component. * First, start the End-to-End server: `make start-e2e-local`. This will spawn a number of processes and services on your system. * When all components have started, run `make test-e2e-local` to run the end-to-end tests against your local services. diff --git a/docs/developer-guide/ui-extensions.md b/docs/developer-guide/ui-extensions.md index 2c25748beb148..dfabfb5574ead 100644 --- a/docs/developer-guide/ui-extensions.md +++ b/docs/developer-guide/ui-extensions.md @@ -1,97 +1,2 @@ -# UI Extensions - -Argo CD web user interface can be extended with additional UI elements. Extensions should be delivered as a javascript file -in the `argocd-server` Pods that are placed in the `/tmp/extensions` directory and starts with `extension` prefix ( matches to `^extension(.*)\.js$` regex ). - -``` -/tmp/extensions -├── example1 -│   └── extension-1.js -└── example2 - └── extension-2.js -``` - -Extensions are loaded during initial page rendering and should register themselves using API exposed in the `extensionsAPI` global variable. (See -corresponding extension type details for additional information). - -The extension should provide a React component that is responsible for rendering the UI element. Extension should not bundle the React library. -Instead extension should use the `react` global variable. You can leverage `externals` setting if you are using webpack: - -```js -externals: { - react: "React"; -} -``` - -## Resource Tab Extensions - -Resource Tab extensions is an extension that provides an additional tab for the resource sliding panel at the Argo CD Application details page. - -The resource tab extension should be registered using the `extensionsAPI.registerResourceExtension` method: - -```typescript -registerResourceExtension(component: ExtensionComponent, group: string, kind: string, tabTitle: string) -``` - -- `component: ExtensionComponent` is a React component that receives the following properties: - - - application: Application - Argo CD Application resource; - - resource: State - the kubernetes resource object; - - tree: ApplicationTree - includes list of all resources that comprise the application; - - See properties interfaces in [models.ts](https://github.com/argoproj/argo-cd/blob/master/ui/src/app/shared/models.ts) - -- `group: string` - the glob expression that matches the group of the resource; note: use globstar (`**`) to match all groups including empty string; -- `kind: string` - the glob expression that matches the kind of the resource; -- `tabTitle: string` - the extension tab title. -- `opts: Object` - additional options: - - `icon: string` - the class name the represents the icon from the [https://fontawesome.com/](https://fontawesome.com/) library (e.g. 'fa-calendar-alt'); - -Below is an example of a resource tab extension: - -```javascript -((window) => { - const component = () => { - return React.createElement("div", {}, "Hello World"); - }; - window.extensionsAPI.registerResourceExtension( - component, - "*", - "*", - "Nice extension" - ); -})(window); -``` - -## System Level Extensions - -Argo CD allows you to add new items to the sidebar that will be displayed as a new page with a custom component when clicked. The system level extension should be registered using the `extensionsAPI.registerSystemLevelExtension` method: - -```typescript -registerSystemLevelExtension(component: ExtensionComponent, title: string, options: {icon?: string}) -``` - -Below is an example of a simple system level extension: - -```typescript -((window) => { - const component = () => { - return React.createElement( - "div", - { style: { padding: "10px" } }, - "Hello World" - ); - }; - window.extensionsAPI.registerSystemLevelExtension( - component, - "Test Ext", - "/hello", - "fa-flask" - ); -})(window); -``` - -## Application Tab Extensions - -Since the Argo CD Application is a Kubernetes resource, application tabs can be the same as any other resource tab. -Make sure to use 'argoproj.io'/'Application' as group/kind and an extension will be used to render the application-level tab. +The contents of this document have been moved to the +[extensions guide](./extensions/ui-extensions.md) diff --git a/docs/developer-guide/use-gitpod.md b/docs/developer-guide/use-gitpod.md index bbea575b5ac5b..12b2c49eabf40 100644 --- a/docs/developer-guide/use-gitpod.md +++ b/docs/developer-guide/use-gitpod.md @@ -9,7 +9,7 @@ for Argo CD development. 1. Fork [https://github.com/argoproj/argo-cd](https://github.com/argoproj/argo-cd) repository 1. Create Gitpod workspace by opening the following url in the browser: `https://gitpod.io/#https://github.com//argo-cd` where - `` is your Github username. + `` is your GitHub username. 1. Once workspace is created you should see VSCode editor in the browser as well as workspace initialization logs in the VSCode terminal. The initialization process downloads all backend and UI dependencies as well diff --git a/docs/faq.md b/docs/faq.md index 91ba8064c4ab8..83bdf8d7d38b5 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -36,6 +36,15 @@ which might cause health check to return `Progressing` state instead of `Healthy As workaround Argo CD allows providing [health check](operator-manual/health.md) customization which overrides default behavior. +If you are using Traefik for your Ingress, you can update the Traefik config to publish the loadBalancer IP using [publishedservice](https://doc.traefik.io/traefik/providers/kubernetes-ingress/#publishedservice), which will resolve this issue. + +```yaml +providers: + kubernetesIngress: + publishedService: + enabled: true +``` + ## I forgot the admin password, how do I reset it? For Argo CD v1.8 and earlier, the initial password is set to the name of the server pod, as @@ -88,7 +97,7 @@ data: ## After deploying my Helm application with Argo CD I cannot see it with `helm ls` and other Helm commands -When deploying a Helm application Argo CD is using Helm +When deploying a Helm application Argo CD is using Helm only as a template mechanism. It runs `helm template` and then deploys the resulting manifests on the cluster instead of doing `helm install`. This means that you cannot use any Helm command to view/verify the application. It is fully managed by Argo CD. @@ -122,27 +131,27 @@ To terminate the sync, click on the "synchronisation" then "terminate": ![Synchronization](assets/synchronization-button.png) ![Terminate](assets/terminate-button.png) -## Why Is My App Out Of Sync Even After Syncing? +## Why Is My App `Out Of Sync` Even After Syncing? -Is some cases, the tool you use may conflict with Argo CD by adding the `app.kubernetes.io/instance` label. E.g. using +In some cases, the tool you use may conflict with Argo CD by adding the `app.kubernetes.io/instance` label. E.g. using Kustomize common labels feature. Argo CD automatically sets the `app.kubernetes.io/instance` label and uses it to determine which resources form the app. If the tool does this too, this causes confusion. You can change this label by setting the `application.instanceLabelKey` value in the `argocd-cm`. We recommend that you use `argocd.argoproj.io/instance`. -!!! note +!!! note When you make this change your applications will become out of sync and will need re-syncing. See [#1482](https://github.com/argoproj/argo-cd/issues/1482). ## How often does Argo CD check for changes to my Git or Helm repository ? -The default polling interval is 3 minutes (180 seconds). -You can change the setting by updating the `timeout.reconciliation` value in the [argocd-cm](https://github.com/argoproj/argo-cd/blob/2d6ce088acd4fb29271ffb6f6023dbb27594d59b/docs/operator-manual/argocd-cm.yaml#L279-L282) config map. If there are any Git changes, ArgoCD will only update applications with the [auto-sync setting](user-guide/auto_sync.md) enabled. If you set it to `0` then Argo CD will stop polling Git repositories automatically and you can only use alternative methods such as [webhooks](operator-manual/webhook.md) and/or manual syncs for deploying applications. +The default polling interval is 3 minutes (180 seconds) with a configurable jitter. +You can change the setting by updating the `timeout.reconciliation` value and the `timeout.reconciliation.jitter` in the [argocd-cm](https://github.com/argoproj/argo-cd/blob/2d6ce088acd4fb29271ffb6f6023dbb27594d59b/docs/operator-manual/argocd-cm.yaml#L279-L282) config map. If there are any Git changes, Argo CD will only update applications with the [auto-sync setting](user-guide/auto_sync.md) enabled. If you set it to `0` then Argo CD will stop polling Git repositories automatically and you can only use alternative methods such as [webhooks](operator-manual/webhook.md) and/or manual syncs for deploying applications. -## Why Are My Resource Limits Out Of Sync? +## Why Are My Resource Limits `Out Of Sync`? Kubernetes has normalized your resource limits when they are applied, and then Argo CD has then compared the version in your generated manifests to the normalized one is Kubernetes - they won't match. @@ -157,7 +166,7 @@ E.g. To fix this use diffing customizations [settings](./user-guide/diffing.md#known-kubernetes-types-in-crds-resource-limits-volume-mounts-etc). -## How Do I Fix "invalid cookie, longer than max length 4093"? +## How Do I Fix `invalid cookie, longer than max length 4093`? Argo CD uses a JWT as the auth token. You likely are part of many groups and have gone over the 4KB limit which is set for cookies. You can get the list of groups by opening "developer tools -> network" @@ -194,7 +203,7 @@ argocd ... --insecure ## I have configured Dex via `dex.config` in `argocd-cm`, it still says Dex is unconfigured. Why? -Most likely you forgot to set the `url` in `argocd-cm` to point to your ArgoCD as well. See also +Most likely you forgot to set the `url` in `argocd-cm` to point to your Argo CD as well. See also [the docs](./operator-manual/user-management/index.md#2-configure-argo-cd-for-sso). ## Why are `SealedSecret` resources reporting a `Status`? @@ -208,14 +217,14 @@ fixed CRD if you want this feature to work at all. ##
Why are resources of type `SealedSecret` stuck in the `Progressing` state? The controller of the `SealedSecret` resource may expose the status condition on resource it provisioned. Since -version `v2.0.0` ArgoCD picks up that status condition to derive a health status for the `SealedSecret`. +version `v2.0.0` Argo CD picks up that status condition to derive a health status for the `SealedSecret`. Versions before `v0.15.0` of the `SealedSecret` controller are affected by an issue regarding this status conditions updates, which is why this feature is disabled by default in these versions. Status condition updates may be enabled by starting the `SealedSecret` controller with the `--update-status` command line parameter or by setting the `SEALED_SECRETS_UPDATE_STATUS` environment variable. -To disable ArgoCD from checking the status condition on `SealedSecret` resources, add the following resource +To disable Argo CD from checking the status condition on `SealedSecret` resources, add the following resource customization in your `argocd-cm` ConfigMap via `resource.customizations.health.` key. ```yaml @@ -224,4 +233,38 @@ resource.customizations.health.bitnami.com_SealedSecret: | hs.status = "Healthy" hs.message = "Controller doesn't report resource status" return hs -``` \ No newline at end of file +``` + +## How do I fix `The order in patch list … doesn't match $setElementOrder list: …`? + +An application may trigger a sync error labeled a `ComparisonError` with a message like: + +> The order in patch list: [map[name:**KEY_BC** value:150] map[name:**KEY_BC** value:500] map[name:**KEY_BD** value:250] map[name:**KEY_BD** value:500] map[name:KEY_BI value:something]] doesn't match $setElementOrder list: [map[name:KEY_AA] map[name:KEY_AB] map[name:KEY_AC] map[name:KEY_AD] map[name:KEY_AE] map[name:KEY_AF] map[name:KEY_AG] map[name:KEY_AH] map[name:KEY_AI] map[name:KEY_AJ] map[name:KEY_AK] map[name:KEY_AL] map[name:KEY_AM] map[name:KEY_AN] map[name:KEY_AO] map[name:KEY_AP] map[name:KEY_AQ] map[name:KEY_AR] map[name:KEY_AS] map[name:KEY_AT] map[name:KEY_AU] map[name:KEY_AV] map[name:KEY_AW] map[name:KEY_AX] map[name:KEY_AY] map[name:KEY_AZ] map[name:KEY_BA] map[name:KEY_BB] map[name:**KEY_BC**] map[name:**KEY_BD**] map[name:KEY_BE] map[name:KEY_BF] map[name:KEY_BG] map[name:KEY_BH] map[name:KEY_BI] map[name:**KEY_BC**] map[name:**KEY_BD**]] + + +There are two parts to the message: + +1. `The order in patch list: [` + + This identifies values for items, especially items that appear multiple times: + + > map[name:**KEY_BC** value:150] map[name:**KEY_BC** value:500] map[name:**KEY_BD** value:250] map[name:**KEY_BD** value:500] map[name:KEY_BI value:something] + + You'll want to identify the keys that are duplicated -- you can focus on the first part, as each duplicated key will appear, once for each of its value with its value in the first list. The second list is really just + + `]` + +2. `doesn't match $setElementOrder list: [` + + This includes all of the keys. It's included for debugging purposes -- you don't need to pay much attention to it. It will give you a hint about the precise location in the list for the duplicated keys: + + > map[name:KEY_AA] map[name:KEY_AB] map[name:KEY_AC] map[name:KEY_AD] map[name:KEY_AE] map[name:KEY_AF] map[name:KEY_AG] map[name:KEY_AH] map[name:KEY_AI] map[name:KEY_AJ] map[name:KEY_AK] map[name:KEY_AL] map[name:KEY_AM] map[name:KEY_AN] map[name:KEY_AO] map[name:KEY_AP] map[name:KEY_AQ] map[name:KEY_AR] map[name:KEY_AS] map[name:KEY_AT] map[name:KEY_AU] map[name:KEY_AV] map[name:KEY_AW] map[name:KEY_AX] map[name:KEY_AY] map[name:KEY_AZ] map[name:KEY_BA] map[name:KEY_BB] map[name:**KEY_BC**] map[name:**KEY_BD**] map[name:KEY_BE] map[name:KEY_BF] map[name:KEY_BG] map[name:KEY_BH] map[name:KEY_BI] map[name:**KEY_BC**] map[name:**KEY_BD**] + + `]` + +In this case, the duplicated keys have been **emphasized** to help you identify the problematic keys. Many editors have the ability to highlight all instances of a string, using such an editor can help with such problems. + +The most common instance of this error is with `env:` fields for `containers`. + +!!! note "Dynamic applications" + It's possible that your application is being generated by a tool in which case the duplication might not be evident within the scope of a single file. If you have trouble debugging this problem, consider filing a ticket to the owner of the generator tool asking them to improve its validation and error reporting. diff --git a/docs/getting_started.md b/docs/getting_started.md index f931bb49494fa..68d9f8f9e8872 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -22,12 +22,8 @@ This will create a new namespace, `argocd`, where Argo CD services and applicati The installation manifests include `ClusterRoleBinding` resources that reference `argocd` namespace. If you are installing Argo CD into a different namespace then make sure to update the namespace reference. -If you are not interested in UI, SSO, multi-cluster features then you can install [core](operator-manual/installation.md#core) Argo CD components only: - -```bash -kubectl create namespace argocd -kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/core-install.yaml -``` +!!! tip + If you are not interested in UI, SSO, and multi-cluster features, then you can install only the [core](operator-manual/core/#installing) Argo CD components. This default installation will have a self-signed certificate and cannot be accessed without a bit of extra work. Do one of: @@ -36,6 +32,12 @@ Do one of: * Configure the client OS to trust the self signed certificate. * Use the --insecure flag on all Argo CD CLI operations in this guide. +!!! note + Default namespace for `kubectl` config must be set to `argocd`. + This is only needed for the following commands since the previous commands have -n argocd already: + `kubectl config set-context --current --namespace=argocd` + + Use `argocd login --core` to [configure](./user-guide/commands/argocd_login.md) CLI access and skip steps 3-5. ## 2. Download Argo CD CLI @@ -81,7 +83,7 @@ in your Argo CD installation namespace. You can simply retrieve this password using the `argocd` CLI: ```bash -argocd admin initial-password +argocd admin initial-password -n argocd ``` !!! warning diff --git a/docs/index.md b/docs/index.md index 975b4ae56cae4..ddb17c2bdc36a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -25,7 +25,7 @@ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/st ``` 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). +is provided for additional features. If you are looking to upgrade Argo CD, see the [upgrade guide](./operator-manual/upgrading/overview.md). Developer oriented [documentation](developer-guide/) is available for people interested in building third-party integrations. ## How it works @@ -53,7 +53,7 @@ meeting: ![Argo CD Architecture](assets/argocd_architecture.png) -Argo CD is implemented as a kubernetes controller which continuously monitors running applications +Argo CD is implemented as a Kubernetes controller which continuously monitors running applications and compares the current, live state against the desired target state (as specified in the Git repo). A deployed application whose live state deviates from the target state is considered `OutOfSync`. Argo CD reports & visualizes the differences, while providing facilities to automatically or diff --git a/docs/operator-manual/app-any-namespace.md b/docs/operator-manual/app-any-namespace.md index 1471aa0d221cb..5f4a76d610afd 100644 --- a/docs/operator-manual/app-any-namespace.md +++ b/docs/operator-manual/app-any-namespace.md @@ -1,7 +1,5 @@ # Applications in any namespace -**Current feature state**: Beta - !!! warning Please read this documentation carefully before you enable this feature. Misconfiguration could lead to potential security issues. @@ -15,16 +13,19 @@ Some manual steps will need to be performed by the Argo CD administrator in orde !!! note This feature is considered beta as of now. Some of the implementation details may change over the course of time until it is promoted to a stable status. We will be happy if early adopters use this feature and provide us with bug reports and feedback. - + + +One additional advantage of adopting applications in any namespace is to allow end-users to configure notifications for their Argo CD application in the namespace where Argo CD application is running in. See notifications [namespace based configuration](notifications/index.md#namespace-based-configuration) page for more information. + ## Prerequisites ### Cluster-scoped Argo CD installation -This feature can only be enabled and used when your Argo CD is installed as a cluster-wide instance, so it has permissions to list and manipulate resources on a cluster scope. It will *not* work with an Argo CD installed in namespace-scoped mode. +This feature can only be enabled and used when your Argo CD is installed as a cluster-wide instance, so it has permissions to list and manipulate resources on a cluster scope. It will not work with an Argo CD installed in namespace-scoped mode. ### Switch resource tracking method -Also, while technically not necessary, it is strongly suggested that you switch the application tracking method from the default `label` setting to either `annotation` or `annotation+label`. The reasonsing for this is, that application names will be a composite of the namespace's name and the name of the `Application`, and this can easily exceed the 63 characters length limit imposed on label values. Annotations have a notably greater length limit. +Also, while technically not necessary, it is strongly suggested that you switch the application tracking method from the default `label` setting to either `annotation` or `annotation+label`. The reasoning for this is, that application names will be a composite of the namespace's name and the name of the `Application`, and this can easily exceed the 63 characters length limit imposed on label values. Annotations have a notably greater length limit. To enable annotation based resource tracking, refer to the documentation about [resource tracking methods](../../user-guide/resource_tracking/) @@ -68,9 +69,11 @@ We decided to not extend the Kubernetes RBAC for the `argocd-server` workload by We supply a `ClusterRole` and `ClusterRoleBinding` suitable for this purpose in the `examples/k8s-rbac/argocd-server-applications` directory. For a default Argo CD installation (i.e. installed to the `argocd` namespace), you can just apply them as-is: ```shell -kubectl apply -f examples/k8s-rbac/argocd-server-applications/ +kubectl apply -k examples/k8s-rbac/argocd-server-applications/ ``` +`argocd-notifications-controller-rbac-clusterrole.yaml` and `argocd-notifications-controller-rbac-clusterrolebinding.yaml` are used to support notifications controller to notify apps in all namespaces. + !!! note At some later point in time, we may make this cluster role part of the default installation manifests. @@ -130,7 +133,7 @@ For backwards compatibility, if the namespace of the Application is the control ### Application RBAC -The RBAC syntax for Application objects has been changed from `/` to `//` to accomodate the need to restrict access based on the source namespace of the Application to be managed. +The RBAC syntax for Application objects has been changed from `/` to `//` to accommodate the need to restrict access based on the source namespace of the Application to be managed. For backwards compatibility, Applications in the `argocd` namespace can still be refered to as `/` in the RBAC policy rules. diff --git a/docs/operator-manual/application.yaml b/docs/operator-manual/application.yaml index c693a581a45ec..864a293ce6890 100644 --- a/docs/operator-manual/application.yaml +++ b/docs/operator-manual/application.yaml @@ -6,7 +6,10 @@ metadata: namespace: argocd # Add this finalizer ONLY if you want these to cascade delete. finalizers: + # The default behaviour is foreground cascading deletion - resources-finalizer.argocd.argoproj.io + # Alternatively, you can use background cascading deletion + # - resources-finalizer.argocd.argoproj.io/background # Add labels to your application object. labels: name: guestbook @@ -48,7 +51,7 @@ spec: # Ignore locally missing valueFiles when installing Helm chart. Defaults to false ignoreMissingValueFiles: false - # Values file as block file + # Values file as block file. Prefer to use valuesObject if possible (see below) values: | ingress: enabled: true @@ -64,6 +67,22 @@ spec: hosts: - mydomain.example.com + # Values file as block file. This takes precedence over values + valuesObject: + ingress: + enabled: true + path: / + hosts: + - mydomain.example.com + annotations: + kubernetes.io/ingress.class: nginx + kubernetes.io/tls-acme: "true" + labels: {} + tls: + - secretName: mydomain-tls + hosts: + - mydomain.example.com + # Skip custom resource definition installation if chart contains custom resource definitions. Defaults to false skipCrds: false @@ -81,10 +100,16 @@ spec: commonLabels: foo: bar commonAnnotations: - beep: boop + beep: boop-${ARGOCD_APP_REVISION} + # Toggle which enables/disables env variables substitution in commonAnnotations + commonAnnotationsEnvsubst: true images: - gcr.io/heptio-images/ks-guestbook-demo:0.2 - my-app=gcr.io/my-repo/my-app:0.1 + namespace: custom-namespace + replicas: + - name: kustomize-guestbook-ui + count: 4 # directory directory: @@ -94,7 +119,7 @@ spec: extVars: - name: foo value: bar - # You can use "code to determine if the value is either string (false, the default) or Jsonnet code (if code is true). + # You can use "code" to determine if the value is either string (false, the default) or Jsonnet code (if code is true). - code: true name: baz value: "true" @@ -114,9 +139,7 @@ spec: # plugin specific config plugin: - # NOTE: this field is deprecated in v2.5 and must be removed to use sidecar-based plugins. - # Only set the plugin name if the plugin is defined in argocd-cm. - # If the plugin is defined as a sidecar, omit the name. The plugin will be automatically matched with the + # If the plugin is defined as a sidecar and name is not passed, the plugin will be automatically matched with the # Application according to the plugin's discovery rules. name: mypluginname # environment variables passed to the plugin @@ -142,10 +165,18 @@ spec: # Destination cluster and namespace to deploy the application destination: + # cluster API URL server: https://kubernetes.default.svc + # or cluster name + # name: in-cluster # The namespace will only be set for namespace-scoped resources that have not set a value for .metadata.namespace namespace: guestbook - + + # Extra information to show in the Argo CD Application details tab + info: + - name: 'Example:' + value: 'https://example.com' + # Sync policy syncPolicy: automated: # automated sync by default retries failed attempts 5 times with following delays between attempts ( 5s, 10s, 20s, 40s, 80s ); retry controlled using `retry` field. @@ -157,6 +188,8 @@ spec: - CreateNamespace=true # Namespace Auto-Creation ensures that namespace specified as the application destination exists in the destination cluster. - PrunePropagationPolicy=foreground # Supported policies are background, foreground and orphan. - PruneLast=true # Allow the ability for resource pruning to happen as a final, implicit wave of a sync operation + - RespectIgnoreDifferences=true # When syncing changes, respect fields ignored by the ignoreDifferences configuration + - ApplyOutOfSyncOnly=true # Only sync out-of-sync resources, rather than applying every object in the application managedNamespaceMetadata: # Sets the metadata for the application namespace. Only valid if CreateNamespace=true (see above), otherwise it's a no-op. labels: # The labels to set on the application namespace any: label @@ -175,18 +208,24 @@ spec: maxDuration: 3m # the maximum amount of time allowed for the backoff strategy # Will ignore differences between live and desired states during the diff. Note that these configurations are not - # used during the sync process. + # used during the sync process unless the `RespectIgnoreDifferences=true` sync option is enabled. ignoreDifferences: # for the specified json pointers - group: apps kind: Deployment jsonPointers: - /spec/replicas + - kind: ConfigMap + jqPathExpressions: + - '.data["config.yaml"].auth' # for the specified managedFields managers - group: "*" kind: "*" managedFieldsManagers: - kube-controller-manager + # Name and namespace are optional. If specified, they must match exactly, these are not glob patterns. + name: my-deployment + namespace: my-namespace # RevisionHistoryLimit limits the number of items kept in the application's revision history, which is used for # informational purposes as well as for rollbacks to previous versions. This should only be changed in exceptional diff --git a/docs/operator-manual/applicationset.yaml b/docs/operator-manual/applicationset.yaml index 2267343a7c489..88264493e248d 100644 --- a/docs/operator-manual/applicationset.yaml +++ b/docs/operator-manual/applicationset.yaml @@ -3,23 +3,312 @@ kind: ApplicationSet metadata: name: test-hello-world-appset namespace: argocd + # To preserve this annotation and label we can use the preservedFields property + preservedFields: + # This annotation and label exists only on this Application, and not in + # the parent ApplicationSet template: + # ignoreApplicationDifferences is the preferred way to accomplish this now. + annotations: + my-custom-annotation: some-value + labels: + my-custom-label: some-value + spec: - # See docs for available generators and their specs. generators: - - list: - elements: - - cluster: https://kubernetes.default.svc + + # Using a generator plugin without combining it with Matrix or Merge + # Plugins allow you to provide your own generator + - plugin: + # Specify the configMap where the plugin configuration is located. + configMapRef: + name: my-plugin + # You can pass arbitrary parameters to the plugin. `input.parameters` is a map, but values may be any type. + # These parameters will also be available on the generator's output under the `generator.input.parameters` key. + input: + parameters: + key1: "value1" + key2: "value2" + list: ["list", "of", "values"] + boolean: true + map: + key1: "value1" + key2: "value2" + key3: "value3" + # You can also attach arbitrary values to the generator's output under the `values` key. These values will be + # available in templates under the `values` key. + values: + value1: something + # When using a Plugin generator, the ApplicationSet controller polls every `requeueAfterSeconds` interval (defaulting to every 30 minutes) to detect changes. + requeueAfterSeconds: 30 + + # to automatically discover repositories within an organization + - scmProvider: + # Which protocol to clone using. + cloneProtocol: ssh + # The GitHub mode uses the GitHub API to scan an organization in either github.com or GitHub Enterprise + github: + # The GitHub organization to scan. + organization: myorg + # For GitHub Enterprise: + api: https://git.example.com/ + # If true, scan every branch of every repository. If false, scan only the default branch. Defaults to false. + allBranches: true + # Reference to a Secret containing an access token. (optional) + tokenRef: + secretName: github-token + key: token + # (optional) use a GitHub App to access the API instead of a PAT. + appSecretName: gh-app-repo-creds + #Pass additional key-value pairs via values field + values: + name: "{{organization}}-{{repository}}" + + #The GitLab mode uses the GitLab API to scan and organization in either gitlab.com or self-hosted GitLab. + gitlab: + #The Gitea mode uses the Gitea API to scan organizations in your instance + gitea: + #Use the Bitbucket Server API (1.0) to scan repos in a project. + bitbucketServer: + #Uses the Azure DevOps API to look up eligible repositories + azureDevOps: + # The Bitbucket mode uses the Bitbucket API V2 to scan a workspace in bitbucket.org + bitbucket: + #Uses AWS ResourceGroupsTagging and AWS CodeCommit APIs to scan repos across AWS accounts and regionsz + awsCodeCommit: + + #Filters allow selecting which repositories to generate for. + filters: + # Include any repository starting with "myapp" AND including a Kustomize config AND labeled with "deploy-ok" ... + - repositoryMatch: ^myapp + pathsExist: [kubernetes/kustomization.yaml] + labelMatch: deploy-ok + # ... OR include any repository starting with "otherapp" AND a Helm folder and doesn't have file disabledrepo.txt. + - repositoryMatch: ^otherapp + pathsExist: [helm] + pathsDoNotExist: [disabledrepo.txt] + # matrix 'parent' generator + - matrix: + generators: + # any of the top-level generators may be used here instead. + + # merge 'parent' generator + # Use the selector set by both child generators to combine them. + - merge: + mergeKeys: + - server + # Note that this would not work with goTemplate enabled, + # nested merge keys are not supported there. + - values.selector + generators: + - clusters: + values: + kafka: 'true' + redis: 'false' + # For clusters with a specific label, enable Kafka. + - clusters: + selector: + matchLabels: + use-kafka: 'false' + values: + kafka: 'false' + # For a specific cluster, enable Redis. + - list: + elements: + - server: https://2.4.6.8 + values.redis: 'true' + + # Determines whether go templating will be used in the `template` field below. - goTemplate: false + goTemplate: true + # Optional list of go templating options, see https://pkg.go.dev/text/template#Template.Option + # This is only relevant if `goTemplate` is true + goTemplateOptions: ["missingkey=error"] + # These fields are identical to the Application spec. + # The generator's template field takes precedence over the spec's template fields template: metadata: name: test-hello-world-app spec: project: my-project + syncPolicy: + automated: + selfHeal: true + syncOptions: + - CreateNamespace=true + # defines from which Git repository to extract the desired Application manifests + source: + - chart: '{{.chart}}' + # developers may customize app details using JSON files from above repo URL + repoURL: https://github.com/argoproj/argo-cd.git + targetRevision: HEAD + # Path within the repository where Kubernetes manifests are located + path: applicationset/examples/list-generator/guestbook/{{cluster}} + helm: + useCredentials: "{{.useCredentials}}" # This field may NOT be templated, because it is a boolean field + parameters: + - name: "image.tag" + value: "pull-{{head_sha}}" + - name: "{{.name}}" + value: "{{.value}}" + - name: throw-away + value: "{{end}}" + destination: + # Only one of name or server may be specified: if both are specified, an error is returned. + # Name of the cluster (within Argo CD) to deploy to + name: production-cluster # cluster is restricted + # API Server URL for the cluster + server: '{{.url}}' + # Target namespace in which to deploy the manifests from source + namespace: dev-team-one # namespace is restricted + # This sync policy pertains to the ApplicationSet, not to the Applications it creates. syncPolicy: - # Determines whether the controller will delete Applications when an ApplicationSet is deleted. - preserveResourcesOnDeletion: false - # Alpha feature to determine the order in which ApplicationSet applies changes. + # Prevents ApplicationSet controller from modifying or deleting Applications + applicationsSync: create-only + + # Prevents ApplicationSet controller from deleting Applications. Update is allowed + # applicationsSync: create-update + + # Prevents ApplicationSet controller from modifying Applications. Delete is allowed. + # applicationsSync: create-delete + + syncOptions: + - CreateNamespace=true + # Prevent an Application's child resources from being deleted, when the parent Application is deleted + preserveResourcesOnDeletion: true + + # which fields of the ApplicationSet should be ignored when comparing Applications. + ignoreApplicationDifferences: + - jsonPointers: + - /spec/source/targetRevision + - name: some-app + jqExpressions: + - .spec.source.helm.values + strategy: + # This field lets you define fields which should be ignored when applying Application resources. This is helpful if you + # want to use ApplicationSets to create apps, but also want to allow users to modify those apps without having their + # changes overwritten by the ApplicationSet. + # This update strategy allows you to group Applications by labels present on the generated Application resources + type: RollingSync + rollingSync: + steps: + # Application groups are selected using their labels and matchExpressions + - matchExpressions: + - key: envLabel + operator: In + values: + - env-dev + # maxUpdate: 100% # if undefined, all applications matched are updated together (default is 100%) + - matchExpressions: + - key: envLabel + operator: In + values: + - env-qa + maxUpdate: 0 # if 0, no matched applications will be synced unless they're synced manually + - matchExpressions: + - key: envLabel + operator: In + values: + - env-prod + maxUpdate: 10% # maxUpdate supports both integer and percentage string values (rounds down, but floored at 1 Application for >0%) + + ignoreApplicationDifferences: + - jsonPointers: + - /spec/source/targetRevision + - name: some-app + jqPathExpressions: + - .spec.source.helm.values + + # Cluster-decision-resource-based ApplicationSet generator + - clusterDecisionResource: + # ConfigMap with GVK information for the duck type resource + configMapRef: my-configmap + name: quak # Choose either "name" of the resource or "labelSelector" + labelSelector: + matchLabels: # OPTIONAL + duck: spotted + matchExpressions: # OPTIONAL + - key: duck + operator: In + values: + - "spotted" + - "canvasback" + # OPTIONAL: Checks for changes every 60sec (default 3min) + requeueAfterSeconds: 60 + + # The Pull Request generator uses the API of an SCMaaS provider to automatically discover open pull requests within a repository + - pullRequest: + # When using a Pull Request generator, the ApplicationSet controller polls every `requeueAfterSeconds` interval (defaulting to every 30 minutes) to detect changes. + requeueAfterSeconds: 1800 + # See below for provider specific options. + # Specify the repository from which to fetch the GitHub Pull requests. + github: + # The GitHub organization or user. + owner: myorg + # The Github repository + repo: myrepository + # For GitHub Enterprise (optional) + api: https://git.example.com/ + # Reference to a Secret containing an access token. (optional) + tokenRef: + secretName: github-token + key: token + # (optional) use a GitHub App to access the API instead of a PAT. + appSecretName: github-app-repo-creds + # Labels is used to filter the PRs that you want to target. (optional) + labels: + - preview + + # Filters allow selecting which pull requests to generate for + # Include any pull request ending with "argocd". (optional) + filters: + - branchMatch: ".*-argocd" + + # Specify the project from which to fetch the GitLab merge requests. + gitlab: + # Specify the repository from which to fetch the Gitea Pull requests. + gitea: + # Fetch pull requests from a repo hosted on a Bitbucket Server (not the same as Bitbucket Cloud). + bitbucketServer: + # Fetch pull requests from a repo hosted on a Bitbucket Cloud. + bitbucket: + # Specify the organization, project and repository from which you want to fetch pull requests. + azuredevops: + # Fetch pull requests from AWS CodeCommit repositories. + awsCodeCommit: + +# The list generator generates a set of two application which then filter by the key value to only select the env with value staging + - list: + elements: + - cluster: engineering-dev + url: https://kubernetes.default.svc + env: staging + - cluster: engineering-prod + url: https://kubernetes.default.svc + env: prod + # The generator's template field takes precedence over the spec's template fields + template: + metadata: {} + spec: + project: "default" + source: + revision: HEAD + repoURL: https://github.com/argoproj/argo-cd.git + # New path value is generated here: + path: 'applicationset/examples/template-override/{{cluster}}-override' + destination: {} + + selector: + matchLabels: + env: staging + # It is also possible to use matchExpressions for more powerful selectors + - clusters: {} + selector: + matchExpressions: + - key: server + operator: In + values: + - https://kubernetes.default.svc + - https://some-other-cluster \ No newline at end of file diff --git a/docs/operator-manual/applicationset/Appset-Any-Namespace.md b/docs/operator-manual/applicationset/Appset-Any-Namespace.md new file mode 100644 index 0000000000000..4e28bc3a8172d --- /dev/null +++ b/docs/operator-manual/applicationset/Appset-Any-Namespace.md @@ -0,0 +1,231 @@ +# ApplicationSet in any namespace + +**Current feature state**: Beta + +!!! warning + Please read this documentation carefully before you enable this feature. Misconfiguration could lead to potential security issues. + +## Introduction + +As of version 2.8, Argo CD supports managing `ApplicationSet` resources in namespaces other than the control plane's namespace (which is usually `argocd`), but this feature has to be explicitly enabled and configured appropriately. + +Argo CD administrators can define a certain set of namespaces where `ApplicationSet` resources may be created, updated and reconciled in. + +As Applications generated by an ApplicationSet are generated in the same namespace as the ApplicationSet itself, this works in combination with [App in any namespace](../app-any-namespace.md). + +## Prerequisites + +### App in any namespace configured + +This feature needs [App in any namespace](../app-any-namespace.md) feature activated. The list of namespaces must be the same. + +### Cluster-scoped Argo CD installation + +This feature can only be enabled and used when your Argo CD ApplicationSet controller is installed as a cluster-wide instance, so it has permissions to list and manipulate resources on a cluster scope. It will *not* work with an Argo CD installed in namespace-scoped mode. + +### SCM Providers secrets consideration + +By allowing ApplicationSet in any namespace you must be aware that any secrets can be exfiltrated using `scmProvider` or `pullRequest` generators. + +Here is an example: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: myapps +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + - scmProvider: + gitea: + # The Gitea owner to scan. + owner: myorg + # With this malicious setting, user can send all request to a Pod that will log incoming requests including headers with tokens + api: http://my-service.my-namespace.svc.cluster.local + # If true, scan every branch of every repository. If false, scan only the default branch. Defaults to false. + allBranches: true + # By changing this token reference, user can exfiltrate any secrets + tokenRef: + secretName: gitea-token + key: token + template: +``` + +Therefore administrator must restrict the urls of the allowed SCM Providers (example: `https://git.mydomain.com/,https://gitlab.mydomain.com/`) by setting the environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS` to argocd-cmd-params-cm `applicationsetcontroller.allowed.scm.providers`. If another url is used, it will be rejected by the applicationset controller. + +For example: +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cmd-params-cm +data: + applicationsetcontroller.allowed.scm.providers: https://git.mydomain.com/,https://gitlab.mydomain.com/ +``` + +!!! note + Please note url used in the `api` field of the `ApplicationSet` must match the url declared by the Administrator including the protocol + +!!! warning + The allow-list only applies to SCM providers for which the user may configure a custom `api`. Where an SCM or PR + generator does not accept a custom API URL, the provider is implicitly allowed. + +If you do not intend to allow users to use the SCM or PR generators, you can disable them entirely by setting the environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS` to argocd-cmd-params-cm `applicationsetcontroller.enable.scm.providers` to `false`. + +### Overview + +In order for an ApplicationSet to be managed and reconciled outside the Argo CD's control plane namespace, two prerequisites must match: + +1. The namespace list from which `argocd-applicationset-controller` can source `ApplicationSets` must be explicitly set using environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES` or alternatively using parameter `--applicationset-namespaces`. +2. The enabled namespaces must be entirely covered by the [App in any namespace](../app-any-namespace.md), otherwise the generated Applications generated outside the allowed Application namespaces won't be reconciled + +It can be achieved by setting the environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES` to argocd-cmd-params-cm `applicationsetcontroller.namespaces` + +`ApplicationSets` in different namespaces can be created and managed just like any other `ApplicationSet` in the `argocd` namespace previously, either declaratively or through the Argo CD API (e.g. using the CLI, the web UI, the REST API, etc). + +### Reconfigure Argo CD to allow certain namespaces + +#### Change workload startup parameters + +In order to enable this feature, the Argo CD administrator must reconfigure the and `argocd-applicationset-controller` workloads to add the `--applicationset-namespaces` parameter to the container's startup command. + +### Safely template project + +As [App in any namespace](../app-any-namespace.md) is a prerequisite, it is possible to safely template project. + +Let's take an example with two teams and an infra project: + +```yaml +kind: AppProject +apiVersion: argoproj.io/v1alpha1 +metadata: + name: infra-project + namespace: argocd +spec: + destinations: + - namespace: '*' +``` + +```yaml +kind: AppProject +apiVersion: argoproj.io/v1alpha1 +metadata: + name: team-one-project + namespace: argocd +spec: + sourceNamespaces: + - team-one-cd +``` + +```yaml +kind: AppProject +apiVersion: argoproj.io/v1alpha1 +metadata: + name: team-two-project + namespace: argocd +spec: + sourceNamespaces: + - team-two-cd +``` + +Creating following `ApplicationSet` generates two Applications `infra-escalation` and `team-two-escalation`. Both will be rejected as they are outside `argocd` namespace, therefore `sourceNamespaces` will be checked + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: team-one-product-one + namespace: team-one-cd +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + list: + - name: infra + project: infra-project + - name: team-two + project: team-two-project + template: + metadata: + name: '{{.name}}-escalation' + spec: + project: "{{.project}}" +``` + +### ApplicationSet names + +For the CLI, applicationSets are now referred to and displayed as in the format `/`. + +For backwards compatibility, if the namespace of the ApplicationSet is the control plane's namespace (i.e. `argocd`), the `` can be omitted from the applicationset name when referring to it. For example, the application names `argocd/someappset` and `someappset` are semantically the same and refer to the same application in the CLI and the UI. + +### Applicationsets RBAC + +The RBAC syntax for Application objects has been changed from `/` to `//` to accommodate the need to restrict access based on the source namespace of the Application to be managed. + +For backwards compatibility, Applications in the argocd namespace can still be referred to as `/` in the RBAC policy rules. + +Wildcards do not make any distinction between project and applicationset namespaces yet. For example, the following RBAC rule would match any application belonging to project foo, regardless of the namespace it is created in: + + +``` +p, somerole, applicationsets, get, foo/*, allow +``` + +If you want to restrict access to be granted only to `ApplicationSets` with project `foo` within namespace `bar`, the rule would need to be adapted as follows: + +``` +p, somerole, applicationsets, get, foo/bar/*, allow +``` + +## Managing applicationSets in other namespaces + +### Using the CLI + +You can use all existing Argo CD CLI commands for managing applications in other namespaces, exactly as you would use the CLI to manage applications in the control plane's namespace. + +For example, to retrieve the `ApplicationSet` named `foo` in the namespace `bar`, you can use the following CLI command: + +```shell +argocd appset get foo/bar +``` + +Likewise, to manage this applicationSet, keep referring to it as `foo/bar`: + +```bash +# Delete the application +argocd appset delete foo/bar +``` + +There is no change on the create command as it is using a file. You just need to add the namespace in the `metadata.namespace` field. + +As stated previously, for applicationSets in the Argo CD's control plane namespace, you can omit the namespace from the application name. + +### Using the REST API + +If you are using the REST API, the namespace for `ApplicationSet` cannot be specified as the application name, and resources need to be specified using the optional `appNamespace` query parameter. For example, to work with the `ApplicationSet` resource named `foo` in the namespace `bar`, the request would look like follows: + +```bash +GET /api/v1/applicationsets/foo?appsetNamespace=bar +``` + +For other operations such as `POST` and `PUT`, the `appNamespace` parameter must be part of the request's payload. + +For `ApplicationSet` resources in the control plane namespace, this parameter can be omitted. + +## Clusters secrets consideration + +By allowing ApplicationSet in any namespace you must be aware that clusters can be discovered and used. + +Example: + +Following will discover all clusters + +```yaml +spec: + generators: + - clusters: {} # Automatically use all clusters defined within Argo CD +``` + +If you don't want to allow users to discover all clusters with ApplicationSets from other namespaces you may consider deploying ArgoCD in namespace scope or use OPA rules. \ No newline at end of file diff --git a/docs/operator-manual/applicationset/Controlling-Resource-Modification.md b/docs/operator-manual/applicationset/Controlling-Resource-Modification.md index f0bf6d37693ba..d72cee60ad401 100644 --- a/docs/operator-manual/applicationset/Controlling-Resource-Modification.md +++ b/docs/operator-manual/applicationset/Controlling-Resource-Modification.md @@ -6,7 +6,7 @@ These settings allow you to exert control over when, and how, changes are made t Here are some of the controller settings that may be modified to alter the ApplicationSet controller's resource-handling behaviour. -### Dry run: prevent ApplicationSet from creating, modifying, or deleting all Applications +## Dry run: prevent ApplicationSet from creating, modifying, or deleting all Applications To prevent the ApplicationSet controller from creating, modifying, or deleting any `Application` resources, you may enable `dry-run` mode. This essentially switches the controller into a "read only" mode, where the controller Reconcile loop will run, but no resources will be modified. @@ -14,18 +14,50 @@ To enable dry-run, add `--dryrun true` to the ApplicationSet Deployment's contai See 'How to modify ApplicationSet container parameters' below for detailed steps on how to add this parameter to the controller. -### Policy - `create-only`: Prevent ApplicationSet controller from modifying or deleting Applications +## Managed Applications modification Policies The ApplicationSet controller supports a parameter `--policy`, which is specified on launch (within the controller Deployment container), and which restricts what types of modifications will be made to managed Argo CD `Application` resources. -The `--policy` parameter takes one of the following valid values: `sync`, `create-only`, `create-update`, and `create-delete`. (`sync` is the default, which is used if the `--policy` parameter is not specified; the other policies are described below). +The `--policy` parameter takes four values: `sync`, `create-only`, `create-delete`, and `create-update`. (`sync` is the default, which is used if the `--policy` parameter is not specified; the other policies are described below). + +It is also possible to set this policy per ApplicationSet. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +spec: + # (...) + syncPolicy: + applicationsSync: create-only # create-update, create-delete sync + +``` + +- Policy `create-only`: Prevents ApplicationSet controller from modifying or deleting Applications. Prevents Application controller from deleting Applications according to [ownerReferences](https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/). +- Policy `create-update`: Prevents ApplicationSet controller from deleting Applications. Update is allowed. Prevents Application controller from deleting Applications according to [ownerReferences](https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/). +- Policy `create-delete`: Prevents ApplicationSet controller from modifying Applications. Delete is allowed. +- Policy `sync`: Update and Delete are allowed. + +If the controller parameter `--policy` is set, it takes precedence on the field `applicationsSync`. It is possible to allow per ApplicationSet sync policy by setting variable `ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_POLICY_OVERRIDE` to argocd-cmd-params-cm `applicationsetcontroller.enable.policy.override` or directly with controller parameter `--enable-policy-override` (default to `false`). + +### Controller parameter To allow the ApplicationSet controller to *create* `Application` resources, but prevent any further modification, such as deletion, or modification of Application fields, add this parameter in the ApplicationSet controller: ``` --policy create-only ``` -### Policy - `create-update`: Prevent ApplicationSet controller from deleting Applications +At ApplicationSet level + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +spec: + # (...) + syncPolicy: + applicationsSync: create-only +``` + +## Policy - `create-update`: Prevent ApplicationSet controller from deleting Applications To allow the ApplicationSet controller to create or modify `Application` resources, but prevent Applications from being deleted, add the following parameter to the ApplicationSet controller `Deployment`: ``` @@ -34,14 +66,124 @@ To allow the ApplicationSet controller to create or modify `Application` resourc This may be useful to users looking for additional protection against deletion of the Applications generated by the controller. -### Policy - `create-delete`: Prevent ApplicationSet controller from updating Applications +At ApplicationSet level + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +spec: + # (...) + syncPolicy: + applicationsSync: create-update +``` + +## Ignore certain changes to Applications + +The ApplicationSet spec includes an `ignoreApplicationDifferences` field, which allows you to specify which fields of +the ApplicationSet should be ignored when comparing Applications. + +The field supports multiple ignore rules. Each ignore rule may specify a list of either `jsonPointers` or +`jqPathExpressions` to ignore. + +You may optionally also specify a `name` to apply the ignore rule to a specific Application, or omit the `name` to apply +the ignore rule to all Applications. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +spec: + ignoreApplicationDifferences: + - jsonPointers: + - /spec/source/targetRevision + - name: some-app + jqPathExpressions: + - .spec.source.helm.values +``` + +### Allow temporarily toggling auto-sync + +One of the most common use cases for ignoring differences is to allow temporarily toggling auto-sync for an Application. + +For example, if you have an ApplicationSet that is configured to automatically sync Applications, you may want to temporarily +disable auto-sync for a specific Application. You can do this by adding an ignore rule for the `spec.syncPolicy.automated` field. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +spec: + ignoreApplicationDifferences: + - jsonPointers: + - /spec/syncPolicy +``` + +### Limitations of `ignoreApplicationDifferences` + +When an ApplicationSet is reconciled, the controller will compare the ApplicationSet spec with the spec of each Application +that it manages. If there are any differences, the controller will generate a patch to update the Application to match the +ApplicationSet spec. + +The generated patch is a MergePatch. According to the MergePatch documentation, "existing lists will be completely +replaced by new lists" when there is a change to the list. + +This limits the effectiveness of `ignoreApplicationDifferences` when the ignored field is in a list. For example, if you +have an application with multiple sources, and you want to ignore changes to the `targetRevision` of one of the sources, +changes in other fields or in other sources will cause the entire `sources` list to be replaced, and the `targetRevision` +field will be reset to the value defined in the ApplicationSet. -To allow the ApplicationSet controller to create or delete `Application` resources, but prevent Applications from being updated, add the following parameter to the ApplicationSet controller `Deployment`: +For example, consider this ApplicationSet: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +spec: + ignoreApplicationDifferences: + - jqPathExpressions: + - .spec.sources[] | select(.repoURL == "https://git.example.com/org/repo1").targetRevision + template: + spec: + sources: + - repoURL: https://git.example.com/org/repo1 + targetRevision: main + - repoURL: https://git.example.com/org/repo2 + targetRevision: main ``` ---policy create-delete + +You can freely change the `targetRevision` of the `repo1` source, and the ApplicationSet controller will not overwrite +your change. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +spec: + sources: + - repoURL: https://git.example.com/org/repo1 + targetRevision: fix/bug-123 + - repoURL: https://git.example.com/org/repo2 + targetRevision: main ``` -### Prevent an `Application`'s child resources from being deleted, when the parent Application is deleted +However, if you change the `targetRevision` of the `repo2` source, the ApplicationSet controller will overwrite the entire +`sources` field. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +spec: + sources: + - repoURL: https://git.example.com/org/repo1 + targetRevision: main + - repoURL: https://git.example.com/org/repo2 + targetRevision: main +``` + +!!! note + [Future improvements](https://github.com/argoproj/argo-cd/issues/15975) to the ApplicationSet controller may + eliminate this problem. For example, the `ref` field might be made a merge key, allowing the ApplicationSet + controller to generate and use a StrategicMergePatch instead of a MergePatch. You could then target a specific + source by `ref`, ignore changes to a field in that source, and changes to other sources would not cause the ignored + field to be overwritten. + +## Prevent an `Application`'s child resources from being deleted, when the parent Application is deleted By default, when an `Application` resource is deleted by the ApplicationSet controller, all of the child resources of the Application will be deleted as well (such as, all of the Application's `Deployments`, `Services`, etc). @@ -57,7 +199,8 @@ spec: More information on the specific behaviour of `preserveResourcesOnDeletion`, and deletion in ApplicationSet controller and Argo CD in general, can be found on the [Application Deletion](Application-Deletion.md) page. -### Prevent an Application's child resources from being modified + +## Prevent an Application's child resources from being modified Changes made to the ApplicationSet will propagate to the Applications managed by the ApplicationSet, and then Argo CD will propagate the Application changes to the underlying cluster resources (as per [Argo CD Integration](Argo-CD-Integration.md)). @@ -121,52 +264,60 @@ cd applicationset/manifests kubectl apply -n argocd -f install.yaml ``` -## Limitations: what isn't supported as of the current release - -Here is a list of commonly requested resource modification features which are not supported as of the current release. This lack of support is *not* necessarily by design; rather these behaviours are documented here to provide clear, concise descriptions of the current state of the feature. - -### Limitation: Control resource modification on a per ApplicationSet basis - -There is currently no way to restrict modification/deletion of the Applications that are owned by an *individual* ApplicationSet. The global `--policy` parameters described above only allow targeting of *all* ApplicationSets (eg it is 'all or nothing'). - -### Limitation: No support for manual edits to individual Applications - -There is currently no way to allow modification of a single child Application of an ApplicationSet, for example, if you wanted to make manual edits to a single Application for debugging/testing purposes. - -For example: - -- Imagine that you have an ApplicationSet that created Applications `app1`, `app2`, and `app3`. -- You now want to edit `app3` with `kubectl edit application/app3`, to update one of the `app3`'s fields. -- However, as soon as you make edits to `app3` (or any of the individual Applications), they will be immediately reverted by the ApplicationSet reconciler back to the `template`-ized version (by design). - -As of this writing, there is [an issue open](https://github.com/argoproj/applicationset/issues/186) for discussion of this behaviour. +## Preserving changes made to an Applications annotations and labels +!!! note + The same behavior can be achieved on a per-app basis using the [`ignoreApplicationDifferences`](#ignore-certain-changes-to-applications) + feature described above. However, preserved fields may be configured globally, a feature that is not yet available + for `ignoreApplicationDifferences`. -### Limitation: ApplicationSet controller will not selectively ignore changes to individual fields +It is common practice in Kubernetes to store state in annotations, operators will often make use of this. To allow for this, it is possible to configure a list of annotations that the ApplicationSet should preserve when reconciling. -There is currently no way to instruct the ApplicationSet controller to ignore changes to individual fields of Applications. - -For example, imagine that we have an Application created from an ApplicationSet, but a user has attempted to add a custom annotation (to the Application) that does not exist in the `ApplicationSet` resource: +For example, imagine that we have an Application created from an ApplicationSet, but a custom annotation and label has since been added (to the Application) that does not exist in the `ApplicationSet` resource: ```yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: + # This annotation and label exists only on this Application, and not in + # the parent ApplicationSet template: annotations: - # This annotation exists only on this Application, and not in - # the parent ApplicationSet template: my-custom-annotation: some-value + labels: + my-custom-label: some-value spec: # (...) ``` -As above, the `ApplicationSet` resource does not have a `my-custom-annotation: some-value` annotation in the `.spec.template.annotations` for the Application. +To preserve this annotation and label we can use the `preservedFields` property of the `ApplicationSet` like so: +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +spec: + # (...) + preservedFields: + annotations: ["my-custom-annotation"] + labels: ["my-custom-label"] +``` + +The ApplicationSet controller will leave this annotation and label as-is when reconciling, even though it is not defined in the metadata of the ApplicationSet itself. -Since this field is not in the ApplicationSet template, as soon as a user adds this custom annotation, it will be immediately reverted (removed) by the ApplicationSet controller. +By default, the Argo CD notifications and the Argo CD refresh type annotations are also preserved. -There is currently no support for disabling or customizing this behaviour. +!!!note + One can also set global preserved fields for the controller by passing a comma separated list of annotations and labels to + `ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_ANNOTATIONS` and `ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_LABELS` respectively. -To some extent this is by design: the main principle of ApplicationSets is that we maintain a 1-to-many relationship between the ApplicationSet and the Applications that it owns, such that all the Applications necessarily conform to a strict template. +## Debugging unexpected changes to Applications -This provides the advantages of the 'cattle not pets' philosophy of microservice/cloud native application resource management, wherein you don't need to worry about individual Applications differing from each other in subtle ways: they will all necessarily be reconciled to be consistent with the parent template. +When the ApplicationSet controller makes a change to an application, it logs the patch at the debug level. To see these +logs, set the log level to debug in the `argocd-cmd-params-cm` ConfigMap in the `argocd` namespace: -BUT, admittedly, that is not always desirable 100% of the time, and there may be a better balance to be found, so discussions are continuing on GitHub and Slack. +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cmd-params-cm + namespace: argocd +data: + applicationsetcontroller.log.level: debug +``` diff --git a/docs/operator-manual/applicationset/Generators-Cluster-Decision-Resource.md b/docs/operator-manual/applicationset/Generators-Cluster-Decision-Resource.md index 8f5bb491b8b44..95c60d95cd68c 100644 --- a/docs/operator-manual/applicationset/Generators-Cluster-Decision-Resource.md +++ b/docs/operator-manual/applicationset/Generators-Cluster-Decision-Resource.md @@ -1,6 +1,6 @@ # Cluster Decision Resource Generator -The cluster decision resource generates a list of Argo CD clusters. This is done using [duck-typing](https://pkg.go.dev/knative.dev/pkg/apis/duck), which does not require knowledge of the full shape of the referenced kubernetes resource. The following is an example of a cluster-decision-resource-based ApplicationSet generator: +The cluster decision resource generates a list of Argo CD clusters. This is done using [duck-typing](https://pkg.go.dev/knative.dev/pkg/apis/duck), which does not require knowledge of the full shape of the referenced Kubernetes resource. The following is an example of a cluster-decision-resource-based ApplicationSet generator: ```yaml apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet @@ -8,6 +8,8 @@ metadata: name: guestbook namespace: argocd spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - clusterDecisionResource: # ConfigMap with GVK information for the duck type resource @@ -26,7 +28,7 @@ spec: requeueAfterSeconds: 60 template: metadata: - name: '{{name}}-guestbook' + name: '{{.name}}-guestbook' spec: project: "default" source: @@ -34,7 +36,7 @@ spec: targetRevision: HEAD path: guestbook destination: - server: '{{clusterName}}' # 'server' field of the secret + server: '{{.clusterName}}' # 'server' field of the secret namespace: guestbook ``` The `quak` resource, referenced by the ApplicationSet `clusterDecisionResource` generator: diff --git a/docs/operator-manual/applicationset/Generators-Cluster.md b/docs/operator-manual/applicationset/Generators-Cluster.md index 26a457e5de408..aa18983fe3d54 100644 --- a/docs/operator-manual/applicationset/Generators-Cluster.md +++ b/docs/operator-manual/applicationset/Generators-Cluster.md @@ -39,11 +39,13 @@ metadata: name: guestbook namespace: argocd spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - clusters: {} # Automatically use all clusters defined within Argo CD template: metadata: - name: '{{name}}-guestbook' # 'name' field of the Secret + name: '{{.name}}-guestbook' # 'name' field of the Secret spec: project: "my-project" source: @@ -51,7 +53,7 @@ spec: targetRevision: HEAD path: guestbook destination: - server: '{{server}}' # 'server' field of the secret + server: '{{.server}}' # 'server' field of the secret namespace: guestbook ``` (*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/cluster).*) @@ -67,11 +69,19 @@ metadata: name: guestbook namespace: argocd spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - clusters: selector: matchLabels: staging: true + # The cluster generator also supports matchExpressions. + #matchExpressions: + # - key: staging + # operator: In + # values: + # - "true" template: # (...) ``` @@ -99,11 +109,19 @@ The cluster generator will automatically target both local and non-local cluster If you wish to target only remote clusters with your Applications (e.g. you want to exclude the local cluster), then use a cluster selector with labels, for example: ```yaml spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - clusters: selector: matchLabels: argocd.argoproj.io/secret-type: cluster + # The cluster generator also supports matchExpressions. + #matchExpressions: + # - key: staging + # operator: In + # values: + # - "true" ``` This selector will not match the default local cluster, since the default local cluster does not have a Secret (and thus does not have the `argocd.argoproj.io/secret-type` label on that secret). Any cluster selector that selects on that label will automatically exclude the default local cluster. @@ -118,6 +136,29 @@ However, if you do wish to target both local and non-local clusters, while also These steps might seem counterintuitive, but the act of changing one of the default values for the local cluster causes the Argo CD Web UI to create a new secret for this cluster. In the Argo CD namespace, you should now see a Secret resource named `cluster-(cluster suffix)` with label `argocd.argoproj.io/secret-type": "cluster"`. You may also create a local [cluster secret declaratively](../../declarative-setup/#clusters), or with the CLI using `argocd cluster add "(context name)" --in-cluster`, rather than through the Web UI. +### Fetch clusters based on their K8s version + +There is also the possibility to fetch clusters based upon their Kubernetes version. To do this, the label `argocd.argoproj.io/auto-label-cluster-info` needs to be set to `true` on the cluster secret. +Once that has been set, the controller will dynamically label the cluster secret with the Kubernetes version it is running on. To retrieve that value, you need to use the +`argocd.argoproj.io/kubernetes-version`, as the example below demonstrates: + +```yaml +spec: + goTemplate: true + generators: + - clusters: + selector: + matchLabels: + argocd.argoproj.io/kubernetes-version: 1.28 + # matchExpressions are also supported. + #matchExpressions: + # - key: argocd.argoproj.io/kubernetes-version + # operator: In + # values: + # - "1.27" + # - "1.28" +``` + ### Pass additional key-value pairs via `values` field You may pass additional, arbitrary string key-value pairs via the `values` field of the cluster generator. Values added via the `values` field are added as `values.(field)` @@ -125,6 +166,8 @@ You may pass additional, arbitrary string key-value pairs via the `values` field In this example, a `revision` parameter value is passed, based on matching labels on the cluster secret: ```yaml spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - clusters: selector: @@ -142,16 +185,16 @@ spec: revision: stable template: metadata: - name: '{{name}}-guestbook' + name: '{{.name}}-guestbook' spec: project: "my-project" source: repoURL: https://github.com/argoproj/argocd-example-apps/ # The cluster values field for each generator will be substituted here: - targetRevision: '{{values.revision}}' + targetRevision: '{{.values.revision}}' path: guestbook destination: - server: '{{server}}' + server: '{{.server}}' namespace: guestbook ``` @@ -172,6 +215,8 @@ Extending the example above, we could do something like this: ```yaml spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - clusters: selector: @@ -180,8 +225,8 @@ spec: # A key-value map for arbitrary parameters values: # If `my-custom-annotation` is in your cluster secret, `revision` will be substituted with it. - revision: '{{metadata.annotations.my-custom-annotation}}' - clusterName: '{{name}}' + revision: '{{index .metadata.annotations "my-custom-annotation"}}' + clusterName: '{{.name}}' - clusters: selector: matchLabels: @@ -189,19 +234,19 @@ spec: values: # production uses a different revision value, for 'stable' branch revision: stable - clusterName: '{{name}}' + clusterName: '{{.name}}' template: metadata: - name: '{{name}}-guestbook' + name: '{{.name}}-guestbook' spec: project: "my-project" source: repoURL: https://github.com/argoproj/argocd-example-apps/ # The cluster values field for each generator will be substituted here: - targetRevision: '{{values.revision}}' + targetRevision: '{{.values.revision}}' path: guestbook destination: # In this case this is equivalent to just using {{name}} - server: '{{values.clusterName}}' + server: '{{.values.clusterName}}' namespace: guestbook ``` diff --git a/docs/operator-manual/applicationset/Generators-Git-File-Globbing.md b/docs/operator-manual/applicationset/Generators-Git-File-Globbing.md new file mode 100644 index 0000000000000..4f8967b5937fa --- /dev/null +++ b/docs/operator-manual/applicationset/Generators-Git-File-Globbing.md @@ -0,0 +1,85 @@ +# Git File Generator Globbing + +## Problem Statement + +The original and default implementation of the Git file generator does very greedy globbing. This can trigger errors or catch users off-guard. For example, consider the following repository layout: + +``` +└── cluster-charts/ + ├── cluster1 + │ ├── mychart/ + │ │  ├── charts/ + │ │   │   └── mysubchart/ + │ │ │ ├── values.yaml + │ │   │   └── etc… + │ │   ├── values.yaml + │ │ └── etc… + │ └── myotherchart/ + │ ├── values.yaml + │ └── etc… + └── cluster2 + └── etc… +``` + +In `cluster1` we have two charts, one of them with a subchart. + +Assuming we need the ApplicationSet to template values in the `values.yaml`, then we need to use a Git file generator instead of a directory generator. The value of the `path` key of the Git file generator should be set to: + +``` +path: cluster-charts/*/*/values.yaml +``` + +However, the default implementation will interpret the above pattern as: + +``` +path: cluster-charts/**/values.yaml +``` + +Meaning, for `mychart` in `cluster1`, that it will pick up both the chart's `values.yaml` but also the one from its subchart. This will most likely fail, and even if it didn't it would be wrong. + +There are multiple other ways this undesirable globbing can fail. For example: + +``` +path: some-path/*.yaml +``` + +This will return all YAML files in any directory at any level under `some-path`, instead of only those directly under it. + +## Enabling the New Globbing + +Since some users may rely on the old behavior it was decided to make the fix optional and not enabled by default. + +It can be enabled in any of these ways: + +1. Pass `--enable-new-git-file-globbing` to the ApplicationSet controller args. +1. Set `ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING=true` in the ApplicationSet controller environment variables. +1. Set `applicationsetcontroller.enable.new.git.file.globbing: true` in the Argo CD ConfigMap. + +Note that the default may change in the future. + +## Usage + +The new Git file generator globbing uses the `doublestar` package. You can find it [here](https://github.com/bmatcuk/doublestar). + +Below is a short excerpt from its documentation. + +doublestar patterns match files and directories recursively. For example, if +you had the following directory structure: + +```bash +grandparent +`-- parent + |-- child1 + `-- child2 +``` + +You could find the children with patterns such as: `**/child*`, +`grandparent/**/child?`, `**/parent/*`, or even just `**` by itself (which will +return all files and directories recursively). + +Bash's globstar is doublestar's inspiration and, as such, works similarly. +Note that the doublestar must appear as a path component by itself. A pattern +such as `/path**` is invalid and will be treated the same as `/path*`, but +`/path*/**` should achieve the desired result. Additionally, `/path/**` will +match all directories and files under the path directory, but `/path/**/` will +only match directories. diff --git a/docs/operator-manual/applicationset/Generators-Git.md b/docs/operator-manual/applicationset/Generators-Git.md index e78fdf2812c6a..7e4aa5fdb1c24 100644 --- a/docs/operator-manual/applicationset/Generators-Git.md +++ b/docs/operator-manual/applicationset/Generators-Git.md @@ -37,6 +37,8 @@ metadata: name: cluster-addons namespace: argocd spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - git: repoURL: https://github.com/argoproj/argo-cd.git @@ -45,16 +47,16 @@ spec: - path: applicationset/examples/git-generator-directory/cluster-addons/* template: metadata: - name: '{{path.basename}}' + name: '{{.path.basename}}' spec: project: "my-project" source: repoURL: https://github.com/argoproj/argo-cd.git targetRevision: HEAD - path: '{{path}}' + path: '{{.path.path}}' destination: server: https://kubernetes.default.svc - namespace: '{{path.basename}}' + namespace: '{{.path.basename}}' syncPolicy: syncOptions: - CreateNamespace=true @@ -63,14 +65,14 @@ spec: The generator parameters are: -- `{{path}}`: The directory paths within the Git repository that match the `path` wildcard. -- `{{path[n]}}`: The directory paths within the Git repository that match the `path` wildcard, split into array elements (`n` - array index) -- `{{path.basename}}`: For any directory path within the Git repository that matches the `path` wildcard, the right-most path name is extracted (e.g. `/directory/directory2` would produce `directory2`). -- `{{path.basenameNormalized}}`: This field is the same as `path.basename` with unsupported characters replaced with `-` (e.g. a `path` of `/directory/directory_2`, and `path.basename` of `directory_2` would produce `directory-2` here). +- `{{.path.path}}`: The directory paths within the Git repository that match the `path` wildcard. +- `{{index .path.segments n}}`: The directory paths within the Git repository that match the `path` wildcard, split into array elements (`n` - array index) +- `{{.path.basename}}`: For any directory path within the Git repository that matches the `path` wildcard, the right-most path name is extracted (e.g. `/directory/directory2` would produce `directory2`). +- `{{.path.basenameNormalized}}`: This field is the same as `path.basename` with unsupported characters replaced with `-` (e.g. a `path` of `/directory/directory_2`, and `path.basename` of `directory_2` would produce `directory-2` here). -**Note**: The right-most path name always becomes `{{path.basename}}`. For example, for `- path: /one/two/three/four`, `{{path.basename}}` is `four`. +**Note**: The right-most path name always becomes `{{.path.basename}}`. For example, for `- path: /one/two/three/four`, `{{.path.basename}}` is `four`. -**Note**: If the `pathParamPrefix` option is specified, all `path`-related parameter names above will be prefixed with the specified value and a dot separator. E.g., if `pathParamPrefix` is `myRepo`, then the generated parameter name would be `myRepo.path` instead of `path`. Using this option is necessary in a Matrix generator where both child generators are Git generators (to avoid conflicts when merging the child generators’ items). +**Note**: If the `pathParamPrefix` option is specified, all `path`-related parameter names above will be prefixed with the specified value and a dot separator. E.g., if `pathParamPrefix` is `myRepo`, then the generated parameter name would be `.myRepo.path` instead of `.path`. Using this option is necessary in a Matrix generator where both child generators are Git generators (to avoid conflicts when merging the child generators’ items). Whenever a new Helm chart/Kustomize YAML/Application/plain subdirectory is added to the Git repository, the ApplicationSet controller will detect this change and automatically deploy the resulting manifests within new `Application` resources. @@ -89,6 +91,8 @@ metadata: name: cluster-addons namespace: argocd spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - git: repoURL: https://github.com/argoproj/argo-cd.git @@ -99,18 +103,18 @@ spec: exclude: true template: metadata: - name: '{{path.basename}}' + name: '{{.path.basename}}' spec: project: "my-project" source: repoURL: https://github.com/argoproj/argo-cd.git targetRevision: HEAD - path: '{{path}}' + path: '{{.path.path}}' destination: server: https://kubernetes.default.svc - namespace: '{{path.basename}}' + namespace: '{{.path.basename}}' ``` -(*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/examples/applicationset/git-generator-directory/excludes).*) +(*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/git-generator-directory/excludes).*) This example excludes the `exclude-helm-guestbook` directory from the list of directories scanned for this `ApplicationSet` resource. @@ -153,7 +157,7 @@ Or, a shorter way (using [path.Match](https://golang.org/pkg/path/#Match) syntax ```yaml - path: /d/* -- path: /d/[f|g] +- path: /d/[fg] exclude: true ``` @@ -170,6 +174,8 @@ metadata: name: cluster-addons namespace: argocd spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - git: repoURL: https://github.com/example/example-repo.git @@ -180,18 +186,59 @@ spec: exclude: true template: metadata: - name: '{{path.basename}}' + name: '{{.path.basename}}' + spec: + project: "my-project" + source: + repoURL: https://github.com/example/example-repo.git + targetRevision: HEAD + path: '{{.path.path}}' + destination: + server: https://kubernetes.default.svc + namespace: '{{.path.basename}}' +``` + +### Pass additional key-value pairs via `values` field + +You may pass additional, arbitrary string key-value pairs via the `values` field of the git directory generator. Values added via the `values` field are added as `values.(field)`. + +In this example, a `cluster` parameter value is passed. It is interpolated from the `branch` and `path` variable, to then be used to determine the destination namespace. +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: cluster-addons + namespace: argocd +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + - git: + repoURL: https://github.com/example/example-repo.git + revision: HEAD + directories: + - path: '*' + values: + cluster: '{{.branch}}-{{.path.basename}}' + template: + metadata: + name: '{{.path.basename}}' spec: project: "my-project" source: repoURL: https://github.com/example/example-repo.git targetRevision: HEAD - path: '{{path}}' + path: '{{.path.path}}' destination: server: https://kubernetes.default.svc - namespace: '{{path.basename}}' + namespace: '{{.values.cluster}}' ``` +!!! note + The `values.` prefix is always prepended to values provided via `generators.git.values` field. Ensure you include this prefix in the parameter name within the `template` when using it. + +In `values` we can also interpolate all fields set by the git directory generator as mentioned above. + ## Git Generator: Files The Git file generator is the second subtype of the Git generator. The Git file generator generates parameters using the contents of JSON/YAML files found within a specified repository. @@ -249,6 +296,8 @@ metadata: name: guestbook namespace: argocd spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - git: repoURL: https://github.com/argoproj/argo-cd.git @@ -257,7 +306,7 @@ spec: - path: "applicationset/examples/git-generator-files-discovery/cluster-config/**/config.json" template: metadata: - name: '{{cluster.name}}-guestbook' + name: '{{.cluster.name}}-guestbook' spec: project: default source: @@ -265,29 +314,72 @@ spec: targetRevision: HEAD path: "applicationset/examples/git-generator-files-discovery/apps/guestbook" destination: - server: '{{cluster.address}}' + server: '{{.cluster.address}}' namespace: guestbook ``` (*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/git-generator-files-discovery).*) -Any `config.json` files found under the `cluster-config` directory will be parameterized based on the `path` wildcard pattern specified. Within each file JSON fields are flattened into key/value pairs, with this ApplicationSet example using the `cluster.address` as `cluster.name` parameters in the template. +Any `config.json` files found under the `cluster-config` directory will be parameterized based on the `path` wildcard pattern specified. Within each file JSON fields are flattened into key/value pairs, with this ApplicationSet example using the `cluster.address` and `cluster.name` parameters in the template. As with other generators, clusters *must* already be defined within Argo CD, in order to generate Applications for them. In addition to the flattened key/value pairs from the configuration file, the following generator parameters are provided: -- `{{path}}`: The path to the directory containing matching configuration file within the Git repository. Example: `/clusters/clusterA`, if the config file was `/clusters/clusterA/config.json` -- `{{path[n]}}`: The path to the matching configuration file within the Git repository, split into array elements (`n` - array index). Example: `path[0]: clusters`, `path[1]: clusterA` -- `{{path.basename}}`: Basename of the path to the directory containing the configuration file (e.g. `clusterA`, with the above example.) -- `{{path.basenameNormalized}}`: This field is the same as `path.basename` with unsupported characters replaced with `-` (e.g. a `path` of `/directory/directory_2`, and `path.basename` of `directory_2` would produce `directory-2` here). -- `{{path.filename}}`: The matched filename. e.g., `config.json` in the above example. -- `{{path.filenameNormalized}}`: The matched filename with unsupported characters replaced with `-`. +- `{{.path.path}}`: The path to the directory containing matching configuration file within the Git repository. Example: `/clusters/clusterA`, if the config file was `/clusters/clusterA/config.json` +- `{{index .path n}}`: The path to the matching configuration file within the Git repository, split into array elements (`n` - array index). Example: `index .path 0: clusters`, `index .path 1: clusterA` +- `{{.path.basename}}`: Basename of the path to the directory containing the configuration file (e.g. `clusterA`, with the above example.) +- `{{.path.basenameNormalized}}`: This field is the same as `.path.basename` with unsupported characters replaced with `-` (e.g. a `path` of `/directory/directory_2`, and `.path.basename` of `directory_2` would produce `directory-2` here). +- `{{.path.filename}}`: The matched filename. e.g., `config.json` in the above example. +- `{{.path.filenameNormalized}}`: The matched filename with unsupported characters replaced with `-`. -**Note**: The right-most *directory* name always becomes `{{path.basename}}`. For example, from `- path: /one/two/three/four/config.json`, `{{path.basename}}` will be `four`. -The filename can always be accessed using `{{path.filename}}`. +**Note**: The right-most *directory* name always becomes `{{.path.basename}}`. For example, from `- path: /one/two/three/four/config.json`, `{{.path.basename}}` will be `four`. +The filename can always be accessed using `{{.path.filename}}`. **Note**: If the `pathParamPrefix` option is specified, all `path`-related parameter names above will be prefixed with the specified value and a dot separator. E.g., if `pathParamPrefix` is `myRepo`, then the generated parameter name would be `myRepo.path` instead of `path`. Using this option is necessary in a Matrix generator where both child generators are Git generators (to avoid conflicts when merging the child generators’ items). +**Note**: The default behavior of the Git file generator is very greedy. Please see [Git File Generator Globbing](./Generators-Git-File-Globbing.md) for more information. + +### Pass additional key-value pairs via `values` field + +You may pass additional, arbitrary string key-value pairs via the `values` field of the git files generator. Values added via the `values` field are added as `values.(field)`. + +In this example, a `base_dir` parameter value is passed. It is interpolated from `path` segments, to then be used to determine the source path. +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: guestbook + namespace: argocd +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + - git: + repoURL: https://github.com/argoproj/argo-cd.git + revision: HEAD + files: + - path: "applicationset/examples/git-generator-files-discovery/cluster-config/**/config.json" + values: + base_dir: "{{index .path 0}}/{{index .path 1}}/{{index .path 2}}" + template: + metadata: + name: '{{.cluster.name}}-guestbook' + spec: + project: default + source: + repoURL: https://github.com/argoproj/argo-cd.git + targetRevision: HEAD + path: "{{.values.base_dir}}/apps/guestbook" + destination: + server: '{{.cluster.address}}' + namespace: guestbook +``` + +!!! note + The `values.` prefix is always prepended to values provided via `generators.git.values` field. Ensure you include this prefix in the parameter name within the `template` when using it. + +In `values` we can also interpolate all fields set by the git files generator as mentioned above. + ## Webhook Configuration When using a Git generator, ApplicationSet polls Git repositories every three minutes to detect changes. To eliminate @@ -295,7 +387,7 @@ this delay from polling, the ApplicationSet webhook server can be configured to Git webhook notifications from GitHub and GitLab. The following explains how to configure a Git webhook for GitHub, but the same process should be applicable to other providers. !!! note - ApplicationSet exposes the webhook server as a service of type ClusterIP. An Ingress resource needs to be created to expose this service to the webhook source. + The ApplicationSet controller webhook does not use the same webhook as the API server as defined [here](../webhook.md). ApplicationSet exposes a webhook server as a service of type ClusterIP. An ApplicationSet specific Ingress resource needs to be created to expose this service to the webhook source. ### 1. Create the webhook in the Git provider @@ -317,15 +409,15 @@ the contents of webhook payloads are considered untrusted, and will only result application (a process which already occurs at three-minute intervals). If ApplicationSet is publicly accessible, then configuring a webhook secret is recommended to prevent a DDoS attack. -In the `argocd-secret` kubernetes secret, include the Git provider's webhook secret configured in step 1. +In the `argocd-secret` Kubernetes secret, include the Git provider's webhook secret configured in step 1. -Edit the Argo CD kubernetes secret: +Edit the Argo CD Kubernetes secret: ```bash kubectl edit secret argocd-secret -n argocd ``` -TIP: for ease of entering secrets, kubernetes supports inputting secrets in the `stringData` field, +TIP: for ease of entering secrets, Kubernetes supports inputting secrets in the `stringData` field, which saves you the trouble of base64 encoding the values and copying it to the `data` field. Simply copy the shared webhook secret created in step 1, to the corresponding GitHub/GitLab/BitBucket key under the `stringData` field: diff --git a/docs/operator-manual/applicationset/Generators-List.md b/docs/operator-manual/applicationset/Generators-List.md index 6b99a854a0f2f..e5696f37b9745 100644 --- a/docs/operator-manual/applicationset/Generators-List.md +++ b/docs/operator-manual/applicationset/Generators-List.md @@ -8,25 +8,26 @@ metadata: name: guestbook namespace: argocd spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - list: elements: - cluster: engineering-dev url: https://kubernetes.default.svc -# - cluster: engineering-prod -# url: https://kubernetes.default.svc -# foo: bar + - cluster: engineering-prod + url: https://kubernetes.default.svc template: metadata: - name: '{{cluster}}-guestbook' + name: '{{.cluster}}-guestbook' spec: project: "my-project" source: repoURL: https://github.com/argoproj/argo-cd.git targetRevision: HEAD - path: applicationset/examples/list-generator/guestbook/{{cluster}} + path: applicationset/examples/list-generator/guestbook/{{.cluster}} destination: - server: '{{url}}' + server: '{{.url}}' namespace: guestbook ``` (*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/list-generator).*) @@ -38,18 +39,78 @@ With the ApplicationSet v0.1.0 release, one could *only* specify `url` and `clus spec: generators: - list: - elements: - # v0.1.0 form - requires cluster/url keys: - - cluster: engineering-dev - url: https://kubernetes.default.svc - values: - additional: value - # v0.2.0+ form - does not require cluster/URL keys - # (but they are still supported). - - staging: "true" - gitRepo: https://kubernetes.default.svc + elements: + # v0.1.0 form - requires cluster/url keys: + - cluster: engineering-dev + url: https://kubernetes.default.svc + values: + additional: value + # v0.2.0+ form - does not require cluster/URL keys + # (but they are still supported). + - staging: "true" + gitRepo: https://kubernetes.default.svc # (...) ``` !!! note "Clusters must be predefined in Argo CD" These clusters *must* already be defined within Argo CD, in order to generate applications for these values. The ApplicationSet controller does not create clusters within Argo CD (for instance, it does not have the credentials to do so). + +## Dynamically generated elements +The List generator can also dynamically generate its elements based on a yaml/json it gets from a previous generator like git by combining the two with a matrix generator. In this example we are using the matrix generator with a git followed by a list generator and pass the content of a file in git as input to the `elementsYaml` field of the list generator: +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: elementsYaml + namespace: argocd +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + - matrix: + generators: + - git: + repoURL: https://github.com/argoproj/argo-cd.git + revision: HEAD + files: + - path: applicationset/examples/list-generator/list-elementsYaml-example.yaml + - list: + elementsYaml: "{{ .key.components | toJson }}" + template: + metadata: + name: '{{.name}}' + spec: + project: default + syncPolicy: + automated: + selfHeal: true + syncOptions: + - CreateNamespace=true + sources: + - chart: '{{.chart}}' + repoURL: '{{.repoUrl}}' + targetRevision: '{{.version}}' + helm: + releaseName: '{{.releaseName}}' + destination: + server: https://kubernetes.default.svc + namespace: '{{.namespace}}' +``` + +where `list-elementsYaml-example.yaml` content is: +```yaml +key: + components: + - name: component1 + chart: podinfo + version: "6.3.2" + releaseName: component1 + repoUrl: "https://stefanprodan.github.io/podinfo" + namespace: component1 + - name: component2 + chart: podinfo + version: "6.3.3" + releaseName: component2 + repoUrl: "ghcr.io/stefanprodan/charts" + namespace: component2 +``` diff --git a/docs/operator-manual/applicationset/Generators-Matrix.md b/docs/operator-manual/applicationset/Generators-Matrix.md index c0316d61c333c..0396b8c0e06d3 100644 --- a/docs/operator-manual/applicationset/Generators-Matrix.md +++ b/docs/operator-manual/applicationset/Generators-Matrix.md @@ -35,6 +35,8 @@ kind: ApplicationSet metadata: name: cluster-git spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: # matrix 'parent' generator - matrix: @@ -52,16 +54,16 @@ spec: argocd.argoproj.io/secret-type: cluster template: metadata: - name: '{{path.basename}}-{{name}}' + name: '{{.path.basename}}-{{.name}}' spec: - project: '{{metadata.labels.environment}}' + project: '{{index .metadata.labels "environment"}}' source: repoURL: https://github.com/argoproj/argo-cd.git targetRevision: HEAD - path: '{{path}}' + path: '{{.path.path}}' destination: - server: '{{server}}' - namespace: '{{path.basename}}' + server: '{{.server}}' + namespace: '{{.path.basename}}' ``` First, the Git directory generator will scan the Git repository, discovering directories under the specified path. It discovers the argo-workflows and prometheus-operator applications, and produces two corresponding sets of parameters: @@ -117,6 +119,8 @@ kind: ApplicationSet metadata: name: cluster-git spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: # matrix 'parent' generator - matrix: @@ -132,10 +136,10 @@ spec: selector: matchLabels: argocd.argoproj.io/secret-type: cluster - kubernetes.io/environment: '{{path.basename}}' + kubernetes.io/environment: '{{.path.basename}}' template: metadata: - name: '{{name}}-guestbook' + name: '{{.name}}-guestbook' spec: project: default source: @@ -143,7 +147,7 @@ spec: targetRevision: HEAD path: "examples/git-generator-files-discovery/apps/guestbook" destination: - server: '{{server}}' + server: '{{.server}}' namespace: guestbook ``` Here is the corresponding folder structure for the git repository used by the git-files generator: @@ -162,12 +166,94 @@ Here is the corresponding folder structure for the git repository used by the gi │ └── config.json └── git-generator-files.yaml ``` -In the above example, the `{{path.basename}}` parameters produced by the git-files generator will resolve to `dev` and `prod`. -In the 2nd child generator, the label selector with label `kubernetes.io/environment: {{path.basename}}` will resolve with the values produced by the first child generator's parameters (`kubernetes.io/environment: prod` and `kubernetes.io/environment: dev`). +In the above example, the `{{.path.basename}}` parameters produced by the git-files generator will resolve to `dev` and `prod`. +In the 2nd child generator, the label selector with label `kubernetes.io/environment: {{.path.basename}}` will resolve with the values produced by the first child generator's parameters (`kubernetes.io/environment: prod` and `kubernetes.io/environment: dev`). So in the above example, clusters with the label `kubernetes.io/environment: prod` will have only prod-specific configuration (ie. `prod/config.json`) applied to it, wheres clusters with the label `kubernetes.io/environment: dev` will have only dev-specific configuration (ie. `dev/config.json`) +## Overriding parameters from one child generator in another child generator + +The Matrix Generator allows parameters with the same name to be defined in multiple child generators. This is useful, for example, to define default values for all stages in one generator and override them with stage-specific values in another generator. The example below generates a Helm-based application using a matrix generator with two git generators: the first provides stage-specific values (one directory per stage) and the second provides global values for all stages. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: parameter-override-example +spec: + generators: + - matrix: + generators: + - git: + repoURL: https://github.com/example/values.git + revision: HEAD + files: + - path: "**/stage.values.yaml" + - git: + repoURL: https://github.com/example/values.git + revision: HEAD + files: + - path: "global.values.yaml" + goTemplate: true + template: + metadata: + name: example + spec: + project: default + source: + repoURL: https://github.com/example/example-app.git + targetRevision: HEAD + path: . + helm: + values: | + {{ `{{ . | mustToPrettyJson }}` }} + destination: + server: in-cluster + namespace: default +``` + +Given the following structure/content of the example/values repository: + +``` +├── test +│ └── stage.values.yaml +│ stageName: test +│ cpuRequest: 100m +│ debugEnabled: true +├── staging +│ └── stage.values.yaml +│ stageName: staging +├── production +│ └── stage.values.yaml +│ stageName: production +│ memoryLimit: 512Mi +│ debugEnabled: false +└── global.values.yaml + cpuRequest: 200m + memoryLimit: 256Mi + debugEnabled: true +``` + +The matrix generator above would yield the following results: + +```yaml +- stageName: test + cpuRequest: 100m + memoryLimit: 256Mi + debugEnabled: true + +- stageName: staging + cpuRequest: 200m + memoryLimit: 256Mi + debugEnabled: true + +- stageName: production + cpuRequest: 200m + memoryLimit: 512Mi + debugEnabled: false +``` + ## Example: Two Git Generators Using `pathParamPrefix` The matrix generator will fail if its children produce results containing identical keys with differing values. @@ -180,6 +266,8 @@ kind: ApplicationSet metadata: name: two-gits-with-path-param-prefix spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - matrix: generators: @@ -198,7 +286,7 @@ spec: repoURL: https://github.com/some-org/some-repo.git revision: HEAD files: - - path: "targets/{{appName}}/*.json" + - path: "targets/{{.appName}}/*.json" pathParamPrefix: target template: {} # ... ``` @@ -308,7 +396,7 @@ For example, the below example would be invalid (cluster-generator must come aft selector: matchLabels: argocd.argoproj.io/secret-type: cluster - kubernetes.io/environment: '{{path.basename}}' # {{path.basename}} is produced by git-files generator + kubernetes.io/environment: '{{.path.basename}}' # {{.path.basename}} is produced by git-files generator # git generator, 'child' #2 - git: repoURL: https://github.com/argoproj/applicationset.git @@ -316,7 +404,7 @@ For example, the below example would be invalid (cluster-generator must come aft files: - path: "examples/git-generator-files-discovery/cluster-config/**/config.json" -1. You cannot have both child generators consuming parameters from each another. In the example below, the cluster generator is consuming the `{{path.basename}}` parameter produced by the git-files generator, whereas the git-files generator is consuming the `{{name}}` parameter produced by the cluster generator. This will result in a circular dependency, which is invalid. +1. You cannot have both child generators consuming parameters from each another. In the example below, the cluster generator is consuming the `{{.path.basename}}` parameter produced by the git-files generator, whereas the git-files generator is consuming the `{{.name}}` parameter produced by the cluster generator. This will result in a circular dependency, which is invalid. - matrix: generators: @@ -325,10 +413,21 @@ For example, the below example would be invalid (cluster-generator must come aft selector: matchLabels: argocd.argoproj.io/secret-type: cluster - kubernetes.io/environment: '{{path.basename}}' # {{path.basename}} is produced by git-files generator + kubernetes.io/environment: '{{.path.basename}}' # {{.path.basename}} is produced by git-files generator # git generator, 'child' #2 - git: repoURL: https://github.com/argoproj/applicationset.git revision: HEAD files: - - path: "examples/git-generator-files-discovery/cluster-config/engineering/{{name}}**/config.json" # {{name}} is produced by cluster generator + - path: "examples/git-generator-files-discovery/cluster-config/engineering/{{.name}}**/config.json" # {{.name}} is produced by cluster generator + +1. When using a Matrix generator nested inside another Matrix or Merge generator, [Post Selectors](Generators-Post-Selector.md) for this nested generator's generators will only be applied when enabled via `spec.applyNestedSelectors`. You may also need to enable this even if your Post Selectors are not within the nested matrix or Merge generator, but are instead a sibling of a nested Matrix or Merge generator. + + - matrix: + generators: + - matrix: + generators: + - list + elements: + - # (...) + selector: { } # Only applied when applyNestedSelectors is true diff --git a/docs/operator-manual/applicationset/Generators-Merge.md b/docs/operator-manual/applicationset/Generators-Merge.md index eb32343accbf4..b2ccfe86fb66d 100644 --- a/docs/operator-manual/applicationset/Generators-Merge.md +++ b/docs/operator-manual/applicationset/Generators-Merge.md @@ -17,6 +17,8 @@ kind: ApplicationSet metadata: name: cluster-git spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: # merge 'parent' generator - merge: @@ -41,9 +43,9 @@ spec: values.redis: 'true' template: metadata: - name: '{{name}}' + name: '{{.name}}' spec: - project: '{{metadata.labels.environment}}' + project: '{{index .metadata.labels "environment"}}' source: repoURL: https://github.com/argoproj/argo-cd.git targetRevision: HEAD @@ -51,11 +53,11 @@ spec: helm: parameters: - name: kafka - value: '{{values.kafka}}' + value: '{{.values.kafka}}' - name: redis - value: '{{values.redis}}' + value: '{{.values.redis}}' destination: - server: '{{server}}' + server: '{{.server}}' namespace: default ``` @@ -111,6 +113,72 @@ When merged with the updated base parameters, the `values.redis` value for the p values.redis: 'true' ``` +## Example: Use value interpolation in merge + +Some generators support additional values and interpolating from generated variables to selected values. This can be used to teach the merge generator which generated variables to use to combine different generators. + +The following example combines discovered clusters and a git repository by cluster labels and the branch name: +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: cluster-git +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + # merge 'parent' generator: + # Use the selector set by both child generators to combine them. + - merge: + mergeKeys: + # Note that this would not work with goTemplate enabled, + # nested merge keys are not supported there. + - values.selector + generators: + # Assuming, all configured clusters have a label for their location: + # Set the selector to this location. + - clusters: + values: + selector: '{{index .metadata.labels "location"}}' + # The git repo may have different directories which correspond to the + # cluster locations, using these as a selector. + - git: + repoURL: https://github.com/argoproj/argocd-example-apps/ + revision: HEAD + directories: + - path: '*' + values: + selector: '{{.path.path}}' + template: + metadata: + name: '{{.name}}' + spec: + project: '{{index .metadata.labels "environment"}}' + source: + repoURL: https://github.com/argoproj/argocd-example-apps/ + # The cluster values field for each generator will be substituted here: + targetRevision: HEAD + path: '{{.path.path}}' + destination: + server: '{{.server}}' + namespace: default +``` + +Assuming a cluster named `germany01` with the label `metadata.labels.location=Germany` and a git repository containing a directory called `Germany`, this could combine to values as follows: + +```yaml + # From the cluster generator +- name: germany01 + server: https://1.2.3.4 + # From the git generator + path: Germany + # Combining selector with the merge generator + values.selector: 'Germany' + # More values from cluster & git generator + # […] +``` + + ## Restrictions 1. You should specify only a single generator per array entry. This is not valid: @@ -142,3 +210,23 @@ When merged with the updated base parameters, the `values.redis` value for the p - list: elements: - # (...) + +1. Merging on nested values while using `goTemplate: true` is currently not supported, this will not work + + spec: + goTemplate: true + generators: + - merge: + mergeKeys: + - values.merge + +1. When using a Merge generator nested inside another Matrix or Merge generator, [Post Selectors](Generators-Post-Selector.md) for this nested generator's generators will only be applied when enabled via `spec.applyNestedSelectors`. + + - merge: + generators: + - merge: + generators: + - list + elements: + - # (...) + selector: { } # Only applied when applyNestedSelectors is true diff --git a/docs/operator-manual/applicationset/Generators-Plugin.md b/docs/operator-manual/applicationset/Generators-Plugin.md new file mode 100644 index 0000000000000..d0888b9949b8e --- /dev/null +++ b/docs/operator-manual/applicationset/Generators-Plugin.md @@ -0,0 +1,344 @@ +# Plugin Generator + +Plugins allow you to provide your own generator. + +- You can write in any language +- Simple: a plugin just responds to RPC HTTP requests. +- You can use it in a sidecar, or standalone deployment. +- You can get your plugin running today, no need to wait 3-5 months for review, approval, merge and an Argo software + release. +- You can combine it with Matrix or Merge. + +To start working on your own plugin, you can generate a new repository based on the example +[applicationset-hello-plugin](https://github.com/argoproj-labs/applicationset-hello-plugin). + +## Simple example + +Using a generator plugin without combining it with Matrix or Merge. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: myplugin +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + - plugin: + # Specify the configMap where the plugin configuration is located. + configMapRef: + name: my-plugin + # You can pass arbitrary parameters to the plugin. `input.parameters` is a map, but values may be any type. + # These parameters will also be available on the generator's output under the `generator.input.parameters` key. + input: + parameters: + key1: "value1" + key2: "value2" + list: ["list", "of", "values"] + boolean: true + map: + key1: "value1" + key2: "value2" + key3: "value3" + + # You can also attach arbitrary values to the generator's output under the `values` key. These values will be + # available in templates under the `values` key. + values: + value1: something + + # When using a Plugin generator, the ApplicationSet controller polls every `requeueAfterSeconds` interval (defaulting to every 30 minutes) to detect changes. + requeueAfterSeconds: 30 + template: + metadata: + name: myplugin + annotations: + example.from.input.parameters: "{{ index .generator.input.parameters.map "key1" }}" + example.from.values: "{{ .values.value1 }}" + # The plugin determines what else it produces. + example.from.plugin.output: "{{ .something.from.the.plugin }}" +``` + +- `configMapRef.name`: A `ConfigMap` name containing the plugin configuration to use for RPC call. +- `input.parameters`: Input parameters included in the RPC call to the plugin. (Optional) + +!!! note + The concept of the plugin should not undermine the spirit of GitOps by externalizing data outside of Git. The goal is to be complementary in specific contexts. + For example, when using one of the PullRequest generators, it's impossible to retrieve parameters related to the CI (only the commit hash is available), which limits the possibilities. By using a plugin, it's possible to retrieve the necessary parameters from a separate data source and use them to extend the functionality of the generator. + +### Add a ConfigMap to configure the access of the plugin + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-plugin + namespace: argocd +data: + token: "$plugin.myplugin.token" # Alternatively $:plugin.myplugin.token + baseUrl: "http://myplugin.plugin-ns.svc.cluster.local." +``` + +- `token`: Pre-shared token used to authenticate HTTP request (points to the right key you created in the `argocd-secret` Secret) +- `baseUrl`: BaseUrl of the k8s service exposing your plugin in the cluster. + +### Store credentials + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: argocd-secret + namespace: argocd + labels: + app.kubernetes.io/name: argocd-secret + app.kubernetes.io/part-of: argocd +type: Opaque +data: + # ... + # The secret value must be base64 encoded **once**. + # this value corresponds to: `printf "strong-password" | base64`. + plugin.myplugin.token: "c3Ryb25nLXBhc3N3b3Jk" + # ... +``` + +#### Alternative + +If you want to store sensitive data in **another** Kubernetes `Secret`, instead of `argocd-secret`, ArgoCD knows how to check the keys under `data` in your Kubernetes `Secret` for a corresponding key whenever a value in a configmap starts with `$`, then your Kubernetes `Secret` name and `:` (colon) followed by the key name. + +Syntax: `$:` + +> NOTE: Secret must have label `app.kubernetes.io/part-of: argocd` + +##### Example + +`another-secret`: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: another-secret + namespace: argocd + labels: + app.kubernetes.io/part-of: argocd +type: Opaque +data: + # ... + # Store client secret like below. + # The secret value must be base64 encoded **once**. + # This value corresponds to: `printf "strong-password" | base64`. + plugin.myplugin.token: "c3Ryb25nLXBhc3N3b3Jk" +``` + +### HTTP server + +#### A Simple Python Plugin + +You can deploy it either as a sidecar or as a standalone deployment (the latter is recommended). + +In the example, the token is stored in a file at this location : `/var/run/argo/token` + +``` +strong-password +``` + +```python +import json +from http.server import BaseHTTPRequestHandler, HTTPServer + +with open("/var/run/argo/token") as f: + plugin_token = f.read().strip() + + +class Plugin(BaseHTTPRequestHandler): + + def args(self): + return json.loads(self.rfile.read(int(self.headers.get('Content-Length')))) + + def reply(self, reply): + self.send_response(200) + self.end_headers() + self.wfile.write(json.dumps(reply).encode("UTF-8")) + + def forbidden(self): + self.send_response(403) + self.end_headers() + + def unsupported(self): + self.send_response(404) + self.end_headers() + + def do_POST(self): + if self.headers.get("Authorization") != "Bearer " + plugin_token: + self.forbidden() + + if self.path == '/api/v1/getparams.execute': + args = self.args() + self.reply({ + "output": { + "parameters": [ + { + "key1": "val1", + "key2": "val2" + }, + { + "key1": "val2", + "key2": "val2" + } + ] + } + }) + else: + self.unsupported() + + +if __name__ == '__main__': + httpd = HTTPServer(('', 4355), Plugin) + httpd.serve_forever() +``` + +Execute getparams with curl : + +``` +curl http://localhost:4355/api/v1/getparams.execute -H "Authorization: Bearer strong-password" -d \ +'{ + "applicationSetName": "fake-appset", + "input": { + "parameters": { + "param1": "value1" + } + } +}' +``` + +Some things to note here: + +- You only need to implement the calls `/api/v1/getparams.execute` +- You should check that the `Authorization` header contains the same bearer value as `/var/run/argo/token`. Return 403 if not +- The input parameters are included in the request body and can be accessed using the `input.parameters` variable. +- The output must always be a list of object maps nested under the `output.parameters` key in a map. +- `generator.input.parameters` and `values` are reserved keys. If present in the plugin output, these keys will be overwritten by the + contents of the `input.parameters` and `values` keys in the ApplicationSet's plugin generator spec. + +## With matrix and pull request example + +In the following example, the plugin implementation is returning a set of image digests for the given branch. The returned list contains only one item corresponding to the latest built image for the branch. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: fb-matrix +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + - matrix: + generators: + - pullRequest: + github: ... + requeueAfterSeconds: 30 + - plugin: + configMapRef: + name: cm-plugin + input: + parameters: + branch: "{{.branch}}" # provided by generator pull request + values: + branchLink: "https://git.example.com/org/repo/tree/{{.branch}}" + template: + metadata: + name: "fb-matrix-{{.branch}}" + spec: + source: + repoURL: "https://github.com/myorg/myrepo.git" + targetRevision: "HEAD" + path: charts/my-chart + helm: + releaseName: fb-matrix-{{.branch}} + valueFiles: + - values.yaml + values: | + front: + image: myregistry:{{.branch}}@{{ .digestFront }} # digestFront is generated by the plugin + back: + image: myregistry:{{.branch}}@{{ .digestBack }} # digestBack is generated by the plugin + project: default + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + destination: + server: https://kubernetes.default.svc + namespace: "{{.branch}}" + info: + - name: Link to the Application's branch + value: "{{values.branchLink}}" +``` + +To illustrate : + +- The generator pullRequest would return, for example, 2 branches: `feature-branch-1` and `feature-branch-2`. + +- The generator plugin would then perform 2 requests as follows : + +```shell +curl http://localhost:4355/api/v1/getparams.execute -H "Authorization: Bearer strong-password" -d \ +'{ + "applicationSetName": "fb-matrix", + "input": { + "parameters": { + "branch": "feature-branch-1" + } + } +}' +``` + +Then, + +```shell +curl http://localhost:4355/api/v1/getparams.execute -H "Authorization: Bearer strong-password" -d \ +'{ + "applicationSetName": "fb-matrix", + "input": { + "parameters": { + "branch": "feature-branch-2" + } + } +}' +``` + +For each call, it would return a unique result such as : + +```json +{ + "output": { + "parameters": [ + { + "digestFront": "sha256:a3f18c17771cc1051b790b453a0217b585723b37f14b413ad7c5b12d4534d411", + "digestBack": "sha256:4411417d614d5b1b479933b7420079671facd434fd42db196dc1f4cc55ba13ce" + } + ] + } +} +``` + +Then, + +```json +{ + "output": { + "parameters": [ + { + "digestFront": "sha256:7c20b927946805124f67a0cb8848a8fb1344d16b4d0425d63aaa3f2427c20497", + "digestBack": "sha256:e55e7e40700bbab9e542aba56c593cb87d680cefdfba3dd2ab9cfcb27ec384c2" + } + ] + } +} +``` + +In this example, by combining the two, you ensure that one or more pull requests are available and that the generated tag has been properly generated. This wouldn't have been possible with just a commit hash because a hash alone does not certify the success of the build. diff --git a/docs/operator-manual/applicationset/Generators-Post-Selector.md b/docs/operator-manual/applicationset/Generators-Post-Selector.md new file mode 100644 index 0000000000000..896e89e267d7c --- /dev/null +++ b/docs/operator-manual/applicationset/Generators-Post-Selector.md @@ -0,0 +1,61 @@ +# Post Selector all generators + +The Selector allows to post-filter based on generated values using the Kubernetes common labelSelector format. In the example, the list generator generates a set of two application which then filter by the key value to only select the `env` with value `staging`: + +## Example: List generator + Post Selector +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: guestbook +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + - list: + elements: + - cluster: engineering-dev + url: https://kubernetes.default.svc + env: staging + - cluster: engineering-prod + url: https://kubernetes.default.svc + env: prod + selector: + matchLabels: + env: staging + template: + metadata: + name: '{{.cluster}}-guestbook' + spec: + project: default + source: + repoURL: https://github.com/argoproj-labs/applicationset.git + targetRevision: HEAD + path: examples/list-generator/guestbook/{{.cluster}} + destination: + server: '{{.url}}' + namespace: guestbook +``` + +The List generator + Post Selector generates a single set of parameters: + +```yaml +- cluster: engineering-dev + url: https://kubernetes.default.svc + env: staging +``` + +It is also possible to use `matchExpressions` for more powerful selectors. + +```yaml +spec: + generators: + - clusters: {} + selector: + matchExpressions: + - key: server + operator: In + values: + - https://kubernetes.default.svc + - https://some-other-cluster +``` diff --git a/docs/operator-manual/applicationset/Generators-Pull-Request.md b/docs/operator-manual/applicationset/Generators-Pull-Request.md index d90eb2d0f4695..a213c1dbb23bb 100644 --- a/docs/operator-manual/applicationset/Generators-Pull-Request.md +++ b/docs/operator-manual/applicationset/Generators-Pull-Request.md @@ -8,6 +8,8 @@ kind: ApplicationSet metadata: name: myapps spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - pullRequest: # When using a Pull Request generator, the ApplicationSet controller polls every `requeueAfterSeconds` interval (defaulting to every 30 minutes) to detect changes. @@ -33,6 +35,8 @@ kind: ApplicationSet metadata: name: myapps spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - pullRequest: github: @@ -75,11 +79,13 @@ kind: ApplicationSet metadata: name: myapps spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - pullRequest: gitlab: - # The GitLab project. - project: myproject + # The GitLab project ID. + project: "12341234" # For self-hosted GitLab (optional) api: https://git.example.com/ # Reference to a Secret containing an access token. (optional) @@ -91,16 +97,21 @@ spec: - preview # MR state is used to filter MRs only with a certain state. (optional) pullRequestState: opened + # If true, skips validating the SCM provider's TLS certificate - useful for self-signed certificates. + insecure: false requeueAfterSeconds: 1800 template: # ... ``` -* `project`: Required name of the GitLab project. +* `project`: Required project ID of the GitLab project. * `api`: If using self-hosted GitLab, the URL to access it. (Optional) * `tokenRef`: A `Secret` name and key containing the GitLab access token to use for requests. If not specified, will make anonymous requests which have a lower rate limit and can only see public repositories. (Optional) * `labels`: Labels is used to filter the MRs that you want to target. (Optional) * `pullRequestState`: PullRequestState is an additional MRs filter to get only those with a certain state. Default: "" (all states) +* `insecure`: By default (false) - Skip checking the validity of the SCM's certificate - useful for self-signed TLS certificates. + +As a preferable alternative to setting `insecure` to true, you can configure self-signed TLS certificates for Gitlab by [mounting self-signed certificate to the applicationset controller](./Generators-SCM-Provider.md#self-signed-tls-certificates). ## Gitea @@ -112,6 +123,8 @@ kind: ApplicationSet metadata: name: myapps spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - pullRequest: gitea: @@ -148,6 +161,8 @@ kind: ApplicationSet metadata: name: myapps spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - pullRequest: bitbucketServer: @@ -180,6 +195,106 @@ If you want to access a private repository, you must also provide the credential * `username`: The username to authenticate with. It only needs read access to the relevant repo. * `passwordRef`: A `Secret` name and key containing the password or personal access token to use for requests. +## Bitbucket Cloud + +Fetch pull requests from a repo hosted on a Bitbucket Cloud. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: myapps +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + - pullRequest: + bitbucket: + # Workspace name where the repoistory is stored under. Required. + owner: myproject + # Repository slug. Required. + repo: myrepository + # URL of the Bitbucket Server. (optional) Will default to 'https://api.bitbucket.org/2.0'. + api: https://api.bitbucket.org/2.0 + # Credentials for Basic authentication (App Password). Either basicAuth or bearerToken + # authentication is required to access private repositories + basicAuth: + # The username to authenticate with + username: myuser + # Reference to a Secret containing the password or personal access token. + passwordRef: + secretName: mypassword + key: password + # Credentials for Bearer Token (App Token) authentication. Either basicAuth or bearerToken + # authentication is required to access private repositories + bearerToken: + tokenRef: + secretName: repotoken + key: token + # Labels are not supported by Bitbucket Cloud, so filtering by label is not possible. + # Filter PRs using the source branch name. (optional) + filters: + - branchMatch: ".*-argocd" + template: + # ... +``` + +- `owner`: Required name of the Bitbucket workspace +- `repo`: Required name of the Bitbucket repository. +- `api`: Optional URL to access the Bitbucket REST API. For the example above, an API request would be made to `https://api.bitbucket.org/2.0/repositories/{workspace}/{repo_slug}/pullrequests`. If not set, defaults to `https://api.bitbucket.org/2.0` +- `branchMatch`: Optional regexp filter which should match the source branch name. This is an alternative to labels which are not supported by Bitbucket server. + +If you want to access a private repository, Argo CD will need credentials to access repository in Bitbucket Cloud. You can use Bitbucket App Password (generated per user, with access to whole workspace), or Bitbucket App Token (generated per repository, with access limited to repository scope only). If both App Password and App Token are defined, App Token will be used. + +To use Bitbucket App Password, use `basicAuth` section. +- `username`: The username to authenticate with. It only needs read access to the relevant repo. +- `passwordRef`: A `Secret` name and key containing the password or personal access token to use for requests. + +In case of Bitbucket App Token, go with `bearerToken` section. +- `tokenRef`: A `Secret` name and key containing the app token to use for requests. + +## Azure DevOps + +Specify the organization, project and repository from which you want to fetch pull requests. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: myapps +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + - pullRequest: + azuredevops: + # Azure DevOps org to scan. Required. + organization: myorg + # Azure DevOps project name to scan. Required. + project: myproject + # Azure DevOps repo name to scan. Required. + repo: myrepository + # The Azure DevOps API URL to talk to. If blank, use https://dev.azure.com/. + api: https://dev.azure.com/ + # Reference to a Secret containing an access token. (optional) + tokenRef: + secretName: azure-devops-token + key: token + # Labels is used to filter the PRs that you want to target. (optional) + labels: + - preview + requeueAfterSeconds: 1800 + template: + # ... +``` + +* `organization`: Required name of the Azure DevOps organization. +* `project`: Required name of the Azure DevOps project. +* `repo`: Required name of the Azure DevOps repository. +* `api`: If using self-hosted Azure DevOps Repos, the URL to access it. (Optional) +* `tokenRef`: A `Secret` name and key containing the Azure DevOps access token to use for requests. If not specified, will make anonymous requests which have a lower rate limit and can only see public repositories. (Optional) +* `labels`: Filter the PRs to those containing **all** of the labels listed. (Optional) + ## Filters Filters allow selecting which pull requests to generate for. Each filter can declare one or more conditions, all of which must pass. If multiple filters are present, any can match for a repository to be included. If no filters are specified, all pull requests will be processed. @@ -191,6 +306,8 @@ kind: ApplicationSet metadata: name: myapps spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - pullRequest: # ... @@ -202,6 +319,7 @@ spec: ``` * `branchMatch`: A regexp matched against source branch names. +* `targetBranchMatch`: A regexp matched against target branch names. [GitHub](#github) and [GitLab](#gitlab) also support a `labels` filter. @@ -217,21 +335,23 @@ kind: ApplicationSet metadata: name: myapps spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - pullRequest: # ... template: metadata: - name: 'myapp-{{branch}}-{{number}}' + name: 'myapp-{{.branch}}-{{.number}}' spec: source: repoURL: 'https://github.com/myorg/myrepo.git' - targetRevision: '{{head_sha}}' + targetRevision: '{{.head_sha}}' path: kubernetes/ helm: parameters: - name: "image.tag" - value: "pull-{{head_sha}}" + value: "pull-{{.head_sha}}" project: "my-project" destination: server: https://kubernetes.default.svc @@ -246,23 +366,25 @@ kind: ApplicationSet metadata: name: myapps spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - pullRequest: # ... template: metadata: - name: 'myapp-{{branch}}-{{number}}' + name: 'myapp-{{.branch}}-{{.number}}' spec: source: repoURL: 'https://github.com/myorg/myrepo.git' - targetRevision: '{{head_sha}}' + targetRevision: '{{.head_sha}}' path: kubernetes/ kustomize: - nameSuffix: {{branch}} + nameSuffix: '{{.branch}}' commonLabels: - app.kubernetes.io/instance: {{branch}}-{{number}} + app.kubernetes.io/instance: '{{.branch}}-{{.number}}' images: - - ghcr.io/myorg/myrepo:{{head_sha}} + - 'ghcr.io/myorg/myrepo:{{.head_sha}}' project: "my-project" destination: server: https://kubernetes.default.svc @@ -272,8 +394,11 @@ spec: * `number`: The ID number of the pull request. * `branch`: The name of the branch of the pull request head. * `branch_slug`: The branch name will be cleaned to be conform to the DNS label standard as defined in [RFC 1123](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names), and truncated to 50 characters to give room to append/suffix-ing it with 13 more characters. +* `target_branch`: The name of the target branch of the pull request. +* `target_branch_slug`: The target branch name will be cleaned to be conform to the DNS label standard as defined in [RFC 1123](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names), and truncated to 50 characters to give room to append/suffix-ing it with 13 more characters. * `head_sha`: This is the SHA of the head of the pull request. * `head_short_sha`: This is the short SHA of the head of the pull request (8 characters long or the length of the head SHA if it's shorter). +* `head_short_sha_7`: This is the short SHA of the head of the pull request (7 characters long or the length of the head SHA if it's shorter). * `labels`: The array of pull request labels. (Supported only for Go Template ApplicationSet manifests.) ## Webhook Configuration @@ -282,6 +407,9 @@ When using a Pull Request generator, the ApplicationSet controller polls every ` The configuration is almost the same as the one described [in the Git generator](Generators-Git.md), but there is one difference: if you want to use the Pull Request Generator as well, additionally configure the following settings. +!!! note + The ApplicationSet controller webhook does not use the same webhook as the API server as defined [here](../webhook.md). ApplicationSet exposes a webhook server as a service of type ClusterIP. An ApplicationSet specific Ingress resource needs to be created to expose this service to the webhook source. + ### Github webhook configuration In section 1, _"Create the webhook in the Git provider"_, add an event so that a webhook request will be sent when a pull request is created, closed, or label changed. diff --git a/docs/operator-manual/applicationset/Generators-SCM-Provider.md b/docs/operator-manual/applicationset/Generators-SCM-Provider.md index e8f8cfe5abbcc..40c8e552fe573 100644 --- a/docs/operator-manual/applicationset/Generators-SCM-Provider.md +++ b/docs/operator-manual/applicationset/Generators-SCM-Provider.md @@ -87,10 +87,17 @@ spec: allBranches: true # If true, recurses through subgroups. If false, it searches only in the base group. Defaults to false. includeSubgroups: true + # If true and includeSubgroups is also true, include Shared Projects, which is gitlab API default. + # If false only search Projects under the same path. Defaults to true. + includeSharedProjects: false + # filter projects by topic. A single topic is supported by Gitlab API. Defaults to "" (all topics). + topic: "my-topic" # Reference to a Secret containing an access token. (optional) tokenRef: secretName: gitlab-token key: token + # If true, skips validating the SCM provider's TLS certificate - useful for self-signed certificates. + insecure: false template: # ... ``` @@ -99,12 +106,23 @@ spec: * `api`: If using self-hosted GitLab, the URL to access it. * `allBranches`: By default (false) the template will only be evaluated for the default branch of each repo. If this is true, every branch of every repository will be passed to the filters. If using this flag, you likely want to use a `branchMatch` filter. * `includeSubgroups`: By default (false) the controller will only search for repos directly in the base group. If this is true, it will recurse through all the subgroups searching for repos to scan. +* `includeSharedProjects`: If true and includeSubgroups is also true, include Shared Projects, which is gitlab API default. If false only search Projects under the same path. In general most would want the behaviour when set to false. Defaults to true. +* `topic`: filter projects by topic. A single topic is supported by Gitlab API. Defaults to "" (all topics). * `tokenRef`: A `Secret` name and key containing the GitLab access token to use for requests. If not specified, will make anonymous requests which have a lower rate limit and can only see public repositories. +* `insecure`: By default (false) - Skip checking the validity of the SCM's certificate - useful for self-signed TLS certificates. -For label filtering, the repository tags are used. +For label filtering, the repository topics are used. Available clone protocols are `ssh` and `https`. +### Self-signed TLS Certificates + +As a preferable alternative to setting `insecure` to true, you can configure self-signed TLS certificates for Gitlab. + +In order for a self-signed TLS certificate be used by an ApplicationSet's SCM / PR Gitlab Generator, the certificate needs to be mounted on the applicationset-controller. The path of the mounted certificate must be explicitly set using the environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH` or alternatively using parameter `--scm-root-ca-path`. The applicationset controller will read the mounted certificate to create the Gitlab client for SCM/PR Providers + +This can be achieved conveniently by setting `applicationsetcontroller.scm.root.ca.path` in the argocd-cmd-params-cm ConfigMap. Be sure to restart the ApplicationSet controller after setting this value. + ## Gitea The Gitea mode uses the Gitea API to scan organizations in your instance @@ -255,6 +273,87 @@ This SCM provider does not yet support label filtering Available clone protocols are `ssh` and `https`. +## AWS CodeCommit (Alpha) + +Uses AWS ResourceGroupsTagging and AWS CodeCommit APIs to scan repos across AWS accounts and regions. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: myapps +spec: + generators: + - scmProvider: + awsCodeCommit: + # AWS region to scan repos. + # default to the environmental region from ApplicationSet controller. + region: us-east-1 + # AWS role to assume to scan repos. + # default to the environmental role from ApplicationSet controller. + role: arn:aws:iam::111111111111:role/argocd-application-set-discovery + # If true, scan every branch of every repository. If false, scan only the main branch. Defaults to false. + allBranches: true + # AWS resource tags to filter repos with. + # see https://docs.aws.amazon.com/resourcegroupstagging/latest/APIReference/API_GetResources.html#resourcegrouptagging-GetResources-request-TagFilters for details + # default to no tagFilters, to include all repos in the region. + tagFilters: + - key: organization + value: platform-engineering + - key: argo-ready + template: + # ... +``` + +* `region`: (Optional) AWS region to scan repos. By default, use ApplicationSet controller's current region. +* `role`: (Optional) AWS role to assume to scan repos. By default, use ApplicationSet controller's current role. +* `allBranches`: (Optional) If `true`, scans every branch of eligible repositories. If `false`, check only the default branch of the eligible repositories. Default `false`. +* `tagFilters`: (Optional) A list of tagFilters to filter AWS CodeCommit repos with. See [AWS ResourceGroupsTagging API](https://docs.aws.amazon.com/resourcegroupstagging/latest/APIReference/API_GetResources.html#resourcegrouptagging-GetResources-request-TagFilters) for details. By default, no filter is included. + +This SCM provider does not support the following features + +* label filtering +* `sha`, `short_sha` and `short_sha_7` template parameters + +Available clone protocols are `ssh`, `https` and `https-fips`. + +### AWS IAM Permission Considerations + +In order to call AWS APIs to discover AWS CodeCommit repos, ApplicationSet controller must be configured with valid environmental AWS config, like current AWS region and AWS credentials. +AWS config can be provided via all standard options, like Instance Metadata Service (IMDS), config file, environment variables, or IAM roles for service accounts (IRSA). + +Depending on whether `role` is provided in `awsCodeCommit` property, AWS IAM permission requirement is different. + +#### Discover AWS CodeCommit Repositories in the same AWS Account as ApplicationSet Controller + +Without specifying `role`, ApplicationSet controller will use its own AWS identity to scan AWS CodeCommit repos. +This is suitable when you have a simple setup that all AWS CodeCommit repos reside in the same AWS account as your Argo CD. + +As the ApplicationSet controller AWS identity is used directly for repo discovery, it must be granted below AWS permissions. + +* `tag:GetResources` +* `codecommit:ListRepositories` +* `codecommit:GetRepository` +* `codecommit:GetFolder` +* `codecommit:ListBranches` + +#### Discover AWS CodeCommit Repositories across AWS Accounts and Regions + +By specifying `role`, ApplicationSet controller will first assume the `role`, and use it for repo discovery. +This enables more complicated use cases to discover repos from different AWS accounts and regions. + +The ApplicationSet controller AWS identity should be granted permission to assume target AWS roles. + +* `sts:AssumeRole` + +All AWS roles must have repo discovery related permissions. + +* `tag:GetResources` +* `codecommit:ListRepositories` +* `codecommit:GetRepository` +* `codecommit:GetFolder` +* `codecommit:ListBranches` + ## Filters Filters allow selecting which repositories to generate for. Each filter can declare one or more conditions, all of which must pass. If multiple filters are present, any can match for a repository to be included. If no filters are specified, all repositories will be processed. @@ -296,16 +395,18 @@ kind: ApplicationSet metadata: name: myapps spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - scmProvider: # ... template: metadata: - name: '{{ repository }}' + name: '{{ .repository }}' spec: source: - repoURL: '{{ url }}' - targetRevision: '{{ branch }}' + repoURL: '{{ .url }}' + targetRevision: '{{ .branch }}' path: kubernetes/ project: default destination: @@ -319,5 +420,52 @@ spec: * `branch`: The default branch of the repository. * `sha`: The Git commit SHA for the branch. * `short_sha`: The abbreviated Git commit SHA for the branch (8 chars or the length of the `sha` if it's shorter). -* `labels`: A comma-separated list of repository labels. +* `short_sha_7`: The abbreviated Git commit SHA for the branch (7 chars or the length of the `sha` if it's shorter). +* `labels`: A comma-separated list of repository labels in case of Gitea, repository topics in case of Gitlab and Github. Not supported by Bitbucket Cloud, Bitbucket Server, or Azure DevOps. * `branchNormalized`: The value of `branch` normalized to contain only lowercase alphanumeric characters, '-' or '.'. + +## Pass additional key-value pairs via `values` field + +You may pass additional, arbitrary string key-value pairs via the `values` field of any SCM generator. Values added via the `values` field are added as `values.(field)`. + +In this example, a `name` parameter value is passed. It is interpolated from `organization` and `repository` to generate a different template name. +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: myapps +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + - scmProvider: + bitbucketServer: + project: myproject + api: https://mycompany.bitbucket.org + allBranches: true + basicAuth: + username: myuser + passwordRef: + secretName: mypassword + key: password + values: + name: "{{.organization}}-{{.repository}}" + + template: + metadata: + name: '{{ .values.name }}' + spec: + source: + repoURL: '{{ .url }}' + targetRevision: '{{ .branch }}' + path: kubernetes/ + project: default + destination: + server: https://kubernetes.default.svc + namespace: default +``` + +!!! note + The `values.` prefix is always prepended to values provided via `generators.scmProvider.values` field. Ensure you include this prefix in the parameter name within the `template` when using it. + +In `values` we can also interpolate all fields set by the SCM generator as mentioned above. diff --git a/docs/operator-manual/applicationset/Generators.md b/docs/operator-manual/applicationset/Generators.md index cd61db5da7918..78600c771fddd 100644 --- a/docs/operator-manual/applicationset/Generators.md +++ b/docs/operator-manual/applicationset/Generators.md @@ -4,9 +4,9 @@ Generators are responsible for generating *parameters*, which are then rendered Generators are primarily based on the data source that they use to generate the template parameters. For example: the List generator provides a set of parameters from a *literal list*, the Cluster generator uses the *Argo CD cluster list* as a source, the Git generator uses files/directories from a *Git repository*, and so. -As of this writing there are eight generators: +As of this writing there are nine generators: -- [List generator](Generators-List.md): The List generator allows you to target Argo CD Applications to clusters based on a fixed list of cluster name/URL values. +- [List generator](Generators-List.md): The List generator allows you to target Argo CD Applications to clusters based on a fixed list of any chosen key/value element pairs. - [Cluster generator](Generators-Cluster.md): The Cluster generator allows you to target Argo CD Applications to clusters, based on the list of clusters defined within (and managed by) Argo CD (which includes automatically responding to cluster addition/removal events from Argo CD). - [Git generator](Generators-Git.md): The Git generator allows you to create Applications based on files within a Git repository, or based on the directory structure of a Git repository. - [Matrix generator](Generators-Matrix.md): The Matrix generator may be used to combine the generated parameters of two separate generators. @@ -14,5 +14,8 @@ As of this writing there are eight generators: - [SCM Provider generator](Generators-SCM-Provider.md): The SCM Provider generator uses the API of an SCM provider (eg GitHub) to automatically discover repositories within an organization. - [Pull Request generator](Generators-Pull-Request.md): The Pull Request generator uses the API of an SCMaaS provider (eg GitHub) to automatically discover open pull requests within an repository. - [Cluster Decision Resource generator](Generators-Cluster-Decision-Resource.md): The Cluster Decision Resource generator is used to interface with Kubernetes custom resources that use custom resource-specific logic to decide which set of Argo CD clusters to deploy to. +- [Plugin generator](Generators-Plugin.md): The Plugin generator make RPC HTTP request to provide parameters. + +All generators can be filtered by using the [Post Selector](Generators-Post-Selector.md) If you are new to generators, begin with the **List** and **Cluster** generators. For more advanced use cases, see the documentation for the remaining generators above. diff --git a/docs/operator-manual/applicationset/GoTemplate.md b/docs/operator-manual/applicationset/GoTemplate.md index 911754009ab14..1b651200bc6cc 100644 --- a/docs/operator-manual/applicationset/GoTemplate.md +++ b/docs/operator-manual/applicationset/GoTemplate.md @@ -12,6 +12,38 @@ An additional `normalize` function makes any string parameter usable as a valid with hyphens and truncating at 253 characters. This is useful when making parameters safe for things like Application names. +Another `slugify` function has been added which, by default, sanitizes and smart truncates (it doesn't cut a word into 2). This function accepts a couple of arguments: + +- The first argument (if provided) is an integer specifying the maximum length of the slug. +- The second argument (if provided) is a boolean indicating whether smart truncation is enabled. +- The last argument (if provided) is the input name that needs to be slugified. + +#### Usage example + +``` +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: test-appset +spec: + ... + template: + metadata: + name: 'hellos3-{{.name}}-{{ cat .branch | slugify 23 }}' + annotations: + label-1: '{{ cat .branch | slugify }}' + label-2: '{{ cat .branch | slugify 23 }}' + label-3: '{{ cat .branch | slugify 50 false }}' +``` + +If you want to customize [options defined by text/template](https://pkg.go.dev/text/template#Template.Option), you can +add the `goTemplateOptions: ["opt1", "opt2", ...]` key to your ApplicationSet next to `goTemplate: true`. Note that at +the time of writing, there is only one useful option defined, which is `missingkey=error`. + +The recommended setting of `goTemplateOptions` is `["missingkey=error"]`, which ensures that if undefined values are +looked up by your template then an error is reported instead of being ignored silently. This is not currently the default +behavior, for backwards compatibility. + ## Motivation Go Template is the Go Standard for string templating. It is also more powerful than fasttemplate (the default templating @@ -29,6 +61,7 @@ possible with Go text templates: kind: ApplicationSet spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] template: spec: source: @@ -42,6 +75,7 @@ possible with Go text templates: kind: ApplicationSet spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] template: spec: syncPolicy: "{{.syncPolicy}}" # This field may NOT be templated, because it is an object field. @@ -53,6 +87,7 @@ possible with Go text templates: kind: ApplicationSet spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] template: spec: source: @@ -87,7 +122,12 @@ By activating Go Templating, `{{ .path }}` becomes an object. Therefore, some ch generators' templating: - `{{ path }}` becomes `{{ .path.path }}` +- `{{ path.basename }}` becomes `{{ .path.basename }}` +- `{{ path.basenameNormalized }}` becomes `{{ .path.basenameNormalized }}` +- `{{ path.filename }}` becomes `{{ .path.filename }}` +- `{{ path.filenameNormalized }}` becomes `{{ .path.filenameNormalized }}` - `{{ path[n] }}` becomes `{{ index .path.segments n }}` +- `{{ values }}` if being used in the file generator becomes `{{ .values }}` Here is an example: @@ -126,6 +166,7 @@ metadata: name: cluster-addons spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - git: repoURL: https://github.com/argoproj/argo-cd.git @@ -157,6 +198,20 @@ It is also possible to use Sprig functions to construct the path variables manua | `{{path.filenameNormalized}}` | `{{.path.filenameNormalized}}` | `{{normalize .path.filename}}` | | `{{path[N]}}` | `-` | `{{index .path.segments N}}` | +## Available template functions + +ApplicationSet controller provides: + +- all [sprig](http://masterminds.github.io/sprig/) Go templates function except `env`, `expandenv` and `getHostByName` +- `normalize`: sanitizes the input so that it complies with the following rules: + 1. contains no more than 253 characters + 2. contains only lowercase alphanumeric characters, '-' or '.' + 3. starts and ends with an alphanumeric character + +- `slugify`: sanitizes like `normalize` and smart truncates (it doesn't cut a word into 2) like described in the [introduction](#introduction) section. +- `toYaml` / `fromYaml` / `fromYamlArray` helm like functions + + ## Examples ### Basic Go template usage @@ -170,6 +225,7 @@ metadata: name: guestbook spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - list: elements: @@ -205,6 +261,7 @@ metadata: name: guestbook spec: goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - list: elements: @@ -215,7 +272,7 @@ spec: nameSuffix: -my-name-suffix template: metadata: - name: '{{.cluster}}{{default "" .nameSuffix}}' + name: '{{.cluster}}{{dig "nameSuffix" "" .}}' spec: project: default source: @@ -229,3 +286,7 @@ spec: This ApplicationSet will produce an Application called `engineering-dev` and another called `engineering-prod-my-name-suffix`. + +Note that unset parameters are an error, so you need to avoid looking up a property that doesn't exist. Instead, use +template functions like `dig` to do the lookup with a default. If you prefer to have unset parameters default to zero, +you can remove `goTemplateOptions: ["missingkey=error"]` or set it to `goTemplateOptions: ["missingkey=invalid"]` diff --git a/docs/operator-manual/applicationset/Progressive-Rollouts.md b/docs/operator-manual/applicationset/Progressive-Syncs.md similarity index 78% rename from docs/operator-manual/applicationset/Progressive-Rollouts.md rename to docs/operator-manual/applicationset/Progressive-Syncs.md index aa7d01702102f..edfe0dad101f2 100644 --- a/docs/operator-manual/applicationset/Progressive-Rollouts.md +++ b/docs/operator-manual/applicationset/Progressive-Syncs.md @@ -1,21 +1,21 @@ -# Progressive Rollouts +# Progressive Syncs !!! warning "Alpha Feature" This is an experimental, alpha-quality feature that allows you to control the order in which the ApplicationSet controller will create or update the Applications owned by an ApplicationSet resource. It may be removed in future releases or modified in backwards-incompatible ways. ## Use Cases -The Progressive Rollouts feature set is intended to be light and flexible. The feature only interacts with the health of managed Applications. It is not intended to support direct integrations with other Rollout controllers (such as the native ReplicaSet controller or Argo Rollouts). +The Progressive Syncs feature set is intended to be light and flexible. The feature only interacts with the health of managed Applications. It is not intended to support direct integrations with other Rollout controllers (such as the native ReplicaSet controller or Argo Rollouts). -* Progressive Rollouts watch for the managed Application resources to become "Healthy" before proceeding to the next stage. +* Progressive Syncs watch for the managed Application resources to become "Healthy" before proceeding to the next stage. * Deployments, DaemonSets, StatefulSets, and [Argo Rollouts](https://argoproj.github.io/argo-rollouts/) are all supported, because the Application enters a "Progressing" state while pods are being rolled out. In fact, any resource with a health check that can report a "Progressing" status is supported. * [Argo CD Resource Hooks](../../user-guide/resource_hooks.md) are supported. We recommend this approach for users that need advanced functionality when an Argo Rollout cannot be used, such as smoke testing after a DaemonSet change. -## Enabling Progressive Rollouts -As an experimental feature, progressive rollouts must be explicitly enabled, in one of these ways. +## Enabling Progressive Syncs +As an experimental feature, progressive syncs must be explicitly enabled, in one of these ways. -1. Pass `--enable-progressive-rollouts` to the ApplicationSet controller args. -1. Set `ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_ROLLOUTS=true` in the ApplicationSet controller environment variables. -1. Set `applicationsetcontroller.enable.progressive.rollouts: true` in the ArgoCD ConfigMap. +1. Pass `--enable-progressive-syncs` to the ApplicationSet controller args. +1. Set `ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS=true` in the ApplicationSet controller environment variables. +1. Set `applicationsetcontroller.enable.progressive.syncs: true` in the Argo CD `argocd-cmd-params-cm` ConfigMap. ## Strategies @@ -31,10 +31,10 @@ All Applications managed by the ApplicationSet resource are updated simultaneous This update strategy allows you to group Applications by labels present on the generated Application resources. When the ApplicationSet changes, the changes will be applied to each group of Application resources sequentially. -* Application groups are selected by `matchExpressions`. +* Application groups are selected using their labels and `matchExpressions`. * All `matchExpressions` must be true for an Application to be selected (multiple expressions match with AND behavior). * The `In` and `NotIn` operators must match at least one value to be considered true (OR behavior). -* The `NotIn` operatorn has priority in the event that both a `NotIn` and `In` operator produce a match. +* The `NotIn` operator has priority in the event that both a `NotIn` and `In` operator produce a match. * All Applications in each group must become Healthy before the ApplicationSet controller will proceed to update the next group of Applications. * The number of simultaneous Application updates in a group will not exceed its `maxUpdate` parameter (default is 100%, unbounded). * RollingSync will capture external changes outside the ApplicationSet resource, since it relies on watching the OutOfSync status of the managed Applications. @@ -44,7 +44,7 @@ When the ApplicationSet changes, the changes will be applied to each group of Ap * If an Application is considered "Pending" for `applicationsetcontroller.default.application.progressing.timeout` seconds, the Application is automatically moved to Healthy status (default 300). #### Example -The following example illustrates how to stage a progressive rollout over Applications with explicitly configured environment labels. +The following example illustrates how to stage a progressive sync over Applications with explicitly configured environment labels. Once a change is pushed, the following will happen in order. @@ -52,8 +52,7 @@ Once a change is pushed, the following will happen in order. * The rollout will wait for all `env-qa` Applications to be manually synced via the `argocd` CLI or by clicking the Sync button in the UI. * 10% of all `env-prod` Applications will be updated at a time until all `env-prod` Applications have been updated. -``` ---- +```yaml apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: @@ -76,29 +75,30 @@ spec: rollingSync: steps: - matchExpressions: - - key: env + - key: envLabel operator: In values: - env-dev #maxUpdate: 100% # if undefined, all applications matched are updated together (default is 100%) - matchExpressions: - - key: env + - key: envLabel operator: In values: - env-qa maxUpdate: 0 # if 0, no matched applications will be updated - matchExpressions: - - key: env + - key: envLabel operator: In values: - env-prod maxUpdate: 10% # maxUpdate supports both integer and percentage string values (rounds down, but floored at 1 Application for >0%) goTemplate: true + goTemplateOptions: ["missingkey=error"] template: metadata: name: '{{.cluster}}-guestbook' labels: - env: '{{.env}}' + envLabel: '{{.env}}' spec: project: my-project source: diff --git a/docs/operator-manual/applicationset/Template.md b/docs/operator-manual/applicationset/Template.md index f66a403586bbd..d96fb39252fed 100644 --- a/docs/operator-manual/applicationset/Template.md +++ b/docs/operator-manual/applicationset/Template.md @@ -85,7 +85,7 @@ spec: spec: project: "default" source: - revision: HEAD + targetRevision: HEAD repoURL: https://github.com/argoproj/argo-cd.git # New path value is generated here: path: 'applicationset/examples/template-override/{{cluster}}-override' @@ -99,7 +99,7 @@ spec: source: repoURL: https://github.com/argoproj/argo-cd.git targetRevision: HEAD - # This 'default' value is not used: it is is replaced by the generator's template path, above + # This 'default' value is not used: it is replaced by the generator's template path, above path: applicationset/examples/template-override/default destination: server: '{{url}}' @@ -108,3 +108,71 @@ spec: (*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/template-override).*) In this example, the ApplicationSet controller will generate an `Application` resource using the `path` generated by the List generator, rather than the `path` value defined in `.spec.template`. + +## Template Patch + +Templating is only available on string type. However, some use cases may require applying templating on other types. + +Example: + +- Conditionally set the automated sync policy. +- Conditionally switch prune boolean to `true`. +- Add multiple helm value files from a list. + +The `templatePatch` feature enables advanced templating, with support for `json` and `yaml`. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: guestbook +spec: + goTemplate: true + generators: + - list: + elements: + - cluster: engineering-dev + url: https://kubernetes.default.svc + autoSync: true + prune: true + valueFiles: + - values.large.yaml + - values.debug.yaml + template: + metadata: + name: '{{.cluster}}-deployment' + spec: + project: "default" + source: + repoURL: https://github.com/infra-team/cluster-deployments.git + targetRevision: HEAD + path: guestbook/{{ .cluster }} + destination: + server: '{{.url}}' + namespace: guestbook + templatePatch: | + spec: + source: + helm: + valueFiles: + {{- range $valueFile := .valueFiles }} + - {{ $valueFile }} + {{- end }} + {{- if .autoSync }} + syncPolicy: + automated: + prune: {{ .prune }} + {{- end }} +``` + +!!! important + The `templatePatch` can apply arbitrary changes to the template. If parameters include untrustworthy user input, it + may be possible to inject malicious changes into the template. It is recommended to use `templatePatch` only with + trusted input or to carefully escape the input before using it in the template. Piping input to `toJson` should help + prevent, for example, a user from successfully injecting a string with newlines. + + The `spec.project` field is not supported in `templatePatch`. If you need to change the project, you can use the + `spec.project` field in the `template` field. + +!!! important + When writing a `templatePatch`, you're crafting a patch. So, if the patch includes an empty `spec: # nothing in here`, it will effectively clear out existing fields. See [#17040](https://github.com/argoproj/argo-cd/issues/17040) for an example of this behavior. diff --git a/docs/operator-manual/applicationset/Use-Cases.md b/docs/operator-manual/applicationset/Use-Cases.md index 0e9c65d3963ee..a13c6598072ca 100644 --- a/docs/operator-manual/applicationset/Use-Cases.md +++ b/docs/operator-manual/applicationset/Use-Cases.md @@ -68,10 +68,26 @@ Thus in the self-service use case, administrators desire to only allow some fiel Fortunately, the ApplicationSet controller presents an alternative solution to this use case: cluster administrators may safely create an `ApplicationSet` resource containing a Git generator that restricts deployment of application resources to fixed values with the `template` field, while allowing customization of 'safe' fields by developers, at will. +The `config.json` files contain information describing the app. + +```json +{ + (...) + "app": { + "source": "https://github.com/argoproj/argo-cd", + "revision": "HEAD", + "path": "applicationset/examples/git-generator-files-discovery/apps/guestbook" + } + (...) +} +``` + ```yaml kind: ApplicationSet # (...) spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - git: repoURL: https://github.com/argoproj/argo-cd.git @@ -82,9 +98,9 @@ spec: project: dev-team-one # project is restricted source: # developers may customize app details using JSON files from above repo URL - repoURL: {{app.source}} - targetRevision: {{app.revision}} - path: {{app.path}} + repoURL: {{.app.source}} + targetRevision: {{.app.revision}} + path: {{.app.path}} destination: name: production-cluster # cluster is restricted namespace: dev-team-one # namespace is restricted diff --git a/docs/operator-manual/applicationset/applicationset-specification.md b/docs/operator-manual/applicationset/applicationset-specification.md new file mode 100644 index 0000000000000..8899057bf7ff6 --- /dev/null +++ b/docs/operator-manual/applicationset/applicationset-specification.md @@ -0,0 +1,7 @@ +# ApplicationSet Specification + +The following describes all the available fields of an ApplicationSet: + +```yaml +{!docs/operator-manual/applicationset.yaml!} +``` diff --git a/docs/operator-manual/applicationset/index.md b/docs/operator-manual/applicationset/index.md index 1fe83fb2a0952..ea7c0f3deaf5d 100644 --- a/docs/operator-manual/applicationset/index.md +++ b/docs/operator-manual/applicationset/index.md @@ -27,6 +27,8 @@ kind: ApplicationSet metadata: name: guestbook spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - list: elements: @@ -38,15 +40,15 @@ spec: url: https://9.8.7.6 template: metadata: - name: '{{cluster}}-guestbook' + name: '{{.cluster}}-guestbook' spec: project: my-project source: repoURL: https://github.com/infra-team/cluster-deployments.git targetRevision: HEAD - path: guestbook/{{cluster}} + path: guestbook/{{.cluster}} destination: - server: '{{url}}' + server: '{{.url}}' namespace: guestbook ``` diff --git a/docs/operator-manual/argocd-cm-yaml.md b/docs/operator-manual/argocd-cm-yaml.md new file mode 100644 index 0000000000000..666e78d03fc1b --- /dev/null +++ b/docs/operator-manual/argocd-cm-yaml.md @@ -0,0 +1,7 @@ +# argocd-cm.yaml example + +An example of an argocd-cm.yaml file: + +```yaml +{!docs/operator-manual/argocd-cm.yaml!} +``` diff --git a/docs/operator-manual/argocd-cm.yaml b/docs/operator-manual/argocd-cm.yaml index 6618b567beac6..49458d40be929 100644 --- a/docs/operator-manual/argocd-cm.yaml +++ b/docs/operator-manual/argocd-cm.yaml @@ -85,6 +85,7 @@ data: # Configuration to customize resource behavior (optional) can be configured via splitted sub keys. # Keys are in the form: resource.customizations.ignoreDifferences., resource.customizations.health. # resource.customizations.actions., resource.customizations.knownTypeFields. + # resource.customizations.ignoreResourceUpdates. resource.customizations.ignoreDifferences.admissionregistration.k8s.io_MutatingWebhookConfiguration: | jsonPointers: - /webhooks/0/clientConfig/caBundle @@ -101,6 +102,33 @@ data: jsonPointers: - /spec/replicas + # Enable resource.customizations.ignoreResourceUpdates rules. If "false," those rules are not applied, and all updates + # to resources are applied to the cluster cache. Default is false. + resource.ignoreResourceUpdatesEnabled: "false" + + # Configuration to define customizations ignoring differences during watched resource updates to skip application reconciles. + resource.customizations.ignoreResourceUpdates.all: | + jsonPointers: + - /metadata/resourceVersion + + # Configuration to define customizations ignoring differences during watched resource updates can be configured via splitted sub key. + resource.customizations.ignoreResourceUpdates.argoproj.io_Application: | + jsonPointers: + - /status + + # jsonPointers and jqPathExpressions can be specified. + resource.customizations.ignoreResourceUpdates.autoscaling_HorizontalPodAutoscaler: | + jqPathExpressions: + - '.metadata.annotations."autoscaling.alpha.kubernetes.io/behavior"' + - '.metadata.annotations."autoscaling.alpha.kubernetes.io/conditions"' + - '.metadata.annotations."autoscaling.alpha.kubernetes.io/metrics"' + - '.metadata.annotations."autoscaling.alpha.kubernetes.io/current-metrics"' + jsonPointers: + - /metadata/annotations/autoscaling.alpha.kubernetes.io~1behavior + - /metadata/annotations/autoscaling.alpha.kubernetes.io~1conditions + - /metadata/annotations/autoscaling.alpha.kubernetes.io~1metrics + - /metadata/annotations/autoscaling.alpha.kubernetes.io~1current-metrics + resource.customizations.health.certmanager.k8s.io-Certificate: | hs = {} if obj.status ~= nil then @@ -203,13 +231,9 @@ data: # 'none' - disabled ignoreResourceStatusField: crd - # Configuration to add a config management plugin. - configManagementPlugins: | - - name: kasane - init: - command: [kasane, update] - generate: - command: [kasane, show] + # configuration to instruct controller to only watch for resources that it has permissions to list + # can be either empty, "normal" or "strict". By default, it is empty i.e. disabled. + resource.respectRBAC: "normal" # A set of settings that allow enabling or disabling the config management tool. # If unset, each defaults to "true". @@ -276,14 +300,22 @@ data: # have either a permanent banner or a regular closeable banner, and NOT both. eg. A user can't dismiss a # notification message (closeable) banner, to then immediately see a permanent banner. # ui.bannerpermanent: "true" - # An option to specify the position of the banner, either the top or bottom of the page. The default is at the top. - # Uncomment to make the banner appear at the bottom of the page. Any value other than "bottom" will make the banner appear at the top. + # An option to specify the position of the banner, either the top or bottom of the page, or both. The valid values + # are: "top", "bottom" and "both". The default (if the option is not provided), is "top". If "both" is specified, then + # the content appears both at the top and the bottom of the page. Uncomment the following line to make the banner appear + # at the bottom of the page. Change the value as needed. # ui.bannerposition: "bottom" # Application reconciliation timeout is the max amount of time required to discover if a new manifests version got # published to the repository. Reconciliation by timeout is disabled if timeout is set to 0. Three minutes by default. # > Note: argocd-repo-server deployment must be manually restarted after changing the setting. timeout.reconciliation: 180s + # With a large number of applications, the periodic refresh for each application can cause a spike in the refresh queue + # and can cause a spike in the repo-server component. To avoid this, you can set a jitter to the sync timeout, which will + # spread out the refreshes and give time to the repo-server to catch up. The jitter is the maximum duration that can be + # added to the sync timeout. So, if the sync timeout is 3 minutes and the jitter is 1 minute, then the actual timeout will + # be between 3 and 4 minutes. Disabled when the value is 0, defaults to 0. + timeout.reconciliation.jitter: 0 # cluster.inClusterEnabled indicates whether to allow in-cluster server address. This is enabled by default. cluster.inClusterEnabled: "true" @@ -331,3 +363,46 @@ data: - url: https://mycompany.splunk.com?search={{.metadata.namespace}} title: Splunk if: kind == "Pod" || kind == "Deployment" + + extension.config: | + extensions: + # Name defines the endpoint that will be used to register + # the extension route. + # Mandatory field. + - name: some-extension + backend: + # ConnectionTimeout is the maximum amount of time a dial to + # the extension server will wait for a connect to complete. + # Optional field. Default: 2 seconds + connectionTimeout: 2s + + # KeepAlive specifies the interval between keep-alive probes + # for an active network connection between the API server and + # the extension server. + # Optional field. Default: 15 seconds + keepAlive: 15s + + # IdleConnectionTimeout is the maximum amount of time an idle + # (keep-alive) connection between the API server and the extension + # server will remain idle before closing itself. + # Optional field. Default: 60 seconds + idleConnectionTimeout: 60s + + # MaxIdleConnections controls the maximum number of idle (keep-alive) + # connections between the API server and the extension server. + # Optional field. Default: 30 + maxIdleConnections: 30 + + services: + # URL is the address where the extension backend must be available. + # Mandatory field. + - url: http://httpbin.org + + # Cluster if provided, will have to match the application + # destination name or the destination server to have requests + # properly forwarded to this service URL. + # Optional field if only one service is specified. + # Mandatory if multiple services are specified. + cluster: + name: some-cluster + server: https://some-cluster diff --git a/docs/operator-manual/argocd-cmd-params-cm-yaml.md b/docs/operator-manual/argocd-cmd-params-cm-yaml.md new file mode 100644 index 0000000000000..1cdba010fcfc6 --- /dev/null +++ b/docs/operator-manual/argocd-cmd-params-cm-yaml.md @@ -0,0 +1,7 @@ +# argocd-cmd-params-cm.yaml example + +An example of an argocd-cmd-params-cm.yaml file: + +```yaml +{!docs/operator-manual/argocd-cmd-params-cm.yaml!} +``` diff --git a/docs/operator-manual/argocd-cmd-params-cm.yaml b/docs/operator-manual/argocd-cmd-params-cm.yaml index a694cc650b0d5..3cb79d85f3150 100644 --- a/docs/operator-manual/argocd-cmd-params-cm.yaml +++ b/docs/operator-manual/argocd-cmd-params-cm.yaml @@ -9,18 +9,19 @@ data: # Repo server address. (default "argocd-repo-server:8081") repo.server: "argocd-repo-server:8081" - # Dex server address (default "http://argocd-dex-server:5556") - dex.server: "http://argocd-dex-server:5556" - # Redis server hostname and port (e.g. argocd-redis:6379) redis.server: "argocd-redis:6379" - # Enable compression for data sent to Redis with the required compression algorithm. (default 'none') - redis.compression: none + # Enable compression for data sent to Redis with the required compression algorithm. (default 'gzip') + redis.compression: gzip # Redis database redis.db: # Open-Telemetry collector address: (e.g. "otel-collector:4317") - otlp.address: + otlp.address: "" + # Open-Telemetry collector insecure: (e.g. "true") + otlp.insecure: "true" + # Open-Telemetry collector headers: (e.g. "key1=value1,key2=value2") + otlp.headers: "" # List of additional namespaces where applications may be created in and # reconciled from. The namespace where Argo CD is installed to will always @@ -57,8 +58,26 @@ data: controller.resource.health.persist: "true" # Cache expiration default (default 24h0m0s) controller.default.cache.expiration: "24h0m0s" + # Sharding algorithm used to balance clusters accross application controller shards (default "legacy") + controller.sharding.algorithm: legacy + # Number of allowed concurrent kubectl fork/execs. Any value less than 1 means no limit. + controller.kubectl.parallelism.limit: "20" + # The maximum number of retries for each request + controller.k8sclient.retry.max: "0" + # The initial backoff delay on the first retry attempt in ms. Subsequent retries will double this backoff time up to a maximum threshold + controller.k8sclient.retry.base.backoff: "100" + # Grace period in seconds for ignoring consecutive errors while communicating with repo server. + controller.repo.error.grace.period.seconds: "180" + # Enables the server side diff feature at the application controller level. + # Diff calculation will be done by running a server side apply dryrun (when + # diff cache is unavailable). + controller.diff.server.side: "false" ## Server properties + # Listen on given address for incoming connections (default "0.0.0.0") + server.listen.address: "0.0.0.0" + # Listen on given address for metrics (default "0.0.0.0") + server.metrics.listen.address: "0.0.0.0" # Run server without TLS server.insecure: "false" # Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from / (default "/") @@ -67,6 +86,13 @@ data: server.rootpath: "" # Directory path that contains additional static assets server.staticassets: "/shared/app" + # The maximum number of retries for each request + server.k8sclient.retry.max: "0" + # The initial backoff delay on the first retry attempt in ms. Subsequent retries will double this backoff time up to a maximum threshold + server.k8sclient.retry.base.backoff: "100" + # Semicolon-separated list of content types allowed on non-GET requests. Set an empty string to allow all. Be aware + # that allowing content types besides application/json may make your API more vulnerable to CSRF attacks. + server.api.content.types: "application/json" # Set the logging format. One of: text|json (default "text") server.log.format: "text" @@ -78,14 +104,16 @@ data: server.repo.server.plaintext: "false" # Perform strict validation of TLS certificates when connecting to repo server server.repo.server.strict.tls: "false" + # Dex server address (default "http://argocd-dex-server:5556") + server.dex.server: "http://argocd-dex-server:5556" # Use a plaintext client (non-TLS) to connect to dex server server.dex.server.plaintext: "false" # Perform strict validation of TLS certificates when connecting to dex server server.dex.server.strict.tls: "false" # Disable client authentication server.disable.auth: "false" - # Enable GZIP compression - server.enable.gzip: "false" + # Toggle GZIP compression + server.enable.gzip: "true" # Set X-Frame-Options header in HTTP responses to value. To disable, set to "". (default "sameorigin") server.x.frame.options: "sameorigin" # The minimum SSL/TLS version that is acceptable (one of: 1.0|1.1|1.2|1.3) (default "1.2") @@ -108,6 +136,10 @@ data: server.enable.proxy.extension: "false" ## Repo-server properties + # Listen on given address for incoming connections (default "0.0.0.0") + reposerver.listen.address: "0.0.0.0" + # Listen on given address for metrics (default "0.0.0.0") + reposerver.metrics.listen.address: "0.0.0.0" # Set the logging format. One of: text|json (default "text") reposerver.log.format: "text" # Set the logging level. One of: debug|info|warn|error (default "info") @@ -143,6 +175,10 @@ data: reposerver.streamed.manifest.max.extracted.size: "1G" # Enable git submodule support reposerver.enable.git.submodule: "true" + # Number of concurrent git ls-remote requests. Any value less than 1 means no limit. + reposerver.git.lsremote.parallelism.limit: "0" + # Git requests timeout. + reposerver.git.request.timeout: "15s" # Disable TLS on the HTTP endpoint dexserver.disable.tls: "false" @@ -150,8 +186,6 @@ data: ## ApplicationSet Controller Properties # Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager. applicationsetcontroller.enable.leader.election: "false" - # Argo CD repo namespace (default: argocd) - applicationsetcontroller.namespace: "" # "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion), 'create-delete' (no update)" applicationsetcontroller.policy: "sync" # Print debug logs. Takes precedence over loglevel @@ -164,5 +198,24 @@ data: applicationsetcontroller.dryrun: "false" # Enable git submodule support applicationsetcontroller.enable.git.submodule: "true" - # Enables use of the Progressive Rollouts capability - applicationsetcontroller.enable.progressive.rollouts: "false" + # Enables use of the Progressive Syncs capability + applicationsetcontroller.enable.progressive.syncs: "false" + # A list of glob patterns specifying where to look for ApplicationSet resources. (default is only the ns where the controller is installed) + applicationsetcontroller.namespaces: "argocd,argocd-appsets-*" + # Path of the self-signed TLS certificate for SCM/PR Gitlab Generator + applicationsetcontroller.scm.root.ca.path: "" + # A comma separated list of allowed SCM providers (default "" is all SCM providers). + # Setting this field is required when using ApplicationSets-in-any-namespace, to prevent users from + # sending secrets from `tokenRef`s to disallowed `api` domains. + # The url used in the scm generator must exactly match one in the list + applicationsetcontroller.allowed.scm.providers: "https://git.example.com/,https://gitlab.example.com/" + # To disable SCM providers entirely (i.e. disable the SCM and PR generators), set this to "false". Default is "true". + applicationsetcontroller.enable.scm.providers: "false" + + ## Argo CD Notifications Controller Properties + # Set the logging level. One of: debug|info|warn|error (default "info") + notificationscontroller.log.level: "info" + # Set the logging format. One of: text|json (default "text") + notificationscontroller.log.format: "text" + # Enable self-service notifications config. Used in conjunction with apps-in-any-namespace. (default "false") + notificationscontroller.selfservice.enabled: "false" diff --git a/docs/operator-manual/argocd-rbac-cm-yaml.md b/docs/operator-manual/argocd-rbac-cm-yaml.md new file mode 100644 index 0000000000000..c0dbcde428543 --- /dev/null +++ b/docs/operator-manual/argocd-rbac-cm-yaml.md @@ -0,0 +1,7 @@ +# argocd-rbac-cm.yaml example + +An example of an argocd-rbac-cm.yaml file: + +```yaml +{!docs/operator-manual/argocd-rbac-cm.yaml!} +``` diff --git a/docs/operator-manual/argocd-rbac-cm.yaml b/docs/operator-manual/argocd-rbac-cm.yaml index 12ec17f8e9e14..b68d93ecc4f89 100644 --- a/docs/operator-manual/argocd-rbac-cm.yaml +++ b/docs/operator-manual/argocd-rbac-cm.yaml @@ -19,6 +19,15 @@ data: # Grant all members of 'my-org:team-beta' admins g, my-org:team-beta, role:admin + # it is possible to provide additional entries in this configmap to compose the final policy csv. + # In this case the key must follow the pattern 'policy..csv'. Argo CD will concatenate + # all additional policies it finds with this pattern below the main one ('policy.csv'). This is useful + # to allow composing policies in config management tools like Kustomize, Helm, etc. + policy.overlay.csv: | + p, role:tester, applications, *, */*, allow + p, role:tester, projects, *, *, allow + g, my-org:team-qa, role:tester + # policy.default is the name of the default role which Argo CD will falls back to, when # authorizing API requests (optional). If omitted or empty, users may be still be able to login, # but will see no apps, projects, etc... diff --git a/docs/operator-manual/argocd-repo-creds-yaml.md b/docs/operator-manual/argocd-repo-creds-yaml.md new file mode 100644 index 0000000000000..dca214068405c --- /dev/null +++ b/docs/operator-manual/argocd-repo-creds-yaml.md @@ -0,0 +1,7 @@ +# argocd-repo-creds.yaml example + +An example of an argocd-repo-creds.yaml file: + +```yaml +{!docs/operator-manual/argocd-repo-creds.yaml!} +``` diff --git a/docs/operator-manual/argocd-repositories-yaml.md b/docs/operator-manual/argocd-repositories-yaml.md new file mode 100644 index 0000000000000..c9c99357c391a --- /dev/null +++ b/docs/operator-manual/argocd-repositories-yaml.md @@ -0,0 +1,7 @@ +# argocd-repositories.yaml example + +An example of an argocd-repositories.yaml file: + +```yaml +{!docs/operator-manual/argocd-repositories.yaml!} +``` diff --git a/docs/operator-manual/argocd-repositories.yaml b/docs/operator-manual/argocd-repositories.yaml index 9857b1601bc16..b6aa0715c389d 100644 --- a/docs/operator-manual/argocd-repositories.yaml +++ b/docs/operator-manual/argocd-repositories.yaml @@ -4,18 +4,33 @@ apiVersion: v1 kind: Secret metadata: - name: my-private-repo + name: my-private-https-repo namespace: argocd labels: argocd.argoproj.io/secret-type: repository stringData: - url: https://github.com/argoproj/my-private-repository + url: https://github.com/argoproj/argocd-example-apps password: my-password username: my-username + insecure: "true" # Ignore validity of server's TLS certificate. Defaults to "false" + forceHttpBasicAuth: "true" # Skip auth method negotiation and force usage of HTTP basic auth. Defaults to "false" + enableLfs: "true" # Enable git-lfs for this repository. Defaults to "false" +--- +apiVersion: v1 +kind: Secret +metadata: + name: my-private-ssh-repo + namespace: argocd + labels: + argocd.argoproj.io/secret-type: repository +stringData: + url: ssh://git@github.com/argoproj/argocd-example-apps sshPrivateKey: | -----BEGIN OPENSSH PRIVATE KEY----- ... -----END OPENSSH PRIVATE KEY----- + insecure: "true" # Do not perform a host key check for the server. Defaults to "false" + enableLfs: "true" # Enable git-lfs for this repository. Defaults to "false" --- apiVersion: v1 kind: Secret diff --git a/docs/operator-manual/argocd-secret-yaml.md b/docs/operator-manual/argocd-secret-yaml.md new file mode 100644 index 0000000000000..33a88a8e96ee2 --- /dev/null +++ b/docs/operator-manual/argocd-secret-yaml.md @@ -0,0 +1,7 @@ +# argocd-secret.yaml example + +An example of an argocd-secret.yaml file: + +```yaml +{!docs/operator-manual/argocd-secret.yaml!} +``` diff --git a/docs/operator-manual/argocd-ssh-known-hosts-cm-yaml.md b/docs/operator-manual/argocd-ssh-known-hosts-cm-yaml.md new file mode 100644 index 0000000000000..4a5977f61e842 --- /dev/null +++ b/docs/operator-manual/argocd-ssh-known-hosts-cm-yaml.md @@ -0,0 +1,7 @@ +# argocd-ssh-known-hosts-cm.yaml example + +An example of an argocd-ssh-known-hosts-cm.yaml file: + +```yaml +{!docs/operator-manual/argocd-ssh-known-hosts-cm.yaml!} +``` diff --git a/docs/operator-manual/argocd-ssh-known-hosts-cm.yaml b/docs/operator-manual/argocd-ssh-known-hosts-cm.yaml index 3b211363837a9..0f30fa5671662 100644 --- a/docs/operator-manual/argocd-ssh-known-hosts-cm.yaml +++ b/docs/operator-manual/argocd-ssh-known-hosts-cm.yaml @@ -7,12 +7,18 @@ metadata: name: argocd-ssh-known-hosts-cm data: ssh_known_hosts: | - bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw== - github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + # This file was automatically generated by hack/update-ssh-known-hosts.sh. DO NOT EDIT + [ssh.github.com]:443 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + [ssh.github.com]:443 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + [ssh.github.com]:443 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + bitbucket.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIQmuzMBuKdWeF4+a2sjSSpBK0iqitSQ+5BM9KhpexuGt20JpTVM7u5BDZngncgrqDMbWdxMWWOGtZ9UgbqgZE= + bitbucket.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIazEu89wgQZ4bqs3d63QSMzYVa0MuJ2e2gKTKqu+UUO + bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQeJzhupRu0u0cdegZIa8e86EG2qOCsIsD1Xw0xSeiPDlCr7kq97NLmMbpKTX6Esc30NuoqEEHCuc7yWtwp8dI76EEEB1VqY9QJq6vk+aySyboD5QF61I/1WeTwu+deCbgKMGbUijeXhtfbxSxm6JwGrXrhBdofTsbKRUsrN1WoNgUa8uqN1Vx6WAJw1JHPhglEGGHea6QICwJOAr/6mrui/oB7pkaWKHj3z7d1IC4KWLtY47elvjbaTlkN04Kc/5LFEirorGYVbt15kAUlqGM65pk6ZBxtaO3+30LVlORZkxOh+LKL/BvbZ/iRNhItLqNyieoQj/uh/7Iv4uyH/cV/0b4WDSd3DptigWq84lJubb9t/DnZlrJazxyDCulTmKdOR7vs9gMTo+uoIrPSb8ScTtvw65+odKAlBj59dhnVp9zd7QUojOpXlL62Aw56U4oO+FALuevvMjiWeavKhJqlR7i5n9srYcrNV7ttmDw7kf/97P5zauIhxcjX+xHv4M= + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H - github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= - github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl diff --git a/docs/operator-manual/argocd-tls-certs-cm-yaml.md b/docs/operator-manual/argocd-tls-certs-cm-yaml.md new file mode 100644 index 0000000000000..e18b54d6e117e --- /dev/null +++ b/docs/operator-manual/argocd-tls-certs-cm-yaml.md @@ -0,0 +1,7 @@ +# argocd-tls-certs-cm.yaml example + +An example of an argocd-tls-certs-cm.yaml file: + +```yaml +{!docs/operator-manual/argocd-tls-certs-cm.yaml!} +``` diff --git a/docs/operator-manual/cluster-management.md b/docs/operator-manual/cluster-management.md new file mode 100644 index 0000000000000..bd0d28e08dba7 --- /dev/null +++ b/docs/operator-manual/cluster-management.md @@ -0,0 +1,23 @@ +# Cluster Management + +This guide is for operators looking to manage clusters on the CLI. If you want to use Kubernetes resources for this, check out [Declarative Setup](./declarative-setup.md#clusters). + +Not all commands are described here, see the [argocd cluster Command Reference](../user-guide/commands/argocd_cluster.md) for all available commands. + +## Adding a cluster + +Run `argocd cluster add context-name`. + +If you're unsure about the context names, run `kubectl config get-contexts` to get them all listed. + +This will connect to the cluster and install the necessary resources for ArgoCD to connect to it. +Note that you will need privileged access to the cluster. + +## Removing a cluster + +Run `argocd cluster rm context-name`. + +This removes the cluster with the specified name. + +!!!note "in-cluster cannot be removed" + The `in-cluster` cluster cannot be removed with this. If you want to disable the `in-cluster` configuration, you need to update your `argocd-cm` ConfigMap. Set [`cluster.inClusterEnabled`](./argocd-cm-yaml.md) to `"false"` diff --git a/docs/operator-manual/config-management-plugins.md b/docs/operator-manual/config-management-plugins.md index d5492891d66bf..7c86075ff2f7f 100644 --- a/docs/operator-manual/config-management-plugins.md +++ b/docs/operator-manual/config-management-plugins.md @@ -1,3 +1,4 @@ + # Config Management Plugins Argo CD's "native" config management tools are Helm, Jsonnet, and Kustomize. If you want to use a different config @@ -18,53 +19,11 @@ The following sections will describe how to create, install, and use plugins. Ch ## Installing a config management plugin -There are two ways to install a Config Management Plugin: - -1. Add the plugin config to the Argo CD ConfigMap (**this method is deprecated and will be removed in a future - version**). The repo-server container will run your plugin's commands. This is a good option for a simple plugin that - requires only a few lines of code that fit nicely in the Argo CD ConfigMap. -2. Add the plugin as a sidecar to the repo-server Pod. - This is a good option for a more complex plugin that would clutter the Argo CD ConfigMap. A copy of the repository is - sent to the sidecar container as a tarball and processed individually per application, which makes it a good option - for [concurrent processing of monorepos](high_availability.md#enable-concurrent-processing). - -### Option 1: Configure plugins via Argo CD configmap (deprecated) - -The following changes are required to configure a new plugin: - -1. Make sure required binaries are available in `argocd-repo-server` pod. The binaries can be added via volume mounts or - using a custom image (see [custom_tools](custom_tools.md) for examples of both). -2. Register a new plugin in `argocd-cm` ConfigMap: - - :::yaml - data: - configManagementPlugins: | - - name: pluginName - init: # Optional command to initialize application source directory - command: ["sample command"] - args: ["sample args"] - generate: # Command to generate manifests YAML - command: ["sample command"] - args: ["sample args"] - lockRepo: true # Defaults to false. See below. - - The `generate` command must print a valid YAML or JSON stream to stdout. Both `init` and `generate` commands are executed inside the application source directory or in `path` when specified for the app. - -3. [Create an Application which uses your new CMP](#using-a-cmp). - -More CMP examples are available in [argocd-example-apps](https://github.com/argoproj/argocd-example-apps/tree/master/plugins). - -!!!note "Repository locking" - If your plugin makes use of `git` (e.g. `git crypt`), it is advised to set - `lockRepo` to `true` so that your plugin will have exclusive access to the - repository at the time it is executed. Otherwise, two applications synced - at the same time may result in a race condition and sync failure. - -### Option 2: Configure plugin via sidecar +### Sidecar plugin An operator can configure a plugin tool via a sidecar to repo-server. The following changes are required to configure a new plugin: -#### 1. Write the plugin configuration file +#### Write the plugin configuration file Plugins will be configured via a ConfigManagementPlugin manifest located inside the plugin container. @@ -75,6 +34,8 @@ metadata: # The name of the plugin must be unique within a given Argo CD instance. name: my-plugin spec: + # The version of your plugin. Optional. If specified, the Application's spec.source.plugin.name field + # must be -. version: v1.0 # The init command runs in the Application source directory at the beginning of each manifest generation. The init # command can output anything. A non-zero status code will fail manifest generation. @@ -84,7 +45,8 @@ spec: command: [sh] args: [-c, 'echo "Initializing..."'] # The generate command runs in the Application source directory each time manifests are generated. Standard output - # must be ONLY valid YAML manifests. A non-zero exit code will fail manifest generation. + # must be ONLY valid Kubernetes Objects in either YAML or JSON. A non-zero exit code will fail manifest generation. + # To write log messages from the command, write them to stderr, it will always be displayed. # Error output will be sent to the UI, so avoid printing sensitive information (such as secrets). generate: command: [sh, -c] @@ -92,12 +54,13 @@ spec: - | echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$ARGOCD_ENV_FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}" # The discovery config is applied to a repository. If every configured discovery tool matches, then the plugin may be - # used to generate manifests for Applications using the repository. + # used to generate manifests for Applications using the repository. If the discovery config is omitted then the plugin + # will not match any application but can still be invoked explicitly by specifying the plugin name in the app spec. # Only one of fileName, find.glob, or find.command should be specified. If multiple are specified then only the # first (in that order) is evaluated. discover: - # fileName is a glob pattern (https://pkg.go.dev/path/filepath#Glob) that is applied to the repository's root - # directory (not the Application source directory). If there is a match, this plugin may be used for the repository. + # fileName is a glob pattern (https://pkg.go.dev/path/filepath#Glob) that is applied to the Application's source + # directory. If there is a match, this plugin may be used for the Application. fileName: "./subdir/s*.yaml" find: # This does the same thing as fileName, but it supports double-start (nested directory) glob patterns. @@ -146,13 +109,17 @@ spec: # The command is run in an Application's source directory. Standard output must be JSON matching the schema of the # static parameter announcements list. command: [echo, '[{"name": "example-param", "string": "default-string-value"}]'] + + # If set to `true` then the plugin receives repository files with original file mode. Dangerous since the repository + # might have executable files. Set to true only if you trust the CMP plugin authors. + preserveFileMode: false ``` !!! note While the ConfigManagementPlugin _looks like_ a Kubernetes object, it is not actually a custom resource. It only follows kubernetes-style spec conventions. -The `generate` command must print a valid YAML stream to stdout. Both `init` and `generate` commands are executed inside the application source directory. +The `generate` command must print a valid Kubernetes YAML or JSON object stream to stdout. Both `init` and `generate` commands are executed inside the application source directory. The `discover.fileName` is used as [glob](https://pkg.go.dev/path/filepath#Glob) pattern to determine whether an application repository is supported by the plugin or not. @@ -167,7 +134,7 @@ If `discover.fileName` is not provided, the `discover.find.command` is executed application repository is supported by the plugin or not. The `find` command should return a non-error exit code and produce output to stdout when the application source type is supported. -#### 2. Place the plugin configuration file in the sidecar +#### Place the plugin configuration file in the sidecar Argo CD expects the plugin configuration file to be located at `/home/argocd/cmp-server/config/plugin.yaml` in the sidecar. @@ -202,7 +169,7 @@ data: fileName: "./subdir/s*.yaml" ``` -#### 3. Register the plugin sidecar +#### Register the plugin sidecar To install a plugin, patch argocd-repo-server to run the plugin container as a sidecar, with argocd-cmp-server as its entrypoint. You can use either off-the-shelf or custom-built plugin image as sidecar image. For example: @@ -245,99 +212,76 @@ volumes: Plugin commands have access to -1. The system environment variables (of the repo-server container for argocd-cm plugins or of the sidecar for sidecar plugins) +1. The system environment variables of the sidecar 2. [Standard build environment variables](../user-guide/build-environment.md) 3. Variables in the Application spec (References to system and build variables will get interpolated in the variables' values): -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -spec: - source: - plugin: - env: - - name: FOO - value: bar - - name: REV - value: test-$ARGOCD_APP_REVISION -``` - -!!! note - The `discover.command` command only has access to the above environment starting with v2.4. - -Before reaching the `init.command`, `generate.command`, and `discover.command` commands, Argo CD prefixes all -user-supplied environment variables (#3 above) with `ARGOCD_ENV_`. This prevents users from directly setting -potentially-sensitive environment variables. - -If your plugin was written before 2.4 and depends on user-supplied environment variables, then you will need to update -your plugin's behavior to work with 2.4. If you use a third-party plugin, make sure they explicitly advertise support -for 2.4. - -4. (Starting in v2.4) Parameters in the Application spec: - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -spec: - source: - plugin: - parameters: - - name: values-files - array: [values-dev.yaml] - - name: helm-parameters - map: - image.tag: v1.2.3 -``` - -The parameters are available as JSON in the `ARGOCD_APP_PARAMETERS` environment variable. The example above would -produce this JSON: - -```json -[{"name": "values-files", "array": ["values-dev.yaml"]}, {"name": "helm-parameters", "map": {"image.tag": "v1.2.3"}}] -``` - -!!! note - Parameter announcements, even if they specify defaults, are _not_ sent to the plugin in `ARGOCD_APP_PARAMETERS`. - Only parameters explicitly set in the Application spec are sent to the plugin. It is up to the plugin to apply - the same defaults as the ones announced to the UI. - -The same parameters are also available as individual environment variables. The names of the environment variables -follows this convention: - -```yaml - - name: some-string-param - string: some-string-value - # PARAM_SOME_STRING_PARAM=some-string-value + apiVersion: argoproj.io/v1alpha1 + kind: Application + spec: + source: + plugin: + env: + - name: FOO + value: bar + - name: REV + value: test-$ARGOCD_APP_REVISION + + Before reaching the `init.command`, `generate.command`, and `discover.find.command` commands, Argo CD prefixes all + user-supplied environment variables (#3 above) with `ARGOCD_ENV_`. This prevents users from directly setting + potentially-sensitive environment variables. + +4. Parameters in the Application spec: + + apiVersion: argoproj.io/v1alpha1 + kind: Application + spec: + source: + plugin: + parameters: + - name: values-files + array: [values-dev.yaml] + - name: helm-parameters + map: + image.tag: v1.2.3 - - name: some-array-param - value: [item1, item2] - # PARAM_SOME_ARRAY_PARAM_0=item1 - # PARAM_SOME_ARRAY_PARAM_1=item2 + The parameters are available as JSON in the `ARGOCD_APP_PARAMETERS` environment variable. The example above would + produce this JSON: - - name: some-map-param - map: - image.tag: v1.2.3 - # PARAM_SOME_MAP_PARAM_IMAGE_TAG=v1.2.3 -``` - -!!! warning - Sanitize/escape user input. As part of Argo CD's manifest generation system, config management plugins are treated with a level of trust. Be + [{"name": "values-files", "array": ["values-dev.yaml"]}, {"name": "helm-parameters", "map": {"image.tag": "v1.2.3"}}] + + !!! note + Parameter announcements, even if they specify defaults, are _not_ sent to the plugin in `ARGOCD_APP_PARAMETERS`. + Only parameters explicitly set in the Application spec are sent to the plugin. It is up to the plugin to apply + the same defaults as the ones announced to the UI. + + The same parameters are also available as individual environment variables. The names of the environment variables + follows this convention: + + - name: some-string-param + string: some-string-value + # PARAM_SOME_STRING_PARAM=some-string-value + + - name: some-array-param + value: [item1, item2] + # PARAM_SOME_ARRAY_PARAM_0=item1 + # PARAM_SOME_ARRAY_PARAM_1=item2 + + - name: some-map-param + map: + image.tag: v1.2.3 + # PARAM_SOME_MAP_PARAM_IMAGE_TAG=v1.2.3 + +!!! warning "Sanitize/escape user input" + As part of Argo CD's manifest generation system, config management plugins are treated with a level of trust. Be sure to escape user input in your plugin to prevent malicious input from causing unwanted behavior. - ## Using a config management plugin with an Application -If your CMP is defined in the `argocd-cm` ConfigMap, you can create a new Application using the CLI. Replace -`` with the name configured in `argocd-cm`. - -```bash -argocd app create --config-management-plugin -``` - -If your CMP is defined as a sidecar, you must manually define the Application manifest. You may leave the `name` field +You may leave the `name` field empty in the `plugin` section for the plugin to be automatically matched with the Application based on its discovery rules. If you do mention the name make sure it is either `-` if version is mentioned in the `ConfigManagementPlugin` spec or else just ``. When name is explicitly -specified only that particular plugin will be used iff it's discovery pattern/command matches the provided application repo. +specified only that particular plugin will be used iff its discovery pattern/command matches the provided application repo. ```yaml apiVersion: argoproj.io/v1alpha1 @@ -352,7 +296,6 @@ spec: targetRevision: HEAD path: guestbook plugin: - # For either argocd-cm- or sidecar-installed CMPs, you can pass environment variables to the CMP. env: - name: FOO value: bar @@ -365,7 +308,7 @@ If you don't need to set any environment variables, you can set an empty plugin ``` !!! important - If your sidecar CMP command runs too long, the command will be killed, and the UI will show an error. The CMP server + If your CMP command runs too long, the command will be killed, and the UI will show an error. The CMP server respects the timeouts set by the `server.repo.server.timeout.seconds` and `controller.repo.server.timeout.seconds` items in `argocd-cm`. Increase their values from the default of 60s. @@ -378,6 +321,8 @@ If you don't need to set any environment variables, you can set an empty plugin plugin configured through the `argocd-cm` ConfigMap to a sidecar, make sure to update the plugin name to either `-` if version was mentioned in the `ConfigManagementPlugin` spec or else just use ``. You can also remove the name altogether and let the automatic discovery to identify the plugin. +!!! note + If a CMP renders blank manfiests, and `prune` is set to `true`, Argo CD will automatically remove resources. CMP plugin authors should ensure errors are part of the exit code. Commonly something like `kustomize build . | cat` won't pass errors because of the pipe. Consider setting `set -o pipefail` so anything piped will pass errors on failure. ## Debugging a CMP @@ -390,6 +335,14 @@ If you are actively developing a sidecar-installed CMP, keep a few things in min image. If you're using a different, static tag, set `imagePullPolicy: Always` on the CMP's sidecar container. 3. CMP errors are cached by the repo-server in Redis. Restarting the repo-server Pod will not clear the cache. Always do a "Hard Refresh" when actively developing a CMP so you have the latest output. +4. Verify your sidecar has started properly by viewing the Pod and seeing that two containers are running `kubectl get pod -l app.kubernetes.io/component=repo-server -n argocd` +5. Write log message to stderr and set the `--loglevel=info` flag in the sidecar. This will print everything written to stderr, even on successfull command execution. + + +### Other Common Errors +| Error Message | Cause | +| -- | -- | +| `no matches for kind "ConfigManagementPlugin" in version "argoproj.io/v1alpha1"` | The `ConfigManagementPlugin` CRD was deprecated in Argo CD 2.4 and removed in 2.8. This error means you've tried to put the configuration for your plugin directly into Kubernetes as a CRD. Refer to this [section of documentation](#write-the-plugin-configuration-file) for how to write the plugin configuration file and place it properly in the sidecar. | ## Plugin tar stream exclusions @@ -408,12 +361,11 @@ them with semicolons. ## Migrating from argocd-cm plugins -Installing plugins by modifying the argocd-cm ConfigMap is deprecated as of v2.4. Support will be completely removed in -a future release. +Installing plugins by modifying the argocd-cm ConfigMap is deprecated as of v2.4 and has been completely removed starting in v2.8. -The following will show how to convert an argocd-cm plugin to a sidecar plugin. +CMP plugins work by adding a sidecar to `argocd-repo-server` along with a configuration in that sidecar located at `/home/argocd/cmp-server/config/plugin.yaml`. A argocd-cm plugin can be easily converted with the following steps. -### 1. Convert the ConfigMap entry into a config file +### Convert the ConfigMap entry into a config file First, copy the plugin's configuration into its own YAML file. Take for example the following ConfigMap entry: @@ -424,7 +376,7 @@ data: init: # Optional command to initialize application source directory command: ["sample command"] args: ["sample args"] - generate: # Command to generate manifests YAML + generate: # Command to generate Kubernetes Objects in either YAML or JSON command: ["sample command"] args: ["sample args"] lockRepo: true # Defaults to false. See below. @@ -441,7 +393,7 @@ spec: init: # Optional command to initialize application source directory command: ["sample command"] args: ["sample args"] - generate: # Command to generate manifests YAML + generate: # Command to generate Kubernetes Objects in either YAML or JSON command: ["sample command"] args: ["sample args"] ``` @@ -450,17 +402,44 @@ spec: The `lockRepo` key is not relevant for sidecar plugins, because sidecar plugins do not share a single source repo directory when generating manifests. -### 2. Write discovery rules for your plugin +Next, we need to decide how this yaml is going to be added to the sidecar. We can either bake the yaml directly into the image, or we can mount it from a ConfigMap. -Sidecar plugins use discovery rules instead of a plugin name to match Applications to plugins. +If using a ConfigMap, our example would look like this: -Write rules applicable to your plugin [using the instructions above](#1-write-the-plugin-configuration-file) and add -them to your configuration file. +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: pluginName + namespace: argocd +data: + pluginName.yaml: | + apiVersion: argoproj.io/v1alpha1 + kind: ConfigManagementPlugin + metadata: + name: pluginName + spec: + init: # Optional command to initialize application source directory + command: ["sample command"] + args: ["sample args"] + generate: # Command to generate Kubernetes Objects in either YAML or JSON + command: ["sample command"] + args: ["sample args"] +``` -!!! note - After installing your sidecar plugin, you may remove the `name` field from the plugin config in your - Application specs for auto-discovery or update the name to `-` - if version was mentioned in the `ConfigManagementPlugin` spec or else just use ``. For example: +Then this would be mounted in our plugin sidecar. + +### Write discovery rules for your plugin + +Sidecar plugins can use either discovery rules or a plugin name to match Applications to plugins. If the discovery rule is omitted +then you have to explicitly specify the plugin by name in the app spec or else that particular plugin will not match any app. + +If you want to use discovery instead of the plugin name to match applications to your plugin, write rules applicable to +your plugin [using the instructions above](#1-write-the-plugin-configuration-file) and add them to your configuration +file. + +To use the name instead of discovery, update the name in your application manifest to `-` +if version was mentioned in the `ConfigManagementPlugin` spec or else just use ``. For example: ```yaml apiVersion: argoproj.io/v1alpha1 @@ -473,18 +452,44 @@ spec: name: pluginName # Delete this for auto-discovery (and set `plugin: {}` if `name` was the only value) or use proper sidecar plugin name ``` -### 3. Make sure the plugin has access to the tools it needs +### Make sure the plugin has access to the tools it needs Plugins configured with argocd-cm ran on the Argo CD image. This gave it access to all the tools installed on that image by default (see the [Dockerfile](https://github.com/argoproj/argo-cd/blob/master/Dockerfile) for base image and installed tools). -You can either use a stock image (like busybox) or design your own base image with the tools your plugin needs. For -security, avoid using image with more binaries installed than what your plugin actually needs. +You can either use a stock image (like busybox, or alpine/k8s) or design your own base image with the tools your plugin needs. For +security, avoid using images with more binaries installed than what your plugin actually needs. -### 4. Test the plugin +### Test the plugin After installing the plugin as a sidecar [according to the directions above](#installing-a-config-management-plugin), test it out on a few Applications before migrating all of them to the sidecar plugin. Once tests have checked out, remove the plugin entry from your argocd-cm ConfigMap. + +### Additional Settings + +#### Preserve repository files mode + +By default, config management plugin receives source repository files with reset file mode. This is done for security +reasons. If you want to preserve original file mode, you can set `preserveFileMode` to `true` in the plugin spec: + +!!! warning + Make sure you trust the plugin you are using. If you set `preserveFileMode` to `true` then the plugin might receive + files with executable permissions which can be a security risk. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ConfigManagementPlugin +metadata: + name: pluginName +spec: + init: + command: ["sample command"] + args: ["sample args"] + generate: + command: ["sample command"] + args: ["sample args"] + preserveFileMode: true +``` diff --git a/docs/operator-manual/core.md b/docs/operator-manual/core.md new file mode 100644 index 0000000000000..3d6e0a322c423 --- /dev/null +++ b/docs/operator-manual/core.md @@ -0,0 +1,99 @@ +# Argo CD Core + +## Introduction + +Argo CD Core is a different installation that runs Argo CD in headless +mode. With this installation, you will have a fully functional GitOps +engine capable of getting the desired state from Git repositories and +applying it in Kubernetes. + +The following groups of features won't be available in this +installation: + +- Argo CD RBAC model +- Argo CD API +- OIDC based authentication + +The following features will be partially available (see the +[usage](#using) section below for more details): + +- Argo CD Web UI +- Argo CD CLI +- Multi-tenancy (strictly GitOps based on git push permissions) + +A few use-cases that justify running Argo CD Core are: + +- As a cluster admin, I want to rely on Kubernetes RBAC only. +- As a devops engineer, I don't want to learn a new API or depend on + another CLI to automate my deployments. I want to rely on the + Kubernetes API only. +- As a cluster admin, I don't want to provide Argo CD UI or Argo CD + CLI to developers. + +## Architecture + +Because Argo CD is designed with a component based architecture in +mind, it is possible to have a more minimalist installation. In this +case fewer components are installed and yet the main GitOps +functionality remains operational. + +In the diagram below, the Core box, shows the components that will be +installed while opting for Argo CD Core: + +![Argo CD Core](../assets/argocd-core-components.png) + +Note that even if the Argo CD controller can run without Redis, it +isn't recommended. The Argo CD controller uses Redis as an important +caching mechanism reducing the load on Kube API and in Git. For this +reason, Redis is also included in this installation method. + +## Installing + +Argo CD Core can be installed by applying a single manifest file that +contains all the required resources. + +Example: + +``` +export ARGOCD_VERSION= +kubectl create namespace argocd +kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/$ARGOCD_VERSION/manifests/core-install.yaml +``` + +## Using + +Once Argo CD Core is installed, users will be able to interact with it +by relying on GitOps. The available Kubernetes resources will be the +`Application` and the `ApplicationSet` CRDs. By using those resources, +users will be able to deploy and manage applications in Kubernetes. + +It is still possible to use Argo CD CLI even when running Argo CD +Core. In this case, the CLI will spawn a local API server process that +will be used to handle the CLI command. Once the command is concluded, +the local API Server process will also be terminated. This happens +transparently for the user with no additional command required. Note +that Argo CD Core will rely only on Kubernetes RBAC and the user (or +the process) invoking the CLI needs to have access to the Argo CD +namespace with the proper permission in the `Application` and +`ApplicationSet` resources for executing a given command. + +To use Argo CD CLI in core mode, it is required to pass the `--core` +flag with the `login` subcommand. + +Example: + +```bash +kubectl config set-context --current --namespace=argocd # change current kube context to argocd namespace +argocd login --core +``` + +Similarly, users can also run the Web UI locally if they prefer to +interact with Argo CD using this method. The Web UI can be started +locally by running the following command: + +``` +argocd admin dashboard -n argocd +``` + +Argo CD Web UI will be available at `http://localhost:8080` + diff --git a/docs/operator-manual/custom-styles.md b/docs/operator-manual/custom-styles.md index 21fa79efeeb2f..6f68d5e23b128 100644 --- a/docs/operator-manual/custom-styles.md +++ b/docs/operator-manual/custom-styles.md @@ -21,7 +21,7 @@ metadata: ... name: argocd-cm data: - ui.cssurl: "https://www.myhost.com/my-styles.css" + ui.cssurl: "https://www.example.com/my-styles.css" ``` ## Adding Styles Via Volume Mounts @@ -56,7 +56,7 @@ metadata: name: argocd-styles-cm data: my-styles.css: | - .nav-bar { + .sidebar { background: linear-gradient(to bottom, #999, #777, #333, #222, #111); } ``` @@ -100,7 +100,7 @@ experience, you may wish to build a separate project using the [Argo CD UI dev s ## Banners -Argo CD can optionally display a banner that can be used to notify your users of upcoming maintenance and operational changes. This feature can be enabled by specifying the banner message using the `ui.bannercontent` field in the `argocd-cm` ConfigMap and Argo CD will display this message at the top of every UI page. You can optionally add a link to this message by setting `ui.bannerurl`. You can also make the banner sticky (permanent) by setting `ui.bannerpermanent` to `true` and change it's position to the bottom by using `ui.bannerposition: "bottom"` +Argo CD can optionally display a banner that can be used to notify your users of upcoming maintenance and operational changes. This feature can be enabled by specifying the banner message using the `ui.bannercontent` field in the `argocd-cm` ConfigMap and Argo CD will display this message at the top of every UI page. You can optionally add a link to this message by setting `ui.bannerurl`. You can also make the banner sticky (permanent) by setting `ui.bannerpermanent` to true and change its position to "both" or "bottom" by using `ui.bannerposition: "both"`, allowing the banner to display on both the top and bottom, or `ui.bannerposition: "bottom"` to display it exclusively at the bottom. ### argocd-cm ```yaml diff --git a/docs/operator-manual/declarative-setup.md b/docs/operator-manual/declarative-setup.md index 8e3ff49fbda28..3830cb610796a 100644 --- a/docs/operator-manual/declarative-setup.md +++ b/docs/operator-manual/declarative-setup.md @@ -8,16 +8,16 @@ All resources, including `Application` and `AppProject` specs, have to be instal ### Atomic configuration -| Sample File | Resource Name | Kind | Description | -|-------------|---------------|------|-------------| -| [`argocd-cm.yaml`](argocd-cm.yaml) | argocd-cm | ConfigMap | General Argo CD configuration | -| [`argocd-repositories.yaml`](argocd-repositories.yaml) | my-private-repo / istio-helm-repo / private-helm-repo / private-repo | Secrets | Sample repository connection details | -| [`argocd-repo-creds.yaml`](argocd-repo-creds.yaml) | argoproj-https-creds / argoproj-ssh-creds / github-creds / github-enterprise-creds | Secrets | Sample repository credential templates | -| [`argocd-cmd-params-cm.yaml`](argocd-cmd-params-cm.yaml) | argocd-cmd-params-cm | ConfigMap | Argo CD env variables configuration | -| [`argocd-secret.yaml`](argocd-secret.yaml) | argocd-secret | Secret | User Passwords, Certificates (deprecated), Signing Key, Dex secrets, Webhook secrets | -| [`argocd-rbac-cm.yaml`](argocd-rbac-cm.yaml) | argocd-rbac-cm | ConfigMap | RBAC Configuration | -| [`argocd-tls-certs-cm.yaml`](argocd-tls-certs-cm.yaml) | argocd-tls-certs-cm | ConfigMap | Custom TLS certificates for connecting Git repositories via HTTPS (v1.2 and later) | -| [`argocd-ssh-known-hosts-cm.yaml`](argocd-ssh-known-hosts-cm.yaml) | argocd-ssh-known-hosts-cm | ConfigMap | SSH known hosts data for connecting Git repositories via SSH (v1.2 and later) | +| Sample File | Resource Name | Kind | Description | +|-----------------------------------------------------------------------|------------------------------------------------------------------------------------|-----------|--------------------------------------------------------------------------------------| +| [`argocd-cm.yaml`](argocd-cm-yaml.md) | argocd-cm | ConfigMap | General Argo CD configuration | +| [`argocd-repositories.yaml`](argocd-repositories-yaml.md) | my-private-repo / istio-helm-repo / private-helm-repo / private-repo | Secrets | Sample repository connection details | +| [`argocd-repo-creds.yaml`](argocd-repo-creds-yaml.md) | argoproj-https-creds / argoproj-ssh-creds / github-creds / github-enterprise-creds | Secrets | Sample repository credential templates | +| [`argocd-cmd-params-cm.yaml`](argocd-cmd-params-cm-yaml.md) | argocd-cmd-params-cm | ConfigMap | Argo CD env variables configuration | +| [`argocd-secret.yaml`](argocd-secret-yaml.md) | argocd-secret | Secret | User Passwords, Certificates (deprecated), Signing Key, Dex secrets, Webhook secrets | +| [`argocd-rbac-cm.yaml`](argocd-rbac-cm-yaml.md) | argocd-rbac-cm | ConfigMap | RBAC Configuration | +| [`argocd-tls-certs-cm.yaml`](argocd-tls-certs-cm-yaml.md) | argocd-tls-certs-cm | ConfigMap | Custom TLS certificates for connecting Git repositories via HTTPS (v1.2 and later) | +| [`argocd-ssh-known-hosts-cm.yaml`](argocd-ssh-known-hosts-cm-yaml.md) | argocd-ssh-known-hosts-cm | ConfigMap | SSH known hosts data for connecting Git repositories via SSH (v1.2 and later) | For each specific kind of ConfigMap and Secret resource, there is only a single supported resource name (as listed in the above table) - if you need to merge things you need to do it before creating them. @@ -26,11 +26,11 @@ For each specific kind of ConfigMap and Secret resource, there is only a single ### Multiple configuration objects -| Sample File | Kind | Description | -|-------------|------|-------------| -| [`application.yaml`](application.yaml) | Application | Example application spec | -| [`project.yaml`](project.yaml) | AppProject | Example project spec | -| - | Secret | Repository credentials | +| Sample File | Kind | Description | +|------------------------------------------------------------------|-------------|--------------------------| +| [`application.yaml`](../user-guide/application-specification.md) | Application | Example application spec | +| [`project.yaml`](./project-specification.md) | AppProject | Example project spec | +| - | Secret | Repository credentials | For `Application` and `AppProject` resources, the name of the resource equals the name of the application or project within Argo CD. This also means that application and project names are unique within a given Argo CD installation - you cannot have the same application name for two different applications. @@ -77,7 +77,7 @@ spec: ``` !!! warning - By default, deleting an application will not perform a cascade delete, which would delete its resources. You must add the finalizer if you want this behaviour - which you may well not want. + Without the `resources-finalizer.argocd.argoproj.io` finalizer, deleting an application will not delete the resources it manages. To perform a cascading delete, you must add the finalizer. See [App Deletion](../user-guide/app_deletion.md#about-the-deletion-finalizer). ```yaml metadata: @@ -98,7 +98,7 @@ The AppProject CRD is the Kubernetes resource object representing a logical grou It is defined by the following key pieces of information: * `sourceRepos` reference to the repositories that applications within the project can pull manifests from. -* `destinations` reference to clusters and namespaces that applications within the project can deploy into (don't use the `name` field, only the `server` field is matched). +* `destinations` reference to clusters and namespaces that applications within the project can deploy into. * `roles` list of entities with definitions of their access to resources within the project. !!!warning "Projects which can deploy to the Argo CD namespace grant admin access" @@ -209,7 +209,7 @@ metadata: argocd.argoproj.io/secret-type: repository stringData: type: git - url: git@github.com:argoproj/my-private-repository + url: git@github.com:argoproj/my-private-repository.git sshPrivateKey: | -----BEGIN OPENSSH PRIVATE KEY----- ... @@ -266,7 +266,7 @@ metadata: argocd.argoproj.io/secret-type: repository stringData: type: git - repo: https://source.developers.google.com/p/my-google-project/r/my-repo + url: https://source.developers.google.com/p/my-google-project/r/my-repo gcpServiceAccountKey: | { "type": "service_account", @@ -416,30 +416,51 @@ data: ### SSH known host public keys -If you are connecting repositories via SSH, Argo CD will need to know the SSH known hosts public key of the repository servers. You can manage the SSH known hosts data in the ConfigMap named `argocd-ssh-known-hosts-cm`. This ConfigMap contains a single key/value pair, with `ssh_known_hosts` as the key and the actual public keys of the SSH servers as data. As opposed to TLS configuration, the public key(s) of each single repository server Argo CD will connect via SSH must be configured, otherwise the connections to the repository will fail. There is no fallback. The data can be copied from any existing `ssh_known_hosts` file, or from the output of the `ssh-keyscan` utility. The basic format is ` `, one entry per line. +If you are configuring repositories to use SSH, Argo CD will need to know their SSH public keys. In order for Argo CD to connect via SSH the public key(s) for each repository server must be pre-configured in Argo CD (unlike TLS configuration), otherwise the connections to the repository will fail. + +You can manage the SSH known hosts data in the `argocd-ssh-known-hosts-cm` ConfigMap. This ConfigMap contains a single entry, `ssh_known_hosts`, with the public keys of the SSH servers as its value. The value can be filled in from any existing `ssh_known_hosts` file, or from the output of the `ssh-keyscan` utility (which is part of OpenSSH's client package). The basic format is ` `, one entry per line. + +Here is an example of running `ssh-keyscan`: +```bash +$ for host in bitbucket.org github.com gitlab.com ssh.dev.azure.com vs-ssh.visualstudio.com ; do ssh-keyscan $host 2> /dev/null ; done +bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQeJzhupRu0u0cdegZIa8e86EG2qOCsIsD1Xw0xSeiPDlCr7kq97NLmMbpKTX6Esc30NuoqEEHCuc7yWtwp8dI76EEEB1VqY9QJq6vk+aySyboD5QF61I/1WeTwu+deCbgKMGbUijeXhtfbxSxm6JwGrXrhBdofTsbKRUsrN1WoNgUa8uqN1Vx6WAJw1JHPhglEGGHea6QICwJOAr/6mrui/oB7pkaWKHj3z7d1IC4KWLtY47elvjbaTlkN04Kc/5LFEirorGYVbt15kAUlqGM65pk6ZBxtaO3+30LVlORZkxOh+LKL/BvbZ/iRNhItLqNyieoQj/uh/7Iv4uyH/cV/0b4WDSd3DptigWq84lJubb9t/DnZlrJazxyDCulTmKdOR7vs9gMTo+uoIrPSb8ScTtvw65+odKAlBj59dhnVp9zd7QUojOpXlL62Aw56U4oO+FALuevvMjiWeavKhJqlR7i5n9srYcrNV7ttmDw7kf/97P5zauIhxcjX+xHv4M= +github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl +github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= +github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= +gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= +gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf +gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 +ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H +vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H +``` -An example ConfigMap object: +Here is an example `ConfigMap` object using the output from `ssh-keyscan` above: ```yaml apiVersion: v1 kind: ConfigMap metadata: - name: argocd-ssh-known-hosts-cm - namespace: argocd labels: - app.kubernetes.io/name: argocd-cm + app.kubernetes.io/name: argocd-ssh-known-hosts-cm app.kubernetes.io/part-of: argocd + name: argocd-ssh-known-hosts-cm data: ssh_known_hosts: | - bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw== - github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + # This file was automatically generated by hack/update-ssh-known-hosts.sh. DO NOT EDIT + [ssh.github.com]:443 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + [ssh.github.com]:443 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + [ssh.github.com]:443 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + bitbucket.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIQmuzMBuKdWeF4+a2sjSSpBK0iqitSQ+5BM9KhpexuGt20JpTVM7u5BDZngncgrqDMbWdxMWWOGtZ9UgbqgZE= + bitbucket.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIazEu89wgQZ4bqs3d63QSMzYVa0MuJ2e2gKTKqu+UUO + bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQeJzhupRu0u0cdegZIa8e86EG2qOCsIsD1Xw0xSeiPDlCr7kq97NLmMbpKTX6Esc30NuoqEEHCuc7yWtwp8dI76EEEB1VqY9QJq6vk+aySyboD5QF61I/1WeTwu+deCbgKMGbUijeXhtfbxSxm6JwGrXrhBdofTsbKRUsrN1WoNgUa8uqN1Vx6WAJw1JHPhglEGGHea6QICwJOAr/6mrui/oB7pkaWKHj3z7d1IC4KWLtY47elvjbaTlkN04Kc/5LFEirorGYVbt15kAUlqGM65pk6ZBxtaO3+30LVlORZkxOh+LKL/BvbZ/iRNhItLqNyieoQj/uh/7Iv4uyH/cV/0b4WDSd3DptigWq84lJubb9t/DnZlrJazxyDCulTmKdOR7vs9gMTo+uoIrPSb8ScTtvw65+odKAlBj59dhnVp9zd7QUojOpXlL62Aw56U4oO+FALuevvMjiWeavKhJqlR7i5n9srYcrNV7ttmDw7kf/97P5zauIhxcjX+xHv4M= + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H - github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= - github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl ``` !!! note @@ -469,7 +490,7 @@ stringData: ### Legacy behaviour -In Argo CD version 2.0 and earlier, repositories where stored as part of the `argocd-cm` config map. For +In Argo CD version 2.0 and earlier, repositories were stored as part of the `argocd-cm` config map. For backward-compatibility, Argo CD will still honor repositories in the config map, but this style of repository configuration is deprecated and support for it will be removed in a future version. @@ -515,6 +536,7 @@ The secret data must include following fields: * `server` - cluster api server url * `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. * `clusterResources` - optional boolean string (`"true"` or `"false"`) determining whether Argo CD can manage cluster-level resources on this cluster. This setting is used only if the list of managed namespaces is not empty. +* `project` - optional string to designate this as a project-scoped cluster. * `config` - JSON representation of following data structure: ```yaml @@ -527,6 +549,7 @@ bearerToken: string awsAuthConfig: clusterName: string roleARN: string + profile: string # Configure external command to supply client credentials # See https://godoc.org/k8s.io/client-go/tools/clientcmd/api#ExecConfig execProviderConfig: @@ -568,8 +591,8 @@ metadata: argocd.argoproj.io/secret-type: cluster type: Opaque stringData: - name: mycluster.com - server: https://mycluster.com + name: mycluster.example.com + server: https://mycluster.example.com config: | { "bearerToken": "", @@ -580,6 +603,271 @@ stringData: } ``` +### EKS + +EKS cluster secret example using argocd-k8s-auth and [IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html): + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: mycluster-secret + labels: + argocd.argoproj.io/secret-type: cluster +type: Opaque +stringData: + name: "mycluster.example.com" + server: "https://mycluster.example.com" + config: | + { + "awsAuthConfig": { + "clusterName": "my-eks-cluster-name", + "roleARN": "arn:aws:iam:::role/" + }, + "tlsClientConfig": { + "insecure": false, + "caData": "" + } + } +``` + +Note that you should have IRSA enabled on your EKS cluster, create an appropriate IAM role which allows it to assume +other IAM roles (whichever `roleARN`s that Argo CD needs to assume) and have an assume role policy which allows +the argocd-application-controller and argocd-server pods to assume said role via OIDC. + +Example trust relationship config for `:role/`, which +is required for Argo CD to perform actions via IAM. Ensure that the cluster has an [IAM OIDC provider configured](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html) +for it. + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam:::oidc-provider/oidc.eks..amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "oidc.eks..amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub": ["system:serviceaccount:argocd:argocd-application-controller", "system:serviceaccount:argocd:argocd-server"], + "oidc.eks..amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:aud": "sts.amazonaws.com" + } + } + } + ] +} +``` + +The Argo CD management role also needs to be allowed to assume other roles, in this case we want it to assume +`arn:aws:iam:::role/` so that it can manage the cluster mapped to that role. This can be +extended to allow assumption of multiple roles, either as an explicit array of role ARNs or by using `*` where appropriate. + +```json +{ + "Version" : "2012-10-17", + "Statement" : { + "Effect" : "Allow", + "Action" : "sts:AssumeRole", + "Resource" : [ + ":role/" + ] + } + } +``` + +Example service account configs for `argocd-application-controller` and `argocd-server`. + +!!! warning + Once the annotations have been set on the service accounts, both the application controller and server pods need to be restarted. + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + eks.amazonaws.com/role-arn: ":role/" + name: argocd-application-controller +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + eks.amazonaws.com/role-arn: ":role/" + name: argocd-server +``` + +In turn, the `roleARN` of each managed cluster needs to be added to each respective cluster's `aws-auth` config map (see +[Enabling IAM principal access to your cluster](https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html)), as +well as having an assume role policy which allows it to be assumed by the Argo CD pod role. + +Example assume role policy for a cluster which is managed by Argo CD: + +```json +{ + "Version" : "2012-10-17", + "Statement" : { + "Effect" : "Allow", + "Action" : "sts:AssumeRole", + "Principal" : { + "AWS" : ":role/" + } + } + } +``` + +Example kube-system/aws-auth configmap for your cluster managed by Argo CD: + +```yaml +apiVersion: v1 +data: + # Other groups and accounts omitted for brevity. Ensure that no other rolearns and/or groups are inadvertently removed, + # or you risk borking access to your cluster. + # + # The group name is a RoleBinding which you use to map to a [Cluster]Role. See https://kubernetes.io/docs/reference/access-authn-authz/rbac/#role-binding-examples + mapRoles: | + - "groups": + - "" + "rolearn": ":role/" + "username": "" +``` + +#### Alternative EKS Authentication Methods +In some scenarios it may not be possible to use IRSA, such as when the Argo CD cluster is running on a different cloud +provider's platform. In this case, there are two options: +1. Use `execProviderConfig` to call the AWS authentication mechanism which enables the injection of environment variables to supply credentials +2. Leverage the new AWS profile option available in Argo CD release 2.10 + +Both of these options will require the steps involving IAM and the `aws-auth` config map (defined above) to provide the +principal with access to the cluster. + +##### Using execProviderConfig with Environment Variables +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: mycluster-secret + labels: + argocd.argoproj.io/secret-type: cluster +type: Opaque +stringData: + name: mycluster + server: https://mycluster.example.com + namespaces: "my,managed,namespaces" + clusterResources: "true" + config: | + { + "execProviderConfig": { + "command": "argocd-k8s-auth", + "args": ["aws", "--cluster-name", "my-eks-cluster"], + "apiVersion": "client.authentication.k8s.io/v1beta1", + "env": { + "AWS_REGION": "xx-east-1", + "AWS_ACCESS_KEY_ID": "{{ .aws_key_id }}", + "AWS_SECRET_ACCESS_KEY": "{{ .aws_key_secret }}", + "AWS_SESSION_TOKEN": "{{ .aws_token }}" + } + }, + "tlsClientConfig": { + "insecure": false, + "caData": "{{ .cluster_cert }}" + } + } +``` + +This example assumes that the role being attached to the credentials that have been supplied, if this is not the case +the role can be appended to the `args` section like so: + +```yaml +... + "args": ["aws", "--cluster-name", "my-eks-cluster", "--roleARN", "arn:aws:iam:::role/"], +... +``` +This construct can be used in conjunction with something like the External Secrets Operator to avoid storing the keys in +plain text and additionally helps to provide a foundation for key rotation. + +##### Using An AWS Profile For Authentication +The option to use profiles, added in release 2.10, provides a method for supplying credentials while still using the +standard Argo CD EKS cluster declaration with an additional command flag that points to an AWS credentials file: +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: mycluster-secret + labels: + argocd.argoproj.io/secret-type: cluster +type: Opaque +stringData: + name: "mycluster.com" + server: "https://mycluster.com" + config: | + { + "awsAuthConfig": { + "clusterName": "my-eks-cluster-name", + "roleARN": "arn:aws:iam:::role/", + "profile": "/mount/path/to/my-profile-file" + }, + "tlsClientConfig": { + "insecure": false, + "caData": "" + } + } +``` +This will instruct ArgoCD to read the file at the provided path and use the credentials defined within to authenticate to +AWS. The profile must be mounted in order for this to work. For example, the following values can be defined in a Helm +based ArgoCD deployment: + +```yaml +controller: + extraVolumes: + - name: my-profile-volume + secret: + secretName: my-aws-profile + items: + - key: my-profile-file + path: my-profile-file + extraVolumeMounts: + - name: my-profile-mount + mountPath: /mount/path/to + readOnly: true + +server: + extraVolumes: + - name: my-profile-volume + secret: + secretName: my-aws-profile + items: + - key: my-profile-file + path: my-profile-file + extraVolumeMounts: + - name: my-profile-mount + mountPath: /mount/path/to + readOnly: true +``` + +Where the secret is defined as follows: +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: my-aws-profile +type: Opaque +stringData: + my-profile-file: | + [default] + region = + aws_access_key_id = + aws_secret_access_key = + aws_session_token = +``` + +> ⚠️ Secret mounts are updated on an interval, not real time. If rotation is a requirement ensure the token lifetime outlives the mount update interval and the rotation process doesn't immediately invalidate the existing token + + +### GKE + GKE cluster secret example using argocd-k8s-auth and [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity): ```yaml @@ -591,8 +879,8 @@ metadata: argocd.argoproj.io/secret-type: cluster type: Opaque stringData: - name: mycluster.com - server: https://mycluster.com + name: mycluster.example.com + server: https://mycluster.example.com config: | { "execProviderConfig": { @@ -609,6 +897,108 @@ stringData: Note that you must enable Workload Identity on your GKE cluster, create GCP service account with appropriate IAM role and bind it to Kubernetes service account for argocd-application-controller and argocd-server (showing Pod logs on UI). See [Use Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) and [Authenticating to the Kubernetes API server](https://cloud.google.com/kubernetes-engine/docs/how-to/api-server-authentication). +### AKS + +Azure cluster secret example using argocd-k8s-auth and [kubelogin](https://github.com/Azure/kubelogin). The option *azure* to the argocd-k8s-auth execProviderConfig encapsulates the *get-token* command for kubelogin. Depending upon which authentication flow is desired (devicecode, spn, ropc, msi, azurecli, workloadidentity), set the environment variable AAD_LOGIN_METHOD with this value. Set other appropriate environment variables depending upon which authentication flow is desired. + +|Variable Name|Description| +|-------------|-----------| +|AAD_LOGIN_METHOD|One of devicecode, spn, ropc, msi, azurecli, or workloadidentity| +|AAD_SERVICE_PRINCIPAL_CLIENT_CERTIFICATE|AAD client cert in pfx. Used in spn login| +|AAD_SERVICE_PRINCIPAL_CLIENT_ID|AAD client application ID| +|AAD_SERVICE_PRINCIPAL_CLIENT_SECRET|AAD client application secret| +|AAD_USER_PRINCIPAL_NAME|Used in the ropc flow| +|AAD_USER_PRINCIPAL_PASSWORD|Used in the ropc flow| +|AZURE_TENANT_ID|The AAD tenant ID.| +|AZURE_AUTHORITY_HOST|Used in the WorkloadIdentityLogin flow| +|AZURE_FEDERATED_TOKEN_FILE|Used in the WorkloadIdentityLogin flow| +|AZURE_CLIENT_ID|Used in the WorkloadIdentityLogin flow| + +In addition to the environment variables above, argocd-k8s-auth accepts two extra environment variables to set the AAD environment, and to set the AAD server application ID. The AAD server application ID will default to 6dae42f8-4368-4678-94ff-3960e28e3630 if not specified. See [here](https://github.com/azure/kubelogin#exec-plugin-format) for details. + +|Variable Name|Description| +|-------------|-----------| +|AAD_ENVIRONMENT_NAME|The azure environment to use, default of AzurePublicCloud| +|AAD_SERVER_APPLICATION_ID|The optional AAD server application ID, defaults to 6dae42f8-4368-4678-94ff-3960e28e3630| + +This is an example of using the [federated workload login flow](https://github.com/Azure/kubelogin#azure-workload-federated-identity-non-interactive). The federated token file needs to be mounted as a secret into argoCD, so it can be used in the flow. The location of the token file needs to be set in the environment variable AZURE_FEDERATED_TOKEN_FILE. + +If your AKS cluster utilizes the [Mutating Admission Webhook](https://azure.github.io/azure-workload-identity/docs/installation/mutating-admission-webhook.html) from the Azure Workload Identity project, follow these steps to enable the `argocd-application-controller` and `argocd-server` pods to use the federated identity: + +1. **Label the Pods**: Add the `azure.workload.identity/use: "true"` label to the `argocd-application-controller` and `argocd-server` pods. + +2. **Create Federated Identity Credential**: Generate an Azure federated identity credential for the `argocd-application-controller` and `argocd-server` service accounts. Refer to the [Federated Identity Credential](https://azure.github.io/azure-workload-identity/docs/topics/federated-identity-credential.html) documentation for detailed instructions. + +3. **Set the AZURE_CLIENT_ID**: Update the `AZURE_CLIENT_ID` in the cluster secret to match the client id of the newly created federated identity credential. + + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: mycluster-secret + labels: + argocd.argoproj.io/secret-type: cluster +type: Opaque +stringData: + name: mycluster.example.com + server: https://mycluster.example.com + config: | + { + "execProviderConfig": { + "command": "argocd-k8s-auth", + "env": { + "AAD_ENVIRONMENT_NAME": "AzurePublicCloud", + "AZURE_CLIENT_ID": "fill in client id", + "AZURE_TENANT_ID": "fill in tenant id", # optional, injected by workload identity mutating admission webhook if enabled + "AZURE_FEDERATED_TOKEN_FILE": "/opt/path/to/federated_file.json", # optional, injected by workload identity mutating admission webhook if enabled + "AZURE_AUTHORITY_HOST": "https://login.microsoftonline.com/", # optional, injected by workload identity mutating admission webhook if enabled + "AAD_LOGIN_METHOD": "workloadidentity" + }, + "args": ["azure"], + "apiVersion": "client.authentication.k8s.io/v1beta1" + }, + "tlsClientConfig": { + "insecure": false, + "caData": "" + } + } +``` + +This is an example of using the spn (service principal name) flow. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: mycluster-secret + labels: + argocd.argoproj.io/secret-type: cluster +type: Opaque +stringData: + name: mycluster.example.com + server: https://mycluster.example.com + config: | + { + "execProviderConfig": { + "command": "argocd-k8s-auth", + "env": { + "AAD_ENVIRONMENT_NAME": "AzurePublicCloud", + "AAD_SERVICE_PRINCIPAL_CLIENT_SECRET": "fill in your service principal client secret", + "AZURE_TENANT_ID": "fill in tenant id", + "AAD_SERVICE_PRINCIPAL_CLIENT_ID": "fill in your service principal client id", + "AAD_LOGIN_METHOD": "spn" + }, + "args": ["azure"], + "apiVersion": "client.authentication.k8s.io/v1beta1" + }, + "tlsClientConfig": { + "insecure": false, + "caData": "" + } + } +``` + ## Helm Chart Repositories Non standard Helm Chart repositories have to be registered explicitly. @@ -649,7 +1039,7 @@ stringData: ## Resource Exclusion/Inclusion -Resources can be excluded from discovery and sync so that Argo CD is unaware of them. For example, `events.k8s.io` and `metrics.k8s.io` are always excluded. Use cases: +Resources can be excluded from discovery and sync so that Argo CD is unaware of them. For example, the apiGroup/kind `events.k8s.io/*`, `metrics.k8s.io/*`, `coordination.k8s.io/Lease`, and `""/Endpoints` are always excluded. Use cases: * You have temporal issues and you want to exclude problematic resources. * There are many of a kind of resources that impacts Argo CD's performance. @@ -709,6 +1099,33 @@ Notes: * Invalid globs result in the whole rule being ignored. * If you add a rule that matches existing resources, these will appear in the interface as `OutOfSync`. +## Auto respect RBAC for controller + +Argocd controller can be restricted from discovering/syncing specific resources using just controller rbac, without having to manually configure resource exclusions. +This feature can be enabled by setting `resource.respectRBAC` key in argocd cm, once it is set the controller will automatically stop watching for resources +that it does not have the permission to list/access. Possible values for `resource.respectRBAC` are: + - `strict` : This setting checks whether the list call made by controller is forbidden/unauthorized and if it is, it will cross-check the permission by making a `SelfSubjectAccessReview` call for the resource. + - `normal` : This will only check whether the list call response is forbidden/unauthorized and skip `SelfSubjectAccessReview` call, to minimize any extra api-server calls. + - unset/empty (default) : This will disable the feature and controller will continue to monitor all resources. + +Users who are comfortable with an increase in kube api-server calls can opt for `strict` option while users who are concerned with higher api calls and are willing to compromise on the accuracy can opt for the `normal` option. + +Notes: + +* When set to use `strict` mode controller must have rbac permission to `create` a `SelfSubjectAccessReview` resource +* The `SelfSubjectAccessReview` request will be only made for the `list` verb, it is assumed that if `list` is allowed for a resource then all other permissions are also available to the controller. + +Example argocd cm with `resource.respectRBAC` set to `strict`: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cm +data: + resource.respectRBAC: "strict" +``` + ## Resource Custom Labels Custom Labels configured with `resource.customLabels` (comma separated string) will be displayed in the UI (for any resource that defines them). @@ -726,17 +1143,15 @@ based application which uses base Argo CD manifests from [https://github.com/arg Example of `kustomization.yaml`: ```yaml -bases: -- github.com/argoproj/argo-cd//manifests/cluster-install?ref=v1.0.1 - # additional resources like ingress rules, cluster and repository secrets. resources: +- github.com/argoproj/argo-cd//manifests/cluster-install?ref=stable - clusters-secrets.yaml - repos-secrets.yaml # changes to config maps -patchesStrategicMerge: -- overlays/argo-cd-cm.yaml +patches: +- path: overlays/argo-cd-cm.yaml ``` The live example of self managed Argo CD config is available at [https://cd.apps.argoproj.io](https://cd.apps.argoproj.io) and with configuration diff --git a/docs/operator-manual/deep_links.md b/docs/operator-manual/deep_links.md index 9b0778e045ac7..c166a1d25d75d 100644 --- a/docs/operator-manual/deep_links.md +++ b/docs/operator-manual/deep_links.md @@ -11,53 +11,68 @@ or individual resources (pods, services, etc.). ## Configuring Deep Links The configuration for Deep Links is present in `argocd-cm` as `.links` fields where -`` determines where it will be displayed. The possible values for `` are : -- `project` : all links under this field will show up in the project tab in the Argo CD UI -- `application` : all links under this field will show up in the application summary tab -- `resource` : all links under this field will show up in the resource (deployments, pods, services, etc.) summary tab - -Each link in the list has five subfields : -1. `title` : title/tag that will be displayed in the UI corresponding to that link -2. `url` : the actual URL where the deep link will redirect to, this field can be templated to use data from the - corresponing application, project or resource objects (depending on where it is located). This uses [text/template](pkg.go.dev/text/template) pkg for templating -3. `description` (optional) : a description for what the deep link is about -4. `icon.class` (optional) : a font-awesome icon class to be used when displaying the links in dropdown menus -5. `if` (optional) : a conditional statement that results in either `true` or `false`, it also has access to the same +`` determines where it will be displayed. The possible values for `` are: + +- `project`: all links under this field will show up in the project tab in the Argo CD UI +- `application`: all links under this field will show up in the application summary tab +- `resource`: all links under this field will show up in the resource (deployments, pods, services, etc.) summary tab + +Each link in the list has five subfields: + +1. `title`: title/tag that will be displayed in the UI corresponding to that link +2. `url`: the actual URL where the deep link will redirect to, this field can be templated to use data from the + corresponding application, project or resource objects (depending on where it is located). This uses [text/template](https://pkg.go.dev/text/template) pkg for templating +3. `description` (optional): a description for what the deep link is about +4. `icon.class` (optional): a font-awesome icon class to be used when displaying the links in dropdown menus +5. `if` (optional): a conditional statement that results in either `true` or `false`, it also has access to the same data as the `url` field. If the condition resolves to `true` the deep link will be displayed - else it will be hidden. If the field is omitted, by default the deep links will be displayed. This uses [antonmedv/expr](https://github.com/antonmedv/expr/tree/master/docs) for evaluating conditions !!!note - For resources of kind Secret the data fields are redacted but other fields are accessible for templating the deep links. + For resources of kind Secret the data fields are redacted but other fields are accessible for templating the deep links. !!!warning - Make sure to validate the url templates and inputs to prevent data leaks or possible generation of any malicious links. + Make sure to validate the url templates and inputs to prevent data leaks or possible generation of any malicious links. + +As mentioned earlier the links and conditions can be templated to use data from the resource, each category of links can access different types of data linked to that resource. +Overall we have these 4 resources available for templating in the system: + +- `app` or `application`: this key is used to access the application resource data. +- `resource`: this key is used to access values for the actual k8s resource. +- `cluster`: this key is used to access the related destination cluster data like name, server, namespaces etc. +- `project`: this key is used to access the project resource data. + +The above resources are accessible in particular link categories, here's a list of resources available in each category: +- `resource.links`: `resource`, `application`, `cluster` and `project` +- `application.links`: `app`/`application` and `cluster` +- `project.links`: `project` An example `argocd-cm.yaml` file with deep links and their variations : ```yaml # sample project level links project.links: | - - url: https://myaudit-system.com?project={{.metadata.name}} + - url: https://myaudit-system.com?project={{.project.metadata.name}} title: Audit description: system audit logs icon.class: "fa-book" # sample application level links application.links: | # pkg.go.dev/text/template is used for evaluating url templates - - url: https://mycompany.splunk.com?search={{.spec.destination.namespace}} + - url: https://mycompany.splunk.com?search={{.app.spec.destination.namespace}}&env={{.project.metadata.labels.env}} title: Splunk # conditionally show link e.g. for specific project # github.com/antonmedv/expr is used for evaluation of conditions - - url: https://mycompany.splunk.com?search={{.spec.destination.namespace}} + - url: https://mycompany.splunk.com?search={{.app.spec.destination.namespace}} title: Splunk - if: spec.project == "default" - - url: https://{{.metadata.annotations.splunkhost}}?search={{.spec.destination.namespace}} + if: application.spec.project == "default" + - url: https://{{.app.metadata.annotations.splunkhost}}?search={{.app.spec.destination.namespace}} title: Splunk - if: metadata.annotations.splunkhost + if: app.metadata.annotations.splunkhost != "" # sample resource level links resource.links: | - - url: https://mycompany.splunk.com?search={{.metadata.namespace}} + - url: https://mycompany.splunk.com?search={{.resource.metadata.name}}&env={{.project.metadata.labels.env}} title: Splunk - if: kind == "Pod" || kind == "Deployment" -``` \ No newline at end of file + if: resource.kind == "Pod" || resource.kind == "Deployment" +``` diff --git a/docs/operator-manual/disaster_recovery.md b/docs/operator-manual/disaster_recovery.md index 6bb52847d978a..97d2868051d65 100644 --- a/docs/operator-manual/disaster_recovery.md +++ b/docs/operator-manual/disaster_recovery.md @@ -15,13 +15,13 @@ export VERSION=v1.0.1 Export to a backup: ```bash -docker run -v ~/.kube:/home/argocd/.kube --rm argoproj/argocd:$VERSION argocd admin export > backup.yaml +docker run -v ~/.kube:/home/argocd/.kube --rm quay.io/argoproj/argocd:$VERSION argocd admin export > backup.yaml ``` Import from a backup: ```bash -docker run -i -v ~/.kube:/home/argocd/.kube --rm argoproj/argocd:$VERSION argocd admin import - < backup.yaml +docker run -i -v ~/.kube:/home/argocd/.kube --rm quay.io/argoproj/argocd:$VERSION argocd admin import - < backup.yaml ``` !!! note diff --git a/docs/operator-manual/dynamic-cluster-distribution.md b/docs/operator-manual/dynamic-cluster-distribution.md new file mode 100644 index 0000000000000..9d5d2104a1795 --- /dev/null +++ b/docs/operator-manual/dynamic-cluster-distribution.md @@ -0,0 +1,53 @@ +# Dynamic Cluster Distribution + +*Current Status: [Alpha][1] (Since v2.9.0)* + +By default, clusters are assigned to shards indefinitely. For users of the default, hash-based sharding algorithm, this +static assignment is fine: shards will always be roughly-balanced by the hash-based algorithm. But for users of the +[round-robin](high_availability.md#argocd-application-controller) or other custom shard assignment algorithms, this +static assignment can lead to unbalanced shards when replicas are added or removed. + +Starting v2.9, Argo CD supports a dynamic cluster distribution feature. When replicas are added or removed, the sharding +algorithm is re-run to ensure that the clusters are distributed according to the algorithm. If the algorithm is +well-balanced, like round-robin, then the shards will be well-balanced. + +Previously, the shard count was set via the `ARGOCD_CONTROLLER_REPLICAS` environment variable. Changing the environment +variable forced a restart of all application controller pods. Now, the shard count is set via the `replicas` field of the deployment, +which does not require a restart of the application controller pods. + +## Enabling Dynamic Distribution of Clusters + +This feature is disabled by default while it is in alpha. In order to utilize the feature, the manifests `manifests/ha/base/controller-deployment/` can be applied as a Kustomize overlay. This overlay sets the StatefulSet replicas to `0` and deploys the application controller as a Deployment. Also, you must set the environment `ARGOCD_ENABLE_DYNAMIC_CLUSTER_DISTRIBUTION` to true when running the Application Controller as a deployment. + +!!! important + The use of a Deployment instead of a StatefulSet is an implementation detail which may change in future versions of this feature. Therefore, the directory name of the Kustomize overlay may change as well. Monitor the release notes to avoid issues. + +Note the introduction of new environment variable `ARGOCD_CONTROLLER_HEARTBEAT_TIME`. The environment variable is explained in [working of Dynamic Distribution Heartbeat Process](#working-of-dynamic-distribution) + +## Working of Dynamic Distribution + +To accomplish runtime distribution of clusters, the Application Controller uses a ConfigMap to associate a controller +pod with a shard number and a heartbeat to ensure that controller pods are still alive and handling their shard, in +effect, their share of the work. + +The Application Controller will create a new ConfigMap named `argocd-app-controller-shard-cm` to store the Controller <-> Shard mapping. The mapping would look like below for each shard: + +```yaml +ShardNumber : 0 +ControllerName : "argocd-application-controller-hydrxyt" +HeartbeatTime : "2009-11-17 20:34:58.651387237 +0000 UTC" +``` + +* `ControllerName`: Stores the hostname of the Application Controller pod +* `ShardNumber` : Stores the shard number managed by the controller pod +* `HeartbeatTime`: Stores the last time this heartbeat was updated. + +Controller Shard Mapping is updated in the ConfigMap during each readiness probe check of the pod, that is every 10 seconds (otherwise as configured). The controller will acquire the shard during every iteration of readiness probe check and try to update the ConfigMap with the `HeartbeatTime`. The default `HeartbeatDuration` after which the heartbeat should be updated is `10` seconds. If the ConfigMap was not updated for any controller pod for more than `3 * HeartbeatDuration`, then the readiness probe for the application pod is marked as `Unhealthy`. To increase the default `HeartbeatDuration`, you can set the environment variable `ARGOCD_CONTROLLER_HEARTBEAT_TIME` with the desired value. + +The new sharding mechanism does not monitor the environment variable `ARGOCD_CONTROLLER_REPLICAS` but instead reads the replica count directly from the Application Controller Deployment. The controller identifies the change in the number of replicas by comparing the replica count in the Application Controller Deployment and the number of mappings in the `argocd-app-controller-shard-cm` ConfigMap. + +In the scenario when the number of Application Controller replicas increases, a new entry is added to the list of mappings in the `argocd-app-controller-shard-cm` ConfigMap and the cluster distribution is triggered to re-distribute the clusters. + +In the scenario when the number of Application Controller replicas decreases, the mappings in the `argocd-app-controller-shard-cm` ConfigMap are reset and every controller acquires the shard again thus triggering the re-distribution of the clusters. + +[1]: https://github.com/argoproj/argoproj/blob/master/community/feature-status.md diff --git a/docs/operator-manual/health.md b/docs/operator-manual/health.md index 6cc12a8e72cfc..8566d6460e6db 100644 --- a/docs/operator-manual/health.md +++ b/docs/operator-manual/health.md @@ -3,9 +3,9 @@ ## Overview Argo CD provides built-in health assessment for several standard Kubernetes types, which is then surfaced to the overall Application health status as a whole. The following checks are made for -specific types of kubernetes resources: +specific types of Kubernetes resources: -### Deployment, ReplicaSet, StatefulSet DaemonSet +### Deployment, ReplicaSet, StatefulSet, DaemonSet * Observed generation is equal to desired generation. * Number of **updated** replicas equals the number of desired replicas. @@ -114,12 +114,13 @@ In order to prevent duplication of the custom health check for potentially multi ```yaml resource.customizations: | - *.aws.crossplane.io/*: + "*.aws.crossplane.io/*": health.lua: | ... ``` - +!!!important + Please note the required quotes in the resource customization health section, if the wildcard starts with `*`. The `obj` is a global variable which contains the resource. The script must return an object with status and optional message field. The custom health check might return one of the following health statuses: @@ -136,9 +137,11 @@ setting `resource.customizations.useOpenLibs.`. In the following exa ```yaml data: - resource.customizations.useOpenLibs.cert-manager.io_Certificate: "true" - resource.customizations.health.cert-manager.io_Certificate: - -- Lua standard libraries are enabled for this script + resource.customizations: | + cert-manager.io/Certificate: + health.lua.useOpenLibs: true + health.lua: | + # Lua standard libraries are enabled for this script ``` ### Way 2. Contribute a Custom Health Check @@ -170,3 +173,36 @@ To test the implemented custom health checks, run `go test -v ./util/lua/`. The [PR#1139](https://github.com/argoproj/argo-cd/pull/1139) is an example of Cert Manager CRDs custom health check. Please note that bundled health checks with wildcards are not supported. + +## Health Checks + +An Argo CD App's health is inferred from the health of its immediate child resources (the resources represented in +source control). + +But the health of a resource is not inherited from child resources - it is calculated using only information about the +resource itself. A resource's status field may or may not contain information about the health of a child resource, and +the resource's health check may or may not take that information into account. + +The lack of inheritance is by design. A resource's health can't be inferred from its children because the health of a +child resource may not be relevant to the health of the parent resource. For example, a Deployment's health is not +necessarily affected by the health of its Pods. + +``` +App (healthy) +└── Deployment (healthy) + └── ReplicaSet (healthy) + └── Pod (healthy) + └── ReplicaSet (unhealthy) + └── Pod (unhealthy) +``` + +If you want the health of a child resource to affect the health of its parent, you need to configure the parent's health +check to take the child's health into account. Since only the parent resource's state is available to the health check, +the parent resource's controller needs to make the child resource's health available in the parent resource's status +field. + +``` +App (healthy) +└── CustomResource (healthy) <- This resource's health check needs to be fixed to mark the App as unhealthy + └── CustomChildResource (unhealthy) +``` diff --git a/docs/operator-manual/high_availability.md b/docs/operator-manual/high_availability.md index 0ae8a2edd9130..1b8a0aad3389a 100644 --- a/docs/operator-manual/high_availability.md +++ b/docs/operator-manual/high_availability.md @@ -1,10 +1,8 @@ # High Availability -Argo CD is largely stateless, all data is persisted as Kubernetes objects, which in turn is stored in Kubernetes' etcd. Redis is only used as a throw-away cache and can be lost. When lost, it will be rebuilt without loss of service. +Argo CD is largely stateless. All data is persisted as Kubernetes objects, which in turn is stored in Kubernetes' etcd. Redis is only used as a throw-away cache and can be lost. When lost, it will be rebuilt without loss of service. -A set of HA manifests are provided for users who wish to run Argo CD in a highly available manner. This runs more containers, and runs Redis in HA mode. - -[Manifests ⧉](https://github.com/argoproj/argo-cd/tree/master/manifests) +A set of [HA manifests](https://github.com/argoproj/argo-cd/tree/master/manifests/ha) are provided for users who wish to run Argo CD in a highly available manner. This runs more containers, and runs Redis in HA mode. > **NOTE:** The HA installation will require at least three different nodes due to pod anti-affinity roles in the > specs. Additionally, IPv6 only clusters are not supported. @@ -17,11 +15,11 @@ A set of HA manifests are provided for users who wish to run Argo CD in a highly The `argocd-repo-server` is responsible for cloning Git repository, keeping it up to date and generating manifests using the appropriate tool. -* `argocd-repo-server` fork/exec config management tool to generate manifests. The fork can fail due to lack of memory and limit on the number of OS threads. -The `--parallelismlimit` flag controls how many manifests generations are running concurrently and allows avoiding OOM kills. +* `argocd-repo-server` fork/exec config management tool to generate manifests. The fork can fail due to lack of memory or limit on the number of OS threads. +The `--parallelismlimit` flag controls how many manifests generations are running concurrently and helps avoid OOM kills. * the `argocd-repo-server` ensures that repository is in the clean state during the manifest generation using config management tools such as Kustomize, Helm -or custom plugin. As a result Git repositories with multiple applications might be affect repository server performance. +or custom plugin. As a result Git repositories with multiple applications might affect repository server performance. Read [Monorepo Scaling Considerations](#monorepo-scaling-considerations) for more information. * `argocd-repo-server` clones the repository into `/tmp` (or the path specified in the `TMPDIR` env variable). The Pod might run out of disk space if it has too many repositories @@ -30,7 +28,7 @@ or if the repositories have a lot of files. To avoid this problem mount a persis * `argocd-repo-server` uses `git ls-remote` to resolve ambiguous revisions such as `HEAD`, a branch or a tag name. This operation happens frequently and might fail. To avoid failed syncs use the `ARGOCD_GIT_ATTEMPTS_COUNT` environment variable to retry failed requests. -* `argocd-repo-server` Every 3m (by default) Argo CD checks for changes to the app manifests. Argo CD assumes by default that manifests only change when the repo changes, so it caches generated manifests (for 24h by default). With Kustomize remote bases, or Helm patch releases, the manifests can change even though the repo has not changed. By reducing the cache time, you can get the changes without waiting for 24h. Use `--repo-cache-expiration duration`, and we'd suggest in low volume environments you try '1h'. Bear in mind this will negate the benefit of caching if set too low. +* `argocd-repo-server` Every 3m (by default) Argo CD checks for changes to the app manifests. Argo CD assumes by default that manifests only change when the repo changes, so it caches the generated manifests (for 24h by default). With Kustomize remote bases, or in case a Helm chart gets changed without bumping its version number, the expected manifests can change even though the repo has not changed. By reducing the cache time, you can get the changes without waiting for 24h. Use `--repo-cache-expiration duration`, and we'd suggest in low volume environments you try '1h'. Bear in mind that this will negate the benefits of caching if set too low. * `argocd-repo-server` executes config management tools such as `helm` or `kustomize` and enforces a 90 second timeout. This timeout can be changed by using the `ARGOCD_EXEC_TIMEOUT` env variable. The value should be in the Go time duration string format, for example, `2m30s`. @@ -59,12 +57,13 @@ performance. For performance reasons the controller monitors and caches only the preferred version into a version of the resource stored in Git. If `kubectl convert` fails because the conversion is not supported then the controller falls back to Kubernetes API query which slows down reconciliation. In this case, we advise to use the preferred resource version in Git. -* The controller polls Git every 3m by default. You can change this duration using the `timeout.reconciliation` setting in the `argocd-cm` ConfigMap. The value of `timeout.reconciliation` is a duration string e.g `60s`, `1m`, `1h` or `1d`. +* The controller polls Git every 3m by default. You can change this duration using the `timeout.reconciliation` and `timeout.reconciliation.jitter` setting in the `argocd-cm` ConfigMap. The value of the fields is a duration string e.g `60s`, `1m`, `1h` or `1d`. * 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 the number of replicas in the `ARGOCD_CONTROLLER_REPLICAS` environment variable. The strategic merge patch below -demonstrates changes required to configure two controller replicas. +controller replicas. To enable sharding, increase the number of replicas in `argocd-application-controller` `StatefulSet` +and repeat the number of replicas in the `ARGOCD_CONTROLLER_REPLICAS` environment variable. The strategic merge patch below demonstrates changes required to configure two controller replicas. + +* By default, the controller will update the cluster information every 10 seconds. If there is a problem with your cluster network environment that is causing the update time to take a long time, you can try modifying the environment variable `ARGO_CD_UPDATE_CLUSTER_INFO_TIMEOUT` to increase the timeout (the unit is seconds). ```yaml apiVersion: apps/v1 @@ -81,9 +80,50 @@ spec: - name: ARGOCD_CONTROLLER_REPLICAS value: "2" ``` +* In order to manually set the cluster's shard number, specify the optional `shard` property when creating a cluster. If not specified, it will be calculated on the fly by the application controller. + +* The shard distribution algorithm of the `argocd-application-controller` can be set by using the `--sharding-method` parameter. Supported sharding methods are : [legacy (default), round-robin]. `legacy` mode uses an `uid` based distribution (non-uniform). `round-robin` uses an equal distribution across all shards. The `--sharding-method` parameter can also be overriden by setting the key `controller.sharding.algorithm` in the `argocd-cmd-params-cm` `configMap` (preferably) or by setting the `ARGOCD_CONTROLLER_SHARDING_ALGORITHM` environment variable and by specifiying the same possible values. + +!!! warning "Alpha Feature" + The `round-robin` shard distribution algorithm is an experimental feature. Reshuffling is known to occur in certain scenarios with cluster removal. If the cluster at rank-0 is removed, reshuffling all clusters across shards will occur and may temporarily have negative performance impacts. + +* A cluster can be manually assigned and forced to a `shard` by patching the `shard` field in the cluster secret to contain the shard number, e.g. +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: mycluster-secret + labels: + argocd.argoproj.io/secret-type: cluster +type: Opaque +stringData: + shard: 1 + name: mycluster.example.com + server: https://mycluster.example.com + config: | + { + "bearerToken": "", + "tlsClientConfig": { + "insecure": false, + "caData": "" + } + } +``` * `ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM` - environment variable that enables collecting RPC performance metrics. Enable it if you need to troubleshoot performance issues. Note: This metric is expensive to both query and store! +* `ARGOCD_CLUSTER_CACHE_LIST_PAGE_BUFFER_SIZE` - environment variable controlling the number of pages the controller + buffers in memory when performing a list operation against the K8s api server while syncing the cluster cache. This + is useful when the cluster contains a large number of resources and cluster sync times exceed the default etcd + compaction interval timeout. In this scenario, when attempting to sync the cluster cache, the application controller + may throw an error that the `continue parameter is too old to display a consistent list result`. Setting a higher + value for this environment variable configures the controller with a larger buffer in which to store pre-fetched + pages which are processed asynchronously, increasing the likelihood that all pages have been pulled before the etcd + compaction interval timeout expires. In the most extreme case, operators can set this value such that + `ARGOCD_CLUSTER_CACHE_LIST_PAGE_SIZE * ARGOCD_CLUSTER_CACHE_LIST_PAGE_BUFFER_SIZE` exceeds the largest resource + count (grouped by k8s api version, the granule of parallelism for list operations). In this case, all resources will + be buffered in memory -- no api server request will be blocked by processing. + **metrics** * `argocd_app_reconcile` - reports application reconciliation duration. Can be used to build reconciliation duration heat map to get a high-level reconciliation performance picture. @@ -92,12 +132,30 @@ non-preferred version and causes performance issues. ### argocd-server -The `argocd-server` is stateless and probably least likely to cause issues. You might consider increasing the number of replicas to 3 or more to ensure there is no downtime during upgrades. +The `argocd-server` is stateless and probably the least likely to cause issues. To ensure there is no downtime during upgrades, consider increasing the number of replicas to `3` or more and repeat the number in the `ARGOCD_API_SERVER_REPLICAS` environment variable. The strategic merge patch below +demonstrates this. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: argocd-server +spec: + replicas: 3 + template: + spec: + containers: + - name: argocd-server + env: + - name: ARGOCD_API_SERVER_REPLICAS + value: "3" +``` **settings:** +* The `ARGOCD_API_SERVER_REPLICAS` environment variable is used to divide [the limit of concurrent login requests (`ARGOCD_MAX_CONCURRENT_LOGIN_REQUESTS_COUNT`)](./user-management/index.md#failed-logins-rate-limiting) between each replica. * 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 this for an Argo CD instance that manages 3000+ applications. +The default value is 200. You might need to increase this for an Argo CD instance that manages 3000+ applications. ### argocd-dex-server, argocd-redis @@ -112,8 +170,8 @@ Argo CD repo server maintains one repository clone locally and uses it for appli Argo CD determines if manifest generation might change local files in the local repository clone based on the config management tool and application settings. If the manifest generation has no side effects then requests are processed in parallel without a performance penalty. The following are known cases that might cause slowness and their workarounds: - * **Multiple Helm based applications pointing to the same directory in one Git repository:** ensure that your Helm chart doesn't have conditional -[dependencies](https://helm.sh/docs/chart_best_practices/dependencies/#conditions-and-tags) and create `.argocd-allow-concurrency` file in the chart directory. + * **Multiple Helm based applications pointing to the same directory in one Git repository:** for historical reasons Argo CD generates Helm manifests sequentially. To enable parallel generation set `ARGOCD_HELM_ALLOW_CONCURRENCY=true` to `argocd-repo-server` deployment or create `.argocd-allow-concurrency` file. + Future versions of Argo CD will enable this by default. * **Multiple Custom plugin based applications:** avoid creating temporal files during manifest generation and create `.argocd-allow-concurrency` file in the app directory, or use the sidecar plugin option, which processes each application using a temporary copy of the repository. @@ -184,4 +242,103 @@ spec: targetRevision: HEAD path: my-application # ... -``` \ No newline at end of file +``` + +### Application Sync Timeout & Jitter + +Argo CD has a timeout for application syncs. It will trigger a refresh for each application periodically when the timeout expires. +With a large number of applications, this will cause a spike in the refresh queue and can cause a spike to the repo-server component. To avoid this, you can set a jitter to the sync timeout which will spread out the refreshes and give time to the repo-server to catch up. + +The jitter is the maximum duration that can be added to the sync timeout, so if the sync timeout is 5 minutes and the jitter is 1 minute, then the actual timeout will be between 5 and 6 minutes. + +To configure the jitter you can set the following environment variables: + +* `ARGOCD_RECONCILIATION_JITTER` - The jitter to apply to the sync timeout. Disabled when value is 0. Defaults to 0. + +## Rate Limiting Application Reconciliations + +To prevent high controller resource usage or sync loops caused either due to misbehaving apps or other environment specific factors, +we can configure rate limits on the workqueues used by the application controller. There are two types of rate limits that can be configured: + + * Global rate limits + * Per item rate limits + +The final rate limiter uses a combination of both and calculates the final backoff as `max(globalBackoff, perItemBackoff)`. + +### Global rate limits + + This is disabled by default, it is a simple bucket based rate limiter that limits the number of items that can be queued per second. +This is useful to prevent a large number of apps from being queued at the same time. + +To configure the bucket limiter you can set the following environment variables: + + * `WORKQUEUE_BUCKET_SIZE` - The number of items that can be queued in a single burst. Defaults to 500. + * `WORKQUEUE_BUCKET_QPS` - The number of items that can be queued per second. Defaults to MaxFloat64, which disables the limiter. + +### Per item rate limits + + This by default returns a fixed base delay/backoff value but can be configured to return exponential values. +Per item rate limiter limits the number of times a particular item can be queued. This is based on exponential backoff where the backoff time for an item keeps increasing exponentially +if it is queued multiple times in a short period, but the backoff is reset automatically if a configured `cool down` period has elapsed since the last time the item was queued. + +To configure the per item limiter you can set the following environment variables: + + * `WORKQUEUE_FAILURE_COOLDOWN_NS` : The cool down period in nanoseconds, once period has elapsed for an item the backoff is reset. Exponential backoff is disabled if set to 0(default), eg. values : 10 * 10^9 (=10s) + * `WORKQUEUE_BASE_DELAY_NS` : The base delay in nanoseconds, this is the initial backoff used in the exponential backoff formula. Defaults to 1000 (=1μs) + * `WORKQUEUE_MAX_DELAY_NS` : The max delay in nanoseconds, this is the max backoff limit. Defaults to 3 * 10^9 (=3s) + * `WORKQUEUE_BACKOFF_FACTOR` : The backoff factor, this is the factor by which the backoff is increased for each retry. Defaults to 1.5 + +The formula used to calculate the backoff time for an item, where `numRequeue` is the number of times the item has been queued +and `lastRequeueTime` is the time at which the item was last queued: + +- When `WORKQUEUE_FAILURE_COOLDOWN_NS` != 0 : + +``` +backoff = time.Since(lastRequeueTime) >= WORKQUEUE_FAILURE_COOLDOWN_NS ? + WORKQUEUE_BASE_DELAY_NS : + min( + WORKQUEUE_MAX_DELAY_NS, + WORKQUEUE_BASE_DELAY_NS * WORKQUEUE_BACKOFF_FACTOR ^ (numRequeue) + ) +``` + +- When `WORKQUEUE_FAILURE_COOLDOWN_NS` = 0 : + +``` +backoff = WORKQUEUE_BASE_DELAY_NS +``` + +## HTTP Request Retry Strategy + +In scenarios where network instability or transient server errors occur, the retry strategy ensures the robustness of HTTP communication by automatically resending failed requests. It uses a combination of maximum retries and backoff intervals to prevent overwhelming the server or thrashing the network. + +### Configuring Retries + +The retry logic can be fine-tuned with the following environment variables: + +* `ARGOCD_K8SCLIENT_RETRY_MAX` - The maximum number of retries for each request. The request will be dropped after this count is reached. Defaults to 0 (no retries). +* `ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF` - The initial backoff delay on the first retry attempt in ms. Subsequent retries will double this backoff time up to a maximum threshold. Defaults to 100ms. + +### Backoff Strategy + +The backoff strategy employed is a simple exponential backoff without jitter. The backoff time increases exponentially with each retry attempt until a maximum backoff duration is reached. + +The formula for calculating the backoff time is: + +``` +backoff = min(retryWaitMax, baseRetryBackoff * (2 ^ retryAttempt)) +``` +Where `retryAttempt` starts at 0 and increments by 1 for each subsequent retry. + +### Maximum Wait Time + +There is a cap on the backoff time to prevent excessive wait times between retries. This cap is defined by: + +`retryWaitMax` - The maximum duration to wait before retrying. This ensures that retries happen within a reasonable timeframe. Defaults to 10 seconds. + +### Non-Retriable Conditions + +Not all HTTP responses are eligible for retries. The following conditions will not trigger a retry: + +* Responses with a status code indicating client errors (4xx) except for 429 Too Many Requests. +* Responses with the status code 501 Not Implemented. diff --git a/docs/operator-manual/ingress.md b/docs/operator-manual/ingress.md index b0fe078482d91..aad2208c21873 100644 --- a/docs/operator-manual/ingress.md +++ b/docs/operator-manual/ingress.md @@ -166,6 +166,43 @@ The argocd-server Service needs to be annotated with `projectcontour.io/upstream The API server should then be run with TLS disabled. Edit the `argocd-server` deployment to add the `--insecure` flag to the argocd-server command, or simply set `server.insecure: "true"` in the `argocd-cmd-params-cm` ConfigMap [as described here](server-commands/additional-configuration-method.md). +Contour httpproxy CRD: + +Using a contour httpproxy CRD allows you to use the same hostname for the GRPC and REST api. + +```yaml +apiVersion: projectcontour.io/v1 +kind: HTTPProxy +metadata: + name: argocd-server + namespace: argocd +spec: + ingressClassName: contour + virtualhost: + fqdn: path.to.argocd.io + tls: + secretName: wildcard-tls + routes: + - conditions: + - prefix: / + - header: + name: Content-Type + contains: application/grpc + services: + - name: argocd-server + port: 80 + protocol: h2c # allows for unencrypted http2 connections + timeoutPolicy: + response: 1h + idle: 600s + idleConnection: 600s + - conditions: + - prefix: / + services: + - name: argocd-server + port: 80 +``` + ## [kubernetes/ingress-nginx](https://github.com/kubernetes/ingress-nginx) ### Option 1: SSL-Passthrough @@ -186,10 +223,10 @@ metadata: name: argocd-server-ingress namespace: argocd annotations: - kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/ssl-passthrough: "true" spec: + ingressClassName: nginx rules: - host: argocd.example.com http: @@ -218,14 +255,13 @@ metadata: namespace: argocd annotations: cert-manager.io/cluster-issuer: letsencrypt-prod - kubernetes.io/ingress.class: nginx - kubernetes.io/tls-acme: "true" nginx.ingress.kubernetes.io/ssl-passthrough: "true" # If you encounter a redirect loop or are getting a 307 response code # then you need to force the nginx ingress to connect to the backend using HTTPS. # nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" spec: + ingressClassName: nginx rules: - host: argocd.example.com http: @@ -240,13 +276,14 @@ spec: tls: - hosts: - argocd.example.com - secretName: argocd-secret # do not change, this is provided by Argo CD + secretName: argocd-server-tls # as expected by argocd-server ``` -### Option 2: Multiple Ingress Objects And Hosts +### Option 2: SSL Termination at Ingress Controller -Since ingress-nginx Ingress supports only a single protocol per Ingress object, an alternative -way would be to define two Ingress objects. One for HTTP/HTTPS, and the other for gRPC: +An alternative approach is to perform the SSL termination at the Ingress. Since an `ingress-nginx` Ingress supports only a single protocol per Ingress object, two Ingress objects need to be defined using the `nginx.ingress.kubernetes.io/backend-protocol` annotation, one for HTTP/HTTPS and the other for gRPC. + +Each ingress will be for a different domain (`argocd.example.com` and `grpc.argocd.example.com`). This requires that the Ingress resources use different TLS `secretName`s to avoid unexpected behavior. HTTP/HTTPS Ingress: ```yaml @@ -256,10 +293,10 @@ metadata: name: argocd-server-http-ingress namespace: argocd annotations: - kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/backend-protocol: "HTTP" spec: + ingressClassName: nginx rules: - http: paths: @@ -274,7 +311,7 @@ spec: tls: - hosts: - argocd.example.com - secretName: argocd-secret # do not change, this is provided by Argo CD + secretName: argocd-ingress-http ``` gRPC Ingress: @@ -285,9 +322,9 @@ metadata: name: argocd-server-grpc-ingress namespace: argocd annotations: - kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/backend-protocol: "GRPC" spec: + ingressClassName: nginx rules: - http: paths: @@ -302,7 +339,7 @@ spec: tls: - hosts: - grpc.argocd.example.com - secretName: argocd-secret # do not change, this is provided by Argo CD + secretName: argocd-ingress-grpc ``` The API server should then be run with TLS disabled. Edit the `argocd-server` deployment to add the @@ -414,6 +451,132 @@ Once we create this service, we can configure the Ingress to conditionally route - argocd.argoproj.io ``` +## [Istio](https://www.istio.io) +You can put Argo CD behind Istio using following configurations. Here we will achive both serving Argo CD behind istio and using subpath on Istio + +First we need to make sure that we can run Argo CD with subpath (ie /argocd). For this we have used install.yaml from argocd project as is + +```bash +curl -kLs -o install.yaml https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml +``` + +save following file as kustomization.yml + +```yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- ./install.yaml + +patches: +- path: ./patch.yml +``` + +And following lines as patch.yml + +```yaml +# Use --insecure so Ingress can send traffic with HTTP +# --bashref /argocd is the subpath like https://IP/argocd +# env was added because of https://github.com/argoproj/argo-cd/issues/3572 error +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: argocd-server +spec: + template: + spec: + containers: + - args: + - /usr/local/bin/argocd-server + - --staticassets + - /shared/app + - --redis + - argocd-redis-ha-haproxy:6379 + - --insecure + - --basehref + - /argocd + - --rootpath + - /argocd + name: argocd-server + env: + - name: ARGOCD_MAX_CONCURRENT_LOGIN_REQUESTS_COUNT + value: "0" +``` + +After that install Argo CD (there should be only 3 yml file defined above in current directory ) + +```bash +kubectl apply -k ./ -n argocd --wait=true +``` + +Be sure you create secret for Isito ( in our case secretname is argocd-server-tls on argocd Namespace). After that we create Istio Resources + +```yaml +apiVersion: networking.istio.io/v1alpha3 +kind: Gateway +metadata: + name: argocd-gateway + namespace: argocd +spec: + selector: + istio: ingressgateway + servers: + - port: + number: 80 + name: http + protocol: HTTP + hosts: + - "*" + tls: + httpsRedirect: true + - port: + number: 443 + name: https + protocol: HTTPS + hosts: + - "*" + tls: + credentialName: argocd-server-tls + maxProtocolVersion: TLSV1_3 + minProtocolVersion: TLSV1_2 + mode: SIMPLE + cipherSuites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + - ECDHE-ECDSA-AES128-SHA + - AES128-GCM-SHA256 + - AES128-SHA + - ECDHE-ECDSA-AES256-GCM-SHA384 + - ECDHE-RSA-AES256-GCM-SHA384 + - ECDHE-ECDSA-AES256-SHA + - AES256-GCM-SHA384 + - AES256-SHA +--- +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: argocd-virtualservice + namespace: argocd +spec: + hosts: + - "*" + gateways: + - argocd-gateway + http: + - match: + - uri: + prefix: /argocd + route: + - destination: + host: argocd-server + port: + number: 80 +``` + +And now we can browse http://{{ IP }}/argocd (it will be rewritten to https://{{ IP }}/argocd + + ## Google Cloud load balancers with Kubernetes Ingress You can make use of the integration of GKE with Google Cloud to deploy Load Balancers using just Kubernetes objects. @@ -535,18 +698,18 @@ metadata: networking.gke.io/v1beta1.FrontendConfig: argocd-frontend-config spec: tls: - - secretName: secret-yourdomain-com + - secretName: secret-example-com rules: - - host: argocd.yourdomain.com - http: - paths: - - pathType: ImplementationSpecific - path: "/*" # "*" is needed. Without this, the UI Javascript and CSS will not load properly - backend: - service: - name: argocd-server - port: - number: 80 + - host: argocd.example.com + http: + paths: + - pathType: ImplementationSpecific + path: "/*" # "*" is needed. Without this, the UI Javascript and CSS will not load properly + backend: + service: + name: argocd-server + port: + number: 80 ``` If you use the version `1.21.3-gke.1600` or later, you should use the following Ingress resource: @@ -560,21 +723,21 @@ metadata: networking.gke.io/v1beta1.FrontendConfig: argocd-frontend-config spec: tls: - - secretName: secret-yourdomain-com + - secretName: secret-example-com rules: - - host: argocd.yourdomain.com - http: - paths: - - pathType: Prefix - path: "/" - backend: - service: - name: argocd-server - port: - number: 80 + - host: argocd.example.com + http: + paths: + - pathType: Prefix + path: "/" + backend: + service: + name: argocd-server + port: + number: 80 ``` -As you may know already, it can take some minutes to deploy the load balancer and become ready to accept connections. Once it's ready, get the public IP address for your Load Balancer, go to your DNS server (Google or third party) and point your domain or subdomain (i.e. argocd.yourdomain.com) to that IP address. +As you may know already, it can take some minutes to deploy the load balancer and become ready to accept connections. Once it's ready, get the public IP address for your Load Balancer, go to your DNS server (Google or third party) and point your domain or subdomain (i.e. argocd.example.com) to that IP address. You can get that IP address describing the Ingress object like this: @@ -586,7 +749,7 @@ Once the DNS change is propagated, you're ready to use Argo with your Google Clo ## Authenticating through multiple layers of authenticating reverse proxies -ArgoCD endpoints may be protected by one or more reverse proxies layers, in that case, you can provide additional headers through the `argocd` CLI `--header` parameter to authenticate through those layers. +Argo CD endpoints may be protected by one or more reverse proxies layers, in that case, you can provide additional headers through the `argocd` CLI `--header` parameter to authenticate through those layers. ```shell $ argocd login : --header 'x-token1:foo' --header 'x-token2:bar' # can be repeated multiple times @@ -594,7 +757,7 @@ $ argocd login : --header 'x-token1:foo,x-token2:bar' # headers can ``` ## ArgoCD Server and UI Root Path (v1.5.3) -ArgoCD server and UI can be configured to be available under a non-root path (e.g. `/argo-cd`). +Argo CD server and UI can be configured to be available under a non-root path (e.g. `/argo-cd`). To do this, add the `--rootpath` flag into the `argocd-server` deployment command: ```yaml diff --git a/docs/operator-manual/installation.md b/docs/operator-manual/installation.md index 8778abe338be5..5782e5660868f 100644 --- a/docs/operator-manual/installation.md +++ b/docs/operator-manual/installation.md @@ -48,19 +48,17 @@ High Availability installation is recommended for production use. This bundle in ## Core -The core installation is most suitable for cluster administrators who independently use Argo CD and don't need multi-tenancy features. This installation -includes fewer components and is easier to setup. The bundle does not include the API server or UI, and installs the lightweight (non-HA) version of each component. +The Argo CD Core installation is primarily used to deploy Argo CD in +headless mode. This type of installation is most suitable for cluster +administrators who independently use Argo CD and don't need +multi-tenancy features. This installation includes fewer components +and is easier to setup. The bundle does not include the API server or +UI, and installs the lightweight (non-HA) version of each component. -The end-users need Kubernetes access to manage Argo CD. The `argocd` CLI has to be configured using the following commands: +Installation manifest is available at [core-install.yaml](https://github.com/argoproj/argo-cd/blob/master/manifests/core-install.yaml). -```bash -kubectl config set-context --current --namespace=argocd # change current kube context to argocd namespace -argocd login --core -``` - -The Web UI is also available and can be started using the `argocd admin dashboard` command. - -Installation manifests are available at [core-install.yaml](https://github.com/argoproj/argo-cd/blob/master/manifests/core-install.yaml). +For more details about Argo CD Core please refer to the [official +documentation](./core.md) ## Kustomize @@ -74,9 +72,12 @@ kind: Kustomization namespace: argocd resources: -- https://raw.githubusercontent.com/argoproj/argo-cd/v2.0.4/manifests/ha/install.yaml +- https://raw.githubusercontent.com/argoproj/argo-cd/v2.7.2/manifests/install.yaml ``` +For an example of this, see the [kustomization.yaml](https://github.com/argoproj/argoproj-deployments/blob/master/argocd/kustomization.yaml) +used to deploy the [Argoproj CI/CD infrastructure](https://github.com/argoproj/argoproj-deployments#argoproj-deployments). + ## Helm The Argo CD can be installed using [Helm](https://helm.sh/). The Helm chart is currently community maintained and available at @@ -84,17 +85,10 @@ The Argo CD can be installed using [Helm](https://helm.sh/). The Helm chart is c ## Supported versions -Similar to the Kubernetes project, the supported versions of Argo CD at any given point in time are the latest patch releases for the N -and N - 1 minor versions. -These Argo CD versions are supported on the same versions of Kubernetes that are supported by Kubernetes itself (normally the last 3 released versions). +For detailed information regarding Argo CD's version support policy, please refer to the [Release Process and Cadence documentation](https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/). -Essentially the Argo CD project follows the same support scheme as Kubernetes but for N, N-1 while Kubernetes supports N, N-1, N-2 versions. +## Tested versions -For example if the latest minor version of ArgoCD are 2.4.3 and 2.3.5 while supported Kubernetes versions are 1.24, 1.23 and 1.22 then the following combinations are supported: +The following table shows the versions of Kubernetes that are tested with each version of Argo CD. -* Argo CD 2.4.3 on Kubernetes 1.24 -* Argo CD 2.4.3 on Kubernetes 1.23 -* Argo CD 2.4.3 on Kubernetes 1.22 -* Argo CD 2.3.5 on Kubernetes 1.24 -* Argo CD 2.3.5 on Kubernetes 1.23 -* Argo CD 2.3.5 on Kubernetes 1.22 \ No newline at end of file +{!docs/operator-manual/tested-kubernetes-versions.md!} diff --git a/docs/operator-manual/metrics.md b/docs/operator-manual/metrics.md index da816f82f519b..a3ddbfe9904d3 100644 --- a/docs/operator-manual/metrics.md +++ b/docs/operator-manual/metrics.md @@ -7,13 +7,13 @@ Metrics about applications. Scraped at the `argocd-metrics:8082/metrics` endpoin | Metric | Type | Description | |--------|:----:|-------------| -| `argocd_app_info` | gauge | Information about Applications. It contains labels such as `sync_status` and `health_status` that reflect the application state in ArgoCD. | -| `argocd_app_k8s_request_total` | counter | Number of kubernetes requests executed during application reconciliation | +| `argocd_app_info` | gauge | Information about Applications. It contains labels such as `sync_status` and `health_status` that reflect the application state in Argo CD. | +| `argocd_app_k8s_request_total` | counter | Number of Kubernetes requests executed during application reconciliation | | `argocd_app_labels` | gauge | Argo Application labels converted to Prometheus labels. Disabled by default. See section below about how to enable it. | | `argocd_app_reconcile` | histogram | Application reconciliation performance. | | `argocd_app_sync_total` | counter | Counter for application sync history | | `argocd_cluster_api_resource_objects` | gauge | Number of k8s resource objects in the cache. | -| `argocd_cluster_api_resources` | gauge | Number of monitored kubernetes API resources. | +| `argocd_cluster_api_resources` | gauge | Number of monitored Kubernetes API resources. | | `argocd_cluster_cache_age_seconds` | gauge | Cluster cache age in seconds. | | `argocd_cluster_connection_status` | gauge | The k8s cluster current connection status. | | `argocd_cluster_events_total` | counter | Number of processes k8s resource events. | @@ -23,7 +23,7 @@ Metrics about applications. Scraped at the `argocd-metrics:8082/metrics` endpoin | `argocd_redis_request_duration` | histogram | Redis requests duration. | | `argocd_redis_request_total` | counter | Number of redis requests executed during application reconciliation | -If you use ArgoCD with many application and project creation and deletion, +If you use Argo CD with many application and project creation and deletion, the metrics page will keep in cache your application and project's history. If you are having issues because of a large number of metrics cardinality due to deleted resources, you can schedule a metrics reset to clean the @@ -32,16 +32,16 @@ history with an application controller flag. Example: ### Exposing Application labels as Prometheus metrics -There are use-cases where ArgoCD Applications contain labels that are desired to be exposed as Prometheus metrics. +There are use-cases where Argo CD Applications contain labels that are desired to be exposed as Prometheus metrics. Some examples are: * Having the team name as a label to allow routing alerts to specific receivers * Creating dashboards broken down by business units As the Application labels are specific to each company, this feature is disabled by default. To enable it, add the -`--metrics-application-labels` flag to the ArgoCD application controller. +`--metrics-application-labels` flag to the Argo CD application controller. -The example below will expose the ArgoCD Application labels `team-name` and `business-unit` to Prometheus: +The example below will expose the Argo CD Application labels `team-name` and `business-unit` to Prometheus: containers: - command: @@ -67,9 +67,11 @@ Scraped at the `argocd-server-metrics:8083/metrics` endpoint. | Metric | Type | Description | |--------|:----:|-------------| | `argocd_redis_request_duration` | histogram | Redis requests duration. | -| `argocd_redis_request_total` | counter | Number of kubernetes requests executed during application reconciliation. | +| `argocd_redis_request_total` | counter | Number of Kubernetes requests executed during application reconciliation. | | `grpc_server_handled_total` | counter | Total number of RPCs completed on the server, regardless of success or failure. | | `grpc_server_msg_sent_total` | counter | Total number of gRPC stream messages sent by the server. | +| `argocd_proxy_extension_request_total` | counter | Number of requests sent to the configured proxy extensions. | +| `argocd_proxy_extension_request_duration_seconds` | histogram | Request duration in seconds between the Argo CD API server and the proxy extension backend. | ## Repo Server Metrics Metrics about the Repo Server. @@ -79,14 +81,15 @@ Scraped at the `argocd-repo-server:8084/metrics` endpoint. |--------|:----:|-------------| | `argocd_git_request_duration_seconds` | histogram | Git requests duration seconds. | | `argocd_git_request_total` | counter | Number of git requests performed by repo server | +| `argocd_git_fetch_fail_total` | counter | Number of git fetch requests failures by repo server | | `argocd_redis_request_duration_seconds` | histogram | Redis requests duration seconds. | -| `argocd_redis_request_total` | counter | Number of kubernetes requests executed during application reconciliation. | +| `argocd_redis_request_total` | counter | Number of Kubernetes requests executed during application reconciliation. | | `argocd_repo_pending_request_total` | gauge | Number of pending requests requiring repository lock | ## Prometheus Operator If using Prometheus Operator, the following ServiceMonitor example manifests can be used. -Change `metadata.labels.release` to the name of label selected by your Prometheus. +Add a namespace where Argo CD is installed and change `metadata.labels.release` to the name of label selected by your Prometheus. ```yaml apiVersion: monitoring.coreos.com/v1 @@ -148,6 +151,54 @@ spec: - port: metrics ``` +```yaml +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: argocd-dex-server + labels: + release: prometheus-operator +spec: + selector: + matchLabels: + app.kubernetes.io/name: argocd-dex-server + endpoints: + - port: metrics +``` + +```yaml +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: argocd-redis-haproxy-metrics + labels: + release: prometheus-operator +spec: + selector: + matchLabels: + app.kubernetes.io/name: argocd-redis-ha-haproxy + endpoints: + - port: http-exporter-port +``` + +For notifications controller, you need to additionally add following: + +```yaml +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: argocd-notifications-controller + labels: + release: prometheus-operator +spec: + selector: + matchLabels: + app.kubernetes.io/name: argocd-notifications-controller-metrics + endpoints: + - port: metrics +``` + + ## Dashboards You can find an example Grafana dashboard [here](https://github.com/argoproj/argo-cd/blob/master/examples/dashboard.json) or check demo instance diff --git a/docs/operator-manual/notifications/catalog.md b/docs/operator-manual/notifications/catalog.md index 8f413ac7eb5b3..add7084304b98 100644 --- a/docs/operator-manual/notifications/catalog.md +++ b/docs/operator-manual/notifications/catalog.md @@ -62,8 +62,7 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -90,8 +89,7 @@ teams: "value": "{{.app.status.sync.revision}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" @@ -145,8 +143,7 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -169,8 +166,7 @@ teams: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" @@ -224,8 +220,7 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -252,8 +247,7 @@ teams: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" @@ -307,8 +301,7 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -335,8 +328,7 @@ teams: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" @@ -394,8 +386,7 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -418,8 +409,7 @@ teams: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" @@ -472,8 +462,7 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -500,8 +489,7 @@ teams: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" diff --git a/docs/operator-manual/notifications/functions.md b/docs/operator-manual/notifications/functions.md index 3d614e4e53a55..c50d122024b76 100644 --- a/docs/operator-manual/notifications/functions.md +++ b/docs/operator-manual/notifications/functions.md @@ -48,6 +48,16 @@ Transforms given GIT URL into HTTPs format. Returns repository URL full name `(/)`. Currently supports only Github, GitLab and Bitbucket. +
+**`repo.QueryEscape(s string) string`** + +QueryEscape escapes the string, so it can be safely placed inside a URL + +Example: +``` +/projects/{{ call .repo.QueryEscape (call .repo.FullNameByRepoURL .app.status.RepoURL) }}/merge_requests +``` +
**`repo.GetCommitMetadata(sha string) CommitMetadata`** diff --git a/docs/operator-manual/notifications/index.md b/docs/operator-manual/notifications/index.md index dafb87169eb14..eccca906ae91b 100644 --- a/docs/operator-manual/notifications/index.md +++ b/docs/operator-manual/notifications/index.md @@ -1,4 +1,4 @@ -# Overview +# Notifications Overview Argo CD Notifications continuously monitors Argo CD applications and provides a flexible way to notify users about important changes in the application state. Using a flexible mechanism of @@ -10,37 +10,106 @@ So you can just use them instead of reinventing new ones. * Install Triggers and Templates from the catalog -``` -kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/notifications_catalog/install.yaml -``` + ```bash + kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/notifications_catalog/install.yaml + ``` * Add Email username and password token to `argocd-notifications-secret` secret -```bash -export EMAIL_USER= -export PASSWORD= -kubectl apply -n argocd -f - << EOF + ```bash + EMAIL_USER= + PASSWORD= + + kubectl apply -n argocd -f - << EOF + apiVersion: v1 + kind: Secret + metadata: + name: argocd-notifications-secret + stringData: + email-username: $EMAIL_USER + email-password: $PASSWORD + type: Opaque + EOF + ``` + +* Register Email notification service + + ```bash + kubectl patch cm argocd-notifications-cm -n argocd --type merge -p '{"data": {"service.email.gmail": "{ username: $email-username, password: $email-password, host: smtp.gmail.com, port: 465, from: $email-username }" }}' + ``` + +* Subscribe to notifications by adding the `notifications.argoproj.io/subscribe.on-sync-succeeded.slack` annotation to the Argo CD application or project: + + ```bash + kubectl patch app -n argocd -p '{"metadata": {"annotations": {"notifications.argoproj.io/subscribe.on-sync-succeeded.slack":""}}}' --type merge + ``` + +Try syncing an application to get notified when the sync is completed. + +## Namespace based configuration + +A common installation method for Argo CD Notifications is to install it in a dedicated namespace to manage a whole cluster. In this case, the administrator is the only +person who can configure notifications in that namespace generally. However, in some cases, it is required to allow end-users to configure notifications +for their Argo CD applications. For example, the end-user can configure notifications for their Argo CD application in the namespace where they have access to and their Argo CD application is running in. + +This feature is based on applications in any namespace. See [applications in any namespace](../app-any-namespace.md) page for more information. + +In order to enable this feature, the Argo CD administrator must reconfigure the argocd-notification-controller workloads to add `--application-namespaces` and `--self-service-notification-enabled` parameters to the container's startup command. +`--application-namespaces` controls the list of namespaces that Argo CD applications are in. `--self-service-notification-enabled` turns on this feature. + +The startup parameters for both can also be conveniently set up and kept in sync by specifying +the `application.namespaces` and `notificationscontroller.selfservice.enabled` in the argocd-cmd-params-cm ConfigMap instead of changing the manifests for the respective workloads. For example: + +```yaml apiVersion: v1 -kind: Secret +kind: ConfigMap metadata: - name: argocd-notifications-secret -stringData: - email-username: $EMAIL_USER - email-password: $PASSWORD -type: Opaque -EOF + name: argocd-cmd-params-cm +data: + application.namespaces: app-team-one, app-team-two + notificationscontroller.selfservice.enabled: "true" ``` -* Register Email notification service +To use this feature, you can deploy configmap named `argocd-notifications-cm` and possibly a secret `argocd-notifications-secret` in the namespace where the Argo CD application lives. -```bash -kubectl patch cm argocd-notifications-cm -n argocd --type merge -p '{"data": {"service.email.gmail": "{ username: $email-username, password: $email-password, host: smtp.gmail.com, port: 465, from: $email-username }" }}' -``` +When it is configured this way the controller will send notifications using both the controller level configuration (the configmap located in the same namespaces as the controller) as well as +the configuration located in the same namespace where the Argo CD application is at. -* Subscribe to notifications by adding the `notifications.argoproj.io/subscribe.on-sync-succeeded.slack` annotation to the Argo CD application or project: +Example: Application team wants to receive notifications using PagerDutyV2, when the controller level configuration is only supporting Slack. + +The following two resources are deployed in the namespace where the Argo CD application lives. +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-notifications-cm +data: + service.pagerdutyv2: | + serviceKeys: + my-service: $pagerduty-key-my-service +... +``` +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: argo-cd-notification-secret +type: Opaque +data: + pagerduty-key-my-service: +``` -```bash -kubectl patch app -n argocd -p '{"metadata": {"annotations": {"notifications.argoproj.io/subscribe.on-sync-succeeded.slack":""}}}' --type merge +When an Argo CD application has the following subscriptions, user receives application sync failure message from pager duty. +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + notifications.argoproj.io/subscribe.on-sync-failed.pagerdutyv2: "" ``` -Try syncing an application to get notified when the sync is completed. +!!! note + When the same notification service and trigger are defined in controller level configuration and application level configuration, + both notifications will be sent according to its own configuration. + +[Defining and using secrets within notification templates](templates.md/#defining-and-using-secrets-within-notification-templates) function is not available when flag `--self-service-notification-enable` is on. diff --git a/docs/operator-manual/notifications/monitoring.md b/docs/operator-manual/notifications/monitoring.md index 31b6f45cf4392..a0aabbaae1f09 100644 --- a/docs/operator-manual/notifications/monitoring.md +++ b/docs/operator-manual/notifications/monitoring.md @@ -3,7 +3,7 @@ The Argo CD Notification controller serves Prometheus metrics on port 9001. !!! note - Metrics port might be changed using the `--metrics-port` flag in `argocd-notifications-controller` deployment. + The metrics port can be changed using the `--metrics-port` flag in `argocd-notifications-controller` deployment. ## Metrics The following metrics are available: @@ -15,7 +15,7 @@ The following metrics are available: * `template` - notification template name * `notifier` - notification service name -* `succeeded` - flag that indicates if notification was successfully sent or failed. +* `succeeded` - flag that indicates if notification was successfully sent or failed ### `argocd_notifications_trigger_eval_total` @@ -23,8 +23,8 @@ The following metrics are available: Labels: * `name` - trigger name -* `triggered` - flag that indicates if trigger condition returned true of false. +* `triggered` - flag that indicates if trigger condition returned true of false -# Examples: +## Examples -* Grafana Dashboard: [grafana-dashboard.json](grafana-dashboard.json) \ No newline at end of file +* Grafana Dashboard: [grafana-dashboard.json](grafana-dashboard.json) diff --git a/docs/operator-manual/notifications/services/alertmanager.md b/docs/operator-manual/notifications/services/alertmanager.md index e0f9d7e4e7889..033a76a29ea65 100755 --- a/docs/operator-manual/notifications/services/alertmanager.md +++ b/docs/operator-manual/notifications/services/alertmanager.md @@ -43,7 +43,7 @@ You should turn off "send_resolved" or you will receive unnecessary recovery not apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.alertmanager: | targets: @@ -58,7 +58,7 @@ If your alertmanager has changed the default api, you can customize "apiPath". apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.alertmanager: | targets: @@ -89,7 +89,7 @@ stringData: apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.alertmanager: | targets: @@ -110,7 +110,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.alertmanager: | targets: diff --git a/docs/operator-manual/notifications/services/awssqs.md b/docs/operator-manual/notifications/services/awssqs.md new file mode 100755 index 0000000000000..5331533826348 --- /dev/null +++ b/docs/operator-manual/notifications/services/awssqs.md @@ -0,0 +1,119 @@ +# AWS SQS + +## Parameters + +This notification service is capable of sending simple messages to AWS SQS queue. + +* `queue` - name of the queue you are intending to send messages to. Can be overridden with target destination annotation. +* `region` - region of the sqs queue can be provided via env variable AWS_DEFAULT_REGION +* `key` - optional, aws access key must be either referenced from a secret via variable or via env variable AWS_ACCESS_KEY_ID +* `secret` - optional, aws access secret must be either referenced from a secret via variable or via env variable AWS_SECRET_ACCESS_KEY +* `account` optional, external accountId of the queue +* `endpointUrl` optional, useful for development with localstack + +## Example + +### Using Secret for credential retrieval: + +Resource Annotation: +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + annotations: + notifications.argoproj.io/subscribe.on-deployment-ready.awssqs: "overwrite-myqueue" +``` + +* ConfigMap +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-notifications-cm +data: + service.awssqs: | + region: "us-east-2" + queue: "myqueue" + account: "1234567" + key: "$awsaccess_key" + secret: "$awsaccess_secret" + + template.deployment-ready: | + message: | + Deployment {{.obj.metadata.name}} is ready! + + trigger.on-deployment-ready: | + - when: any(obj.status.conditions, {.type == 'Available' && .status == 'True'}) + send: [deployment-ready] + - oncePer: obj.metadata.annotations["generation"] + +``` + Secret +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: +stringData: + awsaccess_key: test + awsaccess_secret: test +``` + + +### Minimal configuration using AWS Env variables + +Ensure the following list of environment variables are injected via OIDC, or another method. And assuming SQS is local to the account. +You may skip usage of secret for sensitive data and omit other parameters. (Setting parameters via ConfigMap takes precedent.) + +Variables: + +```bash +export AWS_ACCESS_KEY_ID="test" +export AWS_SECRET_ACCESS_KEY="test" +export AWS_DEFAULT_REGION="us-east-1" +``` + +Resource Annotation: +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + annotations: + notifications.argoproj.io/subscribe.on-deployment-ready.awssqs: "" +``` + +* ConfigMap +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-notifications-cm +data: + service.awssqs: | + queue: "myqueue" + + template.deployment-ready: | + message: | + Deployment {{.obj.metadata.name}} is ready! + + trigger.on-deployment-ready: | + - when: any(obj.status.conditions, {.type == 'Available' && .status == 'True'}) + send: [deployment-ready] + - oncePer: obj.metadata.annotations["generation"] + +``` + +## FIFO SQS Queues + +FIFO queues require a [MessageGroupId](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html#SQS-SendMessage-request-MessageGroupId) to be sent along with every message, every message with a matching MessageGroupId will be processed one by one in order. + +To send to a FIFO SQS Queue you must include a `messageGroupId` in the template such as in the example below: + +```yaml +template.deployment-ready: | + message: | + Deployment {{.obj.metadata.name}} is ready! + messageGroupId: {{.obj.metadata.name}}-deployment +``` diff --git a/docs/operator-manual/notifications/services/email.md b/docs/operator-manual/notifications/services/email.md index e3c4b7d9e6380..7fd3f0e22379c 100755 --- a/docs/operator-manual/notifications/services/email.md +++ b/docs/operator-manual/notifications/services/email.md @@ -20,7 +20,7 @@ The following snippet contains sample Gmail service configuration: apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.email.gmail: | username: $email-username @@ -36,7 +36,7 @@ Without authentication: apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.email.example: | host: smtp.example.com @@ -46,13 +46,13 @@ data: ## Template -Notification templates support specifying subject for email notifications: +[Notification templates](../templates.md) support specifying subject for email notifications: ```yaml apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: template.app-sync-succeeded: | email: diff --git a/docs/operator-manual/notifications/services/github.md b/docs/operator-manual/notifications/services/github.md index 953e55bd65d5f..1fa1a985d2682 100755 --- a/docs/operator-manual/notifications/services/github.md +++ b/docs/operator-manual/notifications/services/github.md @@ -12,7 +12,7 @@ The GitHub notification service changes commit status using [GitHub Apps](https: ## Configuration 1. Create a GitHub Apps using https://github.com/settings/apps/new -2. Change repository permissions to enable write commit statuses +2. Change repository permissions to enable write commit statuses and/or deployments and/or pull requests comments ![2](https://user-images.githubusercontent.com/18019529/108397381-3ca57980-725b-11eb-8d17-5b8992dc009e.png) 3. Generate a private key, and download it automatically ![3](https://user-images.githubusercontent.com/18019529/108397926-d4a36300-725b-11eb-83fe-74795c8c3e03.png) @@ -24,7 +24,7 @@ in `argocd-notifications-cm` ConfigMap apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.github: | appID: @@ -58,15 +58,35 @@ metadata: ![](https://user-images.githubusercontent.com/18019529/108520497-168ce180-730e-11eb-93cb-b0b91f99bdc5.png) -If the message is set to 140 characters or more, it will be truncate. - ```yaml template.app-deployed: | message: | Application {{.app.metadata.name}} is now running new version of deployments manifests. github: + repoURLPath: "{{.app.spec.source.repoURL}}" + revisionPath: "{{.app.status.operationState.syncResult.revision}}" status: state: success label: "continuous-delivery/{{.app.metadata.name}}" targetURL: "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}?operation=true" + deployment: + state: success + environment: production + environmentURL: "https://{{.app.metadata.name}}.example.com" + logURL: "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}?operation=true" + requiredContexts: [] + autoMerge: true + transientEnvironment: false + pullRequestComment: + content: | + Application {{.app.metadata.name}} is now running new version of deployments manifests. + See more here: {{.context.argocdUrl}}/applications/{{.app.metadata.name}}?operation=true ``` + +**Notes**: +- If the message is set to 140 characters or more, it will be truncated. +- If `github.repoURLPath` and `github.revisionPath` are same as above, they can be omitted. +- Automerge is optional and `true` by default for github deployments to ensure the requested ref is up to date with the default branch. + Setting this option to `false` is required if you would like to deploy older refs in your default branch. + For more information see the [GitHub Deployment API Docs](https://docs.github.com/en/rest/deployments/deployments?apiVersion=2022-11-28#create-a-deployment). +- If `github.pullRequestComment.content` is set to 65536 characters or more, it will be truncated. diff --git a/docs/operator-manual/notifications/services/googlechat.md b/docs/operator-manual/notifications/services/googlechat.md index 041ea6e022ef5..821c23023e863 100755 --- a/docs/operator-manual/notifications/services/googlechat.md +++ b/docs/operator-manual/notifications/services/googlechat.md @@ -19,7 +19,7 @@ The Google Chat notification service send message notifications to a google chat apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.googlechat: | webhooks: @@ -59,24 +59,27 @@ A card message can be defined as follows: ```yaml template.app-sync-succeeded: | googlechat: - cards: | + cardsV2: | - header: title: ArgoCD Bot Notification sections: - widgets: - - textParagraph: + - decoratedText: text: The app {{ .app.metadata.name }} has successfully synced! - widgets: - - keyValue: + - decoratedText: topLabel: Repository - content: {{ call .repo.RepoURLToHTTPS .app.spec.source.repoURL }} - - keyValue: + text: {{ call .repo.RepoURLToHTTPS .app.spec.source.repoURL }} + - decoratedText: topLabel: Revision - content: {{ .app.spec.source.targetRevision }} - - keyValue: + text: {{ .app.spec.source.targetRevision }} + - decoratedText: topLabel: Author - content: {{ (call .repo.GetCommitMetadata .app.status.sync.revision).Author }} + text: {{ (call .repo.GetCommitMetadata .app.status.sync.revision).Author }} ``` +All [Card fields](https://developers.google.com/chat/api/reference/rest/v1/cards#Card_1) are supported and can be used +in notifications. It is also possible to use the previous (now deprecated) `cards` key to use the legacy card fields, +but this is not recommended as Google has deprecated this field and recommends using the newer `cardsV2`. The card message can be written in JSON too. @@ -86,7 +89,7 @@ It is possible send both simple text and card messages in a chat thread by speci ```yaml template.app-sync-succeeded: | - message: The app {{ .app.metadata.name }} has succesfully synced! + message: The app {{ .app.metadata.name }} has successfully synced! googlechat: threadKey: {{ .app.metadata.name }} ``` diff --git a/docs/operator-manual/notifications/services/grafana.md b/docs/operator-manual/notifications/services/grafana.md index ff567b71c1ee6..1f3e77701f044 100755 --- a/docs/operator-manual/notifications/services/grafana.md +++ b/docs/operator-manual/notifications/services/grafana.md @@ -4,6 +4,12 @@ To be able to create Grafana annotation with argocd-notifications you have to cr ![sample](https://user-images.githubusercontent.com/18019529/112024976-0f106080-8b78-11eb-9658-7663305899be.png) +Available parameters : + +* `apiURL` - the server url, e.g. https://grafana.example.com +* `apiKey` - the API key for the serviceaccount +* `insecureSkipVerify` - optional bool, true or false + 1. Login to your Grafana instance as `admin` 2. On the left menu, go to Configuration / API Keys 3. Click "Add API Key" @@ -15,7 +21,7 @@ To be able to create Grafana annotation with argocd-notifications you have to cr apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.grafana: | apiUrl: https://grafana.example.com/api diff --git a/docs/operator-manual/notifications/services/mattermost.md b/docs/operator-manual/notifications/services/mattermost.md index 98e0d0fd7b82f..d1f187e955b9c 100755 --- a/docs/operator-manual/notifications/services/mattermost.md +++ b/docs/operator-manual/notifications/services/mattermost.md @@ -19,7 +19,7 @@ in `argocd-notifications-cm` ConfigMap apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.mattermost: | apiURL: diff --git a/docs/operator-manual/notifications/services/newrelic.md b/docs/operator-manual/notifications/services/newrelic.md index d98288a846422..b0c7e340c9b28 100755 --- a/docs/operator-manual/notifications/services/newrelic.md +++ b/docs/operator-manual/notifications/services/newrelic.md @@ -14,7 +14,7 @@ apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.newrelic: | apiURL: diff --git a/docs/operator-manual/notifications/services/opsgenie.md b/docs/operator-manual/notifications/services/opsgenie.md index 665d0081e7c73..e92ee99756ab8 100755 --- a/docs/operator-manual/notifications/services/opsgenie.md +++ b/docs/operator-manual/notifications/services/opsgenie.md @@ -12,14 +12,15 @@ To be able to send notifications with argocd-notifications you have to create an 8. Give your integration a name, copy the "API key" and safe it somewhere for later 9. Make sure the checkboxes for "Create and Update Access" and "enable" are selected, disable the other checkboxes to remove unnecessary permissions 10. Click "Safe Integration" at the bottom -11. Check your browser for the correct server apiURL. If it is "app.opsgenie.com" then use the us/international api url `api.opsgenie.com` in the next step, otherwise use `api.eu.opsgenie.com` (european api). -12. You are finished with configuring opsgenie. Now you need to configure argocd-notifications. Use the apiUrl, the team name and the apiKey to configure the opsgenie integration in the `argocd-notifications-secret` secret. +11. Check your browser for the correct server apiURL. If it is "app.opsgenie.com" then use the US/international api url `api.opsgenie.com` in the next step, otherwise use `api.eu.opsgenie.com` (European API). +12. You are finished with configuring Opsgenie. Now you need to configure argocd-notifications. Use the apiUrl, the team name and the apiKey to configure the Opsgenie integration in the `argocd-notifications-secret` secret. + ```yaml apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.opsgenie: | apiUrl: diff --git a/docs/operator-manual/notifications/services/overview.md b/docs/operator-manual/notifications/services/overview.md index 15e674f65412b..265e575755088 100755 --- a/docs/operator-manual/notifications/services/overview.md +++ b/docs/operator-manual/notifications/services/overview.md @@ -38,6 +38,7 @@ metadata: ## Service Types +* [AwsSqs](./awssqs.md) * [Email](./email.md) * [GitHub](./github.md) * [Slack](./slack.md) diff --git a/docs/operator-manual/notifications/services/pagerduty.md b/docs/operator-manual/notifications/services/pagerduty.md index 849b4db802d9d..c6e1e41dac81d 100755 --- a/docs/operator-manual/notifications/services/pagerduty.md +++ b/docs/operator-manual/notifications/services/pagerduty.md @@ -1,17 +1,17 @@ -# Pagerduty +# PagerDuty ## Parameters -The Pagerduty notification service is used to create pagerduty incidents and requires specifying the following settings: +The PagerDuty notification service is used to create PagerDuty incidents and requires specifying the following settings: -* `pagerdutyToken` - the pagerduty auth token +* `pagerdutyToken` - the PagerDuty auth token * `from` - email address of a valid user associated with the account making the request. * `serviceID` - The ID of the resource. ## Example -The following snippet contains sample Pagerduty service configuration: +The following snippet contains sample PagerDuty service configuration: ```yaml apiVersion: v1 @@ -26,7 +26,7 @@ stringData: apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.pagerduty: | token: $pagerdutyToken @@ -35,13 +35,13 @@ data: ## Template -Notification templates support specifying subject for pagerduty notifications: +[Notification templates](../templates.md) support specifying subject for PagerDuty notifications: ```yaml apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: template.rollout-aborted: | message: Rollout {{.rollout.metadata.name}} is aborted. @@ -62,5 +62,5 @@ apiVersion: argoproj.io/v1alpha1 kind: Rollout metadata: annotations: - notifications.argoproj.io/subscribe.on-rollout-aborted.pagerduty: "" -``` \ No newline at end of file + notifications.argoproj.io/subscribe.on-rollout-aborted.pagerduty: "" +``` diff --git a/docs/operator-manual/notifications/services/pagerduty_v2.md b/docs/operator-manual/notifications/services/pagerduty_v2.md new file mode 100755 index 0000000000000..549cdc937b150 --- /dev/null +++ b/docs/operator-manual/notifications/services/pagerduty_v2.md @@ -0,0 +1,78 @@ +# PagerDuty V2 + +## Parameters + +The PagerDuty notification service is used to trigger PagerDuty events and requires specifying the following settings: + +* `serviceKeys` - a dictionary with the following structure: + * `service-name: $pagerduty-key-service-name` where `service-name` is the name you want to use for the service to make events for, and `$pagerduty-key-service-name` is a reference to the secret that contains the actual PagerDuty integration key (Events API v2 integration) + +If you want multiple Argo apps to trigger events to their respective PagerDuty services, create an integration key in each service you want to setup alerts for. + +To create a PagerDuty integration key, [follow these instructions](https://support.pagerduty.com/docs/services-and-integrations#create-a-generic-events-api-integration) to add an Events API v2 integration to the service of your choice. + +## Configuration + +The following snippet contains sample PagerDuty service configuration. It assumes the service you want to alert on is called `my-service`. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: +stringData: + pagerduty-key-my-service: +``` + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-notifications-cm +data: + service.pagerdutyv2: | + serviceKeys: + my-service: $pagerduty-key-my-service +``` + +## Template + +[Notification templates](../templates.md) support specifying subject for PagerDuty notifications: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-notifications-cm +data: + template.rollout-aborted: | + message: Rollout {{.rollout.metadata.name}} is aborted. + pagerdutyv2: + summary: "Rollout {{.rollout.metadata.name}} is aborted." + severity: "critical" + source: "{{.rollout.metadata.name}}" +``` + +The parameters for the PagerDuty configuration in the template generally match with the payload for the Events API v2 endpoint. All parameters are strings. + +* `summary` - (required) A brief text summary of the event, used to generate the summaries/titles of any associated alerts. +* `severity` - (required) The perceived severity of the status the event is describing with respect to the affected system. Allowed values: `critical`, `warning`, `error`, `info` +* `source` - (required) The unique location of the affected system, preferably a hostname or FQDN. +* `component` - Component of the source machine that is responsible for the event. +* `group` - Logical grouping of components of a service. +* `class` - The class/type of the event. +* `url` - The URL that should be used for the link "View in ArgoCD" in PagerDuty. + +The `timestamp` and `custom_details` parameters are not currently supported. + +## Annotation + +Annotation sample for PagerDuty notifications: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + annotations: + notifications.argoproj.io/subscribe.on-rollout-aborted.pagerdutyv2: "" +``` diff --git a/docs/operator-manual/notifications/services/pushover.md b/docs/operator-manual/notifications/services/pushover.md index 37cb20b277dcc..a09b3660f9233 100755 --- a/docs/operator-manual/notifications/services/pushover.md +++ b/docs/operator-manual/notifications/services/pushover.md @@ -1,13 +1,13 @@ # Pushover 1. Create an app at [pushover.net](https://pushover.net/apps/build). -2. Store the API key in `` Secret and define the secret name in `` ConfigMap: +2. Store the API key in `` Secret and define the secret name in `argocd-notifications-cm` ConfigMap: ```yaml apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.pushover: | token: $pushover-token diff --git a/docs/operator-manual/notifications/services/rocketchat.md b/docs/operator-manual/notifications/services/rocketchat.md index 554f42a808f01..20aaa405c80d0 100755 --- a/docs/operator-manual/notifications/services/rocketchat.md +++ b/docs/operator-manual/notifications/services/rocketchat.md @@ -43,7 +43,7 @@ stringData: apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.rocketchat: | email: $rocketchat-email @@ -64,7 +64,7 @@ metadata: ## Templates -Notification templates can be customized with RocketChat [attachments](https://developer.rocket.chat/api/rest-api/methods/chat/postmessage#attachments-detail). +[Notification templates](../templates.md) can be customized with RocketChat [attachments](https://developer.rocket.chat/api/rest-api/methods/chat/postmessage#attachments-detail). *Note: Attachments structure in Rocketchat is same with Slack attachments [feature](https://api.slack.com/messaging/composing/layouts).* diff --git a/docs/operator-manual/notifications/services/slack.md b/docs/operator-manual/notifications/services/slack.md index 0cd9a0f17708e..41bdddd7617c4 100755 --- a/docs/operator-manual/notifications/services/slack.md +++ b/docs/operator-manual/notifications/services/slack.md @@ -6,11 +6,16 @@ If you want to send message using incoming webhook, you can use [webhook](./webh The Slack notification service configuration includes following settings: -* `token` - the app token -* `apiURL` - optional, the server url, e.g. https://example.com/api -* `username` - optional, the app username -* `icon` - optional, the app icon, e.g. :robot_face: or https://example.com/image.png -* `insecureSkipVerify` - optional bool, true or false +| **Option** | **Required** | **Type** | **Description** | **Example** | +| -------------------- | ------------ | -------------- | --------------- | ----------- | +| `apiURL` | False | `string` | The server URL. | `https://example.com/api` | +| `channels` | False | `list[string]` | | `["my-channel-1", "my-channel-2"]` | +| `icon` | False | `string` | The app icon. | `:robot_face:` or `https://example.com/image.png` | +| `insecureSkipVerify` | False | `bool` | | `true` | +| `signingSecret` | False | `string` | | `8f742231b10e8888abcd99yyyzzz85a5` | +| `token` | **True** | `string` | The app's OAuth access token. | `xoxb-1234567890-1234567890123-5n38u5ed63fgzqlvuyxvxcx6` | +| `username` | False | `string` | The app username. | `argocd` | +| `disableUnfurl` | False | `bool` | Disable slack unfurling links in messages | `true` | ## Configuration @@ -29,60 +34,60 @@ The Slack notification service configuration includes following settings: 1. Invite your slack bot to this channel **otherwise slack bot won't be able to deliver notifications to this channel** 1. Store Oauth access token in `argocd-notifications-secret` secret -```yaml - apiVersion: v1 - kind: Secret - metadata: - name: - stringData: - slack-token: -``` + ```yaml + apiVersion: v1 + kind: Secret + metadata: + name: + stringData: + slack-token: + ``` 1. Define service type slack in data section of `argocd-notifications-cm` configmap: -```yaml - apiVersion: v1 - kind: ConfigMap - metadata: - name: - data: - service.slack: | - token: $slack-token -``` - -1. Add annotation in application yaml file to enable notifications for specific argocd app - -```yaml - apiVersion: argoproj.io/v1alpha1 - kind: Application - metadata: - annotations: - notifications.argoproj.io/subscribe.on-sync-succeeded.slack: my_channel -``` - -1. Annotation with more than one trigger multiple of destinations and recipients - -```yaml - apiVersion: argoproj.io/v1alpha1 - kind: Application - metadata: - annotations: - notifications.argoproj.io/subscriptions: | - - trigger: [on-scaling-replica-set, on-rollout-updated, on-rollout-step-completed] - destinations: - - service: slack - recipients: [my-channel-1, my-channel-2] - - service: email - recipients: [recipient-1, recipient-2, recipient-3 ] - - trigger: [on-rollout-aborted, on-analysis-run-failed, on-analysis-run-error] - destinations: - - service: slack - recipients: [my-channel-21, my-channel-22] -``` + ```yaml + apiVersion: v1 + kind: ConfigMap + metadata: + name: argocd-notifications-cm + data: + service.slack: | + token: $slack-token + ``` + +1. Add annotation in application yaml file to enable notifications for specific argocd app. The following example uses the [on-sync-succeeded trigger](../catalog.md#triggers): + + ```yaml + apiVersion: argoproj.io/v1alpha1 + kind: Application + metadata: + annotations: + notifications.argoproj.io/subscribe.on-sync-succeeded.slack: my_channel + ``` + +1. Annotation with more than one [trigger](../catalog.md#triggers), with multiple destinations and recipients + + ```yaml + apiVersion: argoproj.io/v1alpha1 + kind: Application + metadata: + annotations: + notifications.argoproj.io/subscriptions: | + - trigger: [on-scaling-replica-set, on-rollout-updated, on-rollout-step-completed] + destinations: + - service: slack + recipients: [my-channel-1, my-channel-2] + - service: email + recipients: [recipient-1, recipient-2, recipient-3 ] + - trigger: [on-rollout-aborted, on-analysis-run-failed, on-analysis-run-error] + destinations: + - service: slack + recipients: [my-channel-21, my-channel-22] + ``` ## Templates -Notification templates can be customized to leverage slack message blocks and attachments +[Notification templates](../templates.md) can be customized to leverage slack message blocks and attachments [feature](https://api.slack.com/messaging/composing/layouts). ![](https://user-images.githubusercontent.com/426437/72776856-6dcef880-3bc8-11ea-8e3b-c72df16ee8e6.png) diff --git a/docs/operator-manual/notifications/services/teams.md b/docs/operator-manual/notifications/services/teams.md index eb50f5538c8b6..0e44456d4de19 100755 --- a/docs/operator-manual/notifications/services/teams.md +++ b/docs/operator-manual/notifications/services/teams.md @@ -18,7 +18,7 @@ The Teams notification service send message notifications using Teams bot and re apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.teams: | recipientUrls: @@ -48,7 +48,7 @@ metadata: ![](https://user-images.githubusercontent.com/18019529/114271500-9d2b8880-9a4c-11eb-85c1-f6935f0431d5.png) -Notification templates can be customized to leverage teams message sections, facts, themeColor, summary and potentialAction [feature](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using). +[Notification templates](../templates.md) can be customized to leverage teams message sections, facts, themeColor, summary and potentialAction [feature](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using). ```yaml template.app-sync-succeeded: | @@ -113,7 +113,7 @@ template.app-sync-succeeded: | ### summary field -You can set a summary of the message that will be shown on Notifcation & Activity Feed +You can set a summary of the message that will be shown on Notification & Activity Feed ![](https://user-images.githubusercontent.com/6957724/116587921-84c4d480-a94d-11eb-9da4-f365151a12e7.jpg) @@ -123,4 +123,4 @@ You can set a summary of the message that will be shown on Notifcation & Activit template.app-sync-succeeded: | teams: summary: "Sync Succeeded" -``` \ No newline at end of file +``` diff --git a/docs/operator-manual/notifications/services/telegram.md b/docs/operator-manual/notifications/services/telegram.md index 953c2a9fca0bf..8612a09d1ca84 100755 --- a/docs/operator-manual/notifications/services/telegram.md +++ b/docs/operator-manual/notifications/services/telegram.md @@ -2,13 +2,13 @@ 1. Get an API token using [@Botfather](https://t.me/Botfather). 2. Store token in `` Secret and configure telegram integration -in `` ConfigMap: +in `argocd-notifications-cm` ConfigMap: ```yaml apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.telegram: | token: $telegram-token diff --git a/docs/operator-manual/notifications/services/webex.md b/docs/operator-manual/notifications/services/webex.md index 440ed1ddc738f..eba4c5e11b8dc 100755 --- a/docs/operator-manual/notifications/services/webex.md +++ b/docs/operator-manual/notifications/services/webex.md @@ -24,7 +24,7 @@ The Webex Teams notification service configuration includes following settings: apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.webex: | token: $webex-token diff --git a/docs/operator-manual/notifications/services/webhook.md b/docs/operator-manual/notifications/services/webhook.md index bd45b1f69e40b..4b8ca38a685ad 100755 --- a/docs/operator-manual/notifications/services/webhook.md +++ b/docs/operator-manual/notifications/services/webhook.md @@ -1,7 +1,7 @@ # Webhook The webhook notification service allows sending a generic HTTP request using the templatized request body and URL. -Using Webhook you might trigger a Jenkins job, update Github commit status. +Using Webhook you might trigger a Jenkins job, update GitHub commit status. ## Parameters @@ -9,8 +9,17 @@ The Webhook notification service configuration includes following settings: - `url` - the url to send the webhook to - `headers` - optional, the headers to pass along with the webhook -- `basicAuth` - optional, the basic authentication to pass along with the webook +- `basicAuth` - optional, the basic authentication to pass along with the webhook - `insecureSkipVerify` - optional bool, true or false +- `retryWaitMin` - Optional, the minimum wait time between retries. Default value: 1s. +- `retryWaitMax` - Optional, the maximum wait time between retries. Default value: 5s. +- `retryMax` - Optional, the maximum number of retries. Default value: 3. + +## Retry Behavior + +The webhook service will automatically retry the request if it fails due to network errors or if the server returns a 5xx status code. The number of retries and the wait time between retries can be configured using the `retryMax`, `retryWaitMin`, and `retryWaitMax` parameters. + +The wait time between retries is between `retryWaitMin` and `retryWaitMax`. If all retries fail, the `Send` method will return an error. ## Configuration @@ -22,7 +31,7 @@ Use the following steps to configure webhook: apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.webhook.: | url: https:/// @@ -41,7 +50,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: template.github-commit-status: | webhook: @@ -67,13 +76,13 @@ metadata: ## Examples -### Set Github commit status +### Set GitHub commit status ```yaml apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.webhook.github: | url: https://api.github.com @@ -88,7 +97,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.webhook.github: | url: https://api.github.com @@ -119,7 +128,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.webhook.jenkins: | url: http:///job//build?token= @@ -136,7 +145,7 @@ type: Opaque apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.webhook.form: | url: https://form.example.com @@ -157,7 +166,7 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: + name: argocd-notifications-cm data: service.webhook.slack_webhook: | url: https://hooks.slack.com/services/xxxxx diff --git a/docs/operator-manual/notifications/subscriptions.md b/docs/operator-manual/notifications/subscriptions.md index 44c500ac16f4a..e9f985280f8ad 100644 --- a/docs/operator-manual/notifications/subscriptions.md +++ b/docs/operator-manual/notifications/subscriptions.md @@ -9,13 +9,13 @@ metadata: notifications.argoproj.io/subscribe.on-sync-succeeded.slack: my-channel1;my-channel2 ``` -Annotation key consists of following parts: +The annotation key consists of following parts: * `on-sync-succeeded` - trigger name * `slack` - notification service name * `my-channel1;my-channel2` - a semicolon separated list of recipients -You can create subscriptions for all applications of the Argo CD project by adding the same annotation to AppProject CRD: +You can create subscriptions for all applications of an Argo CD project by adding the same annotation to the AppProject resource: ```yaml apiVersion: argoproj.io/v1alpha1 @@ -27,7 +27,7 @@ metadata: ## Default Subscriptions -The subscriptions might be configured globally in the `argocd-notifications-cm` ConfigMap using `subscriptions` field. The default subscriptions +The subscriptions might be configured globally in the `argocd-notifications-cm` ConfigMap using the `subscriptions` field. The default subscriptions are applied to all applications. The trigger and applications might be configured using the `triggers` and `selector` fields: @@ -53,7 +53,7 @@ data: - on-sync-status-unknown ``` -If you want to use webhook in subscriptions, you need to store the custom name to recipients. +If you want to use webhook in subscriptions, you need to store the custom webhook name in the subscription's `recipients` field. ```yaml apiVersion: v1 diff --git a/docs/operator-manual/notifications/templates.md b/docs/operator-manual/notifications/templates.md index 972c62734f739..1d80f20953b24 100644 --- a/docs/operator-manual/notifications/templates.md +++ b/docs/operator-manual/notifications/templates.md @@ -1,5 +1,5 @@ -The notification template is used to generate the notification content and configured in `argocd-notifications-cm` ConfigMap. The template is leveraging -[html/template](https://golang.org/pkg/html/template/) golang package and allow to customize notification message. +The notification template is used to generate the notification content and is configured in the `argocd-notifications-cm` ConfigMap. The template is leveraging +the [html/template](https://golang.org/pkg/html/template/) golang package and allows customization of the notification message. Templates are meant to be reusable and can be referenced by multiple triggers. The following template is used to notify the user about application sync status. @@ -19,9 +19,10 @@ data: Each template has access to the following fields: - `app` holds the application object. -- `context` is user defined string map and might include any string keys and values. -- `serviceType` holds the notification service type name. The field can be used to conditionally -render service specific fields. +- `context` is a user-defined string map and might include any string keys and values. +- `secrets` provides access to sensitive data stored in `argocd-notifications-secret` +- `serviceType` holds the notification service type name (such as "slack" or "email). The field can be used to conditionally +render service-specific fields. - `recipient` holds the recipient name. ## Defining user-defined `context` @@ -43,6 +44,39 @@ data: message: "Something happened in {{ .context.environmentName }} in the {{ .context.region }} data center!" ``` +## Defining and using secrets within notification templates + +Some notification service use cases will require the use of secrets within templates. This can be achieved with the use of +the `secrets` data variable available within the templates. + +Given that we have the following `argocd-notifications-secret`: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: argocd-notifications-secret +stringData: + sampleWebhookToken: secret-token +type: Opaque +``` + +We can use the defined `sampleWebhookToken` in a template as such: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-notifications-cm +data: + template.trigger-webhook: | + webhook: + sample-webhook: + method: POST + path: 'webhook/endpoint/with/auth' + body: 'token={{ .secrets.sampleWebhookToken }}&variables[APP_SOURCE_PATH]={{ .app.spec.source.path }} +``` + ## Notification Service Specific Fields The `message` field of the template definition allows creating a basic notification for any notification service. You can leverage notification service-specific @@ -51,30 +85,30 @@ See corresponding service [documentation](services/overview.md) for more informa ## Change the timezone -You can change the timezone to show it as follows. +You can change the timezone to show in notifications as follows. 1. Call time functions. -``` -{{ (call .time.Parse .app.status.operationState.startedAt).Local.Format "2006-01-02T15:04:05Z07:00" }} -``` + ``` + {{ (call .time.Parse .app.status.operationState.startedAt).Local.Format "2006-01-02T15:04:05Z07:00" }} + ``` -2. Set environment to container. +2. Set the `TZ` environment variable on the argocd-notifications-controller container. -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: argocd-notifications-controller -spec: -(snip) + ```yaml + apiVersion: apps/v1 + kind: Deployment + metadata: + name: argocd-notifications-controller spec: - containers: - - name: argocd-notifications-controller - env: - - name: TZ - value: Asia/Tokyo -``` + template: + spec: + containers: + - name: argocd-notifications-controller + env: + - name: TZ + value: Asia/Tokyo + ``` ## Functions diff --git a/docs/operator-manual/notifications/triggers.md b/docs/operator-manual/notifications/triggers.md index aecaee81b80c6..49a6244777959 100644 --- a/docs/operator-manual/notifications/triggers.md +++ b/docs/operator-manual/notifications/triggers.md @@ -1,9 +1,9 @@ The trigger defines the condition when the notification should be sent. The definition includes name, condition and notification templates reference. The condition is a predicate expression that returns true if the notification should be sent. The trigger condition evaluation is powered by [antonmedv/expr](https://github.com/antonmedv/expr). -The condition language syntax is described at [Language-Definition.md](https://github.com/antonmedv/expr/blob/master/docs/Language-Definition.md). +The condition language syntax is described at [language-definition.md](https://github.com/antonmedv/expr/blob/master/docs/language-definition.md). -The trigger is configured in `argocd-notifications-cm` ConfigMap. For example the following trigger sends a notification +The trigger is configured in the `argocd-notifications-cm` ConfigMap. For example the following trigger sends a notification when application sync status changes to `Unknown` using the `app-sync-status` template: ```yaml @@ -17,9 +17,9 @@ data: send: [app-sync-status, github-commit-status] # template names ``` -Each condition might use several templates. Typically each template is responsible for generating a service-specific notification part. -In the example above `app-sync-status` template "knows" how to create email and slack notification and `github-commit-status` knows how to -generate payload for Github webhook. +Each condition might use several templates. Typically, each template is responsible for generating a service-specific notification part. +In the example above, the `app-sync-status` template "knows" how to create email and Slack notification, and `github-commit-status` knows how to +generate the payload for GitHub webhook. ## Conditions Bundles @@ -28,7 +28,6 @@ The end users just need to subscribe to the trigger and specify the notification triggers might include multiple conditions with a different set of templates for each condition. For example, the following trigger covers all stages of sync status operation and use a different template for different cases: - ```yaml apiVersion: v1 kind: ConfigMap @@ -66,9 +65,13 @@ data: send: [app-sync-succeeded] ``` +**Mono Repo Usage** + +When one repo is used to sync multiple applications, the `oncePer: app.status.sync.revision` field will trigger a notification for each commit. For mono repos, the better approach will be using `oncePer: app.status.operationState.syncResult.revision` statement. This way a notification will be sent only for a particular Application's revision. + ### oncePer -The `oncePer` filed is supported like as follows. +The `oncePer` field is supported like as follows. ```yaml apiVersion: argoproj.io/v1alpha1 diff --git a/docs/operator-manual/notifications/troubleshooting-commands.md b/docs/operator-manual/notifications/troubleshooting-commands.md index 633eb47d71690..8674e9677c1eb 100644 --- a/docs/operator-manual/notifications/troubleshooting-commands.md +++ b/docs/operator-manual/notifications/troubleshooting-commands.md @@ -39,6 +39,7 @@ argocd admin notifications template get app-sync-succeeded -o=yaml --cluster string The name of the kubeconfig cluster to use --config-map string argocd-notifications-cm.yaml file path --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster -n, --namespace string If present, the namespace scope for this CLI request @@ -95,6 +96,7 @@ argocd admin notifications template notify app-sync-succeeded guestbook --cluster string The name of the kubeconfig cluster to use --config-map string argocd-notifications-cm.yaml file path --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster -n, --namespace string If present, the namespace scope for this CLI request @@ -150,6 +152,7 @@ argocd admin notifications trigger get on-sync-failed -o=yaml --cluster string The name of the kubeconfig cluster to use --config-map string argocd-notifications-cm.yaml file path --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster -n, --namespace string If present, the namespace scope for this CLI request @@ -205,6 +208,7 @@ argocd admin notifications trigger run on-sync-status-unknown ./sample-app.yaml --cluster string The name of the kubeconfig cluster to use --config-map string argocd-notifications-cm.yaml file path --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster -n, --namespace string If present, the namespace scope for this CLI request diff --git a/docs/operator-manual/notifications/troubleshooting-errors.md b/docs/operator-manual/notifications/troubleshooting-errors.md index c46d4c47468e0..f76bb7a2b0d3f 100644 --- a/docs/operator-manual/notifications/troubleshooting-errors.md +++ b/docs/operator-manual/notifications/troubleshooting-errors.md @@ -36,6 +36,6 @@ You need to check your argocd-notifications controller version. For instance, th ## Failed to notify recipient -### notification service 'xxxx' is not supported" +### notification service 'xxxx' is not supported You have not defined `xxxx` in `argocd-notifications-cm` or to fail to parse settings. diff --git a/docs/operator-manual/notifications/troubleshooting.md b/docs/operator-manual/notifications/troubleshooting.md index 68ea134e0c52a..616cd4b024e82 100644 --- a/docs/operator-manual/notifications/troubleshooting.md +++ b/docs/operator-manual/notifications/troubleshooting.md @@ -1,32 +1,30 @@ -## Troubleshooting - -The `argocd admin notifications` is a CLI command group that helps to configure the controller -settings and troubleshoot issues. +`argocd admin notifications` is a CLI command group that helps to configure the controller +settings and troubleshoot issues. Full command details are available in the [command reference](../../user-guide/commands/argocd_admin_notifications.md). ## Global flags -Following global flags are available for all sub-commands: +The following global flags are available for all sub-commands: -* `config-map` - path to the file containing `argocd-notifications-cm` ConfigMap. If not specified +* `--config-map` - path to the file containing `argocd-notifications-cm` ConfigMap. If not specified then the command loads `argocd-notification-cm` ConfigMap using the local Kubernetes config file. -* `secret` - path to the file containing `argocd-notifications-secret` ConfigMap. If not +* `--secret` - path to the file containing `argocd-notifications-secret` ConfigMap. If not specified then the command loads `argocd-notification-secret` Secret using the local Kubernetes config file. -Additionally, you can specify `:empty` value to use empty secret with no notification service settings. +Additionally, you can specify `:empty` to use empty secret with no notification service settings. **Examples:** -* Get list of triggers configured in the local config map: +* Get a list of triggers configured in the local config map: -```bash -argocd admin notifications trigger get \ - --config-map ./argocd admin notifications-cm.yaml --secret :empty -``` + ```bash + argocd admin notifications trigger get \ + --config-map ./argocd-notifications-cm.yaml --secret :empty + ``` * Trigger notification using in-cluster config map and secret: -```bash -argocd admin notifications template notify \ - app-sync-succeeded guestbook --recipient slack:argocd admin notifications -``` + ```bash + argocd admin notifications template notify \ + app-sync-succeeded guestbook --recipient slack:argocd admin notifications + ``` ## Kustomize @@ -44,17 +42,17 @@ kustomize build ./argocd-notifications | \ ### On your laptop -You can download the `argocd` CLI from the github [release](https://github.com/argoproj/argo-cd/releases) +You can download the `argocd` CLI from the GitHub [release](https://github.com/argoproj/argo-cd/releases) attachments. -The binary is available in `argoproj/argo-cd` image. Use the `docker run` and volume mount +The binary is available in the `quay.io/argoproj/argocd` image. Use the `docker run` and volume mount to execute binary on any platform. **Example:** ```bash docker run --rm -it -w /src -v $(pwd):/src \ - argoproj/argo-cd: \ + quay.io/argoproj/argocd: \ /app/argocd admin notifications trigger get \ --config-map ./argocd-notifications-cm.yaml --secret :empty ``` @@ -72,7 +70,12 @@ kubectl exec -it argocd-notifications-controller- \ ## Commands -{!docs/operator-manual/notifications/troubleshooting-commands.md!} +The following commands may help debug issues with notifications: + +* [`argocd admin notifications template get`](../../user-guide/commands/argocd_admin_notifications_template_get.md) +* [`argocd admin notifications template notify`](../../user-guide/commands/argocd_admin_notifications_template_notify.md) +* [`argocd admin notifications trigger get`](../../user-guide/commands/argocd_admin_notifications_trigger_get.md) +* [`argocd admin notifications trigger run`](../../user-guide/commands/argocd_admin_notifications_trigger_run.md) ## Errors diff --git a/docs/operator-manual/project-specification.md b/docs/operator-manual/project-specification.md new file mode 100644 index 0000000000000..4d18eb1a9dd1b --- /dev/null +++ b/docs/operator-manual/project-specification.md @@ -0,0 +1,7 @@ +# Project Specification + +The following describes all the available fields of a Project: + +```yaml +{!docs/operator-manual/project.yaml!} +``` diff --git a/docs/operator-manual/project.yaml b/docs/operator-manual/project.yaml index 20dcfa87ab29c..c4d93f536239f 100644 --- a/docs/operator-manual/project.yaml +++ b/docs/operator-manual/project.yaml @@ -86,3 +86,13 @@ spec: clusters: - in-cluster - cluster1 + + # By default, apps may sync to any cluster specified under the `destinations` field, even if they are not + # scoped to this project. Set the following field to `true` to restrict apps in this cluster to only clusters + # scoped to this project. + permitOnlyProjectScopedClusters: false + + # When using Applications-in-any-namespace, this field determines which namespaces this AppProject permits + # Applications to reside in. Details: https://argo-cd.readthedocs.io/en/stable/operator-manual/app-any-namespace/ + sourceNamespaces: + - "argocd-apps-*" diff --git a/docs/operator-manual/rbac.md b/docs/operator-manual/rbac.md index fac03e4b2f744..b1d386fb5eb8e 100644 --- a/docs/operator-manual/rbac.md +++ b/docs/operator-manual/rbac.md @@ -2,7 +2,7 @@ The RBAC feature enables restriction of access to Argo CD resources. Argo CD does not have its own user management system and has only one built-in user `admin`. The `admin` user is a superuser and -it has unrestricted access to the system. RBAC requires [SSO configuration](user-management/index.md) or [one or more local users setup](user-management/index.md). +it has unrestricted access to the system. RBAC requires [SSO configuration](user-management/index.md) or [one or more local users setup](user-management/index.md). Once SSO or local users are configured, additional RBAC roles can be defined, and SSO groups or local users can then be mapped to roles. ## Basic Built-in Roles @@ -22,13 +22,15 @@ Breaking down the permissions definition differs slightly between applications a `p, , , , ` -* Applications, applicationsets, logs, and exec (which belong to an AppProject): +* Applications, applicationsets, logs, and exec (which belong to an `AppProject`): `p, , , , /` ### RBAC Resources and Actions -Resources: `clusters`, `projects`, `applications`, `applicationsets`, `repositories`, `certificates`, `accounts`, `gpgkeys`, `logs`, `exec` +Resources: `clusters`, `projects`, `applications`, `applicationsets`, +`repositories`, `certificates`, `accounts`, `gpgkeys`, `logs`, `exec`, +`extensions` Actions: `get`, `create`, `update`, `delete`, `sync`, `override`,`action/` @@ -55,18 +57,25 @@ corresponds to the `action` path `action/extensions/DaemonSet/restart`. You can also use glob patterns in the action path: `action/*` (or regex patterns if you have [enabled the `regex` match mode](https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/argocd-rbac-cm.yaml)). +If the resource is not under a group (for examples, Pods or ConfigMaps), then omit the group name from your RBAC +configuration: + +```csv +p, example-user, applications, action//Pod/maintenance-off, default/*, allow +``` + #### The `exec` resource -`exec` is a special resource. When enabled with the `create` action, this privilege allows a user to `exec` into Pods via +`exec` is a special resource. When enabled with the `create` action, this privilege allows a user to `exec` into Pods via the Argo CD UI. The functionality is similar to `kubectl exec`. See [Web-based Terminal](web_based_terminal.md) for more info. #### The `applicationsets` resource -[ApplicationSets](applicationset) provide a declarative way to automatically create/update/delete Applications. +[ApplicationSets](applicationset/index.md) provide a declarative way to automatically create/update/delete Applications. -Granting `applicationsets, create` effectively grants the ability to create Applications. While it doesn't allow the +Granting `applicationsets, create` effectively grants the ability to create Applications. While it doesn't allow the user to create Applications directly, they can create Applications via an ApplicationSet. In v2.5, it is not possible to create an ApplicationSet with a templated Project field (e.g. `project: {{path.basename}}`) @@ -79,6 +88,40 @@ p, dev-group, applicationsets, *, dev-project/*, allow With this rule in place, a `dev-group` user will be unable to create an ApplicationSet capable of creating Applications outside the `dev-project` project. +#### The `extensions` resource + +With the `extensions` resource it is possible configure permissions to +invoke [proxy +extensions](../developer-guide/extensions/proxy-extensions.md). The +`extensions` RBAC validation works in conjunction with the +`applications` resource. A user logged in Argo CD (UI or CLI), needs +to have at least read permission on the project, namespace and +application where the request is originated from. + +Consider the example below: + +```csv +g, ext, role:extension +p, role:extension, applications, get, default/httpbin-app, allow +p, role:extension, extensions, invoke, httpbin, allow +``` + +Explanation: + +* *line1*: defines the group `role:extension` associated with the + subject `ext`. +* *line2*: defines a policy allowing this role to read (`get`) the + `httpbin-app` application in the `default` project. +* *line3*: defines another policy allowing this role to `invoke` the + `httpbin` extension. + +**Note 1**: that for extensions requests to be allowed, the policy defined +in the *line2* is also required. + +**Note 2**: `invoke` is a new action introduced specifically to be used +with the `extensions` resource. The current actions for `extensions` +are `*` or `invoke`. + ## Tying It All Together Additional roles and groups can be configured in `argocd-rbac-cm` ConfigMap. The example below @@ -87,7 +130,7 @@ configures a custom role, named `org-admin`. The role is assigned to any user wh which cannot modify Argo CD settings. !!! warning - All authenticated users get _at least_ the permissions granted by the default policy. This access cannot be blocked + All authenticated users get *at least* the permissions granted by the default policy. This access cannot be blocked by a `deny` rule. Instead, restrict the default policy and then grant permissions to individual roles as needed. *ArgoCD ConfigMap `argocd-rbac-cm` Example:* @@ -107,29 +150,96 @@ data: p, role:org-admin, repositories, create, *, allow p, role:org-admin, repositories, update, *, allow p, role:org-admin, repositories, delete, *, allow + p, role:org-admin, projects, get, *, allow + p, role:org-admin, projects, create, *, allow + p, role:org-admin, projects, update, *, allow + p, role:org-admin, projects, delete, *, allow p, role:org-admin, logs, get, *, allow p, role:org-admin, exec, create, */*, allow g, your-github-org:your-team, role:org-admin ``` + ---- Another `policy.csv` example might look as follows: ```csv -p, role:staging-db-admins, applications, create, staging-db-admins/*, allow -p, role:staging-db-admins, applications, delete, staging-db-admins/*, allow -p, role:staging-db-admins, applications, get, staging-db-admins/*, allow -p, role:staging-db-admins, applications, override, staging-db-admins/*, allow -p, role:staging-db-admins, applications, sync, staging-db-admins/*, allow -p, role:staging-db-admins, applications, update, staging-db-admins/*, allow -p, role:staging-db-admins, logs, get, staging-db-admins/*, allow -p, role:staging-db-admins, exec, create, staging-db-admins/*, allow -p, role:staging-db-admins, projects, get, staging-db-admins, allow -g, db-admins, role:staging-db-admins +p, role:staging-db-admin, applications, create, staging-db-project/*, allow +p, role:staging-db-admin, applications, delete, staging-db-project/*, allow +p, role:staging-db-admin, applications, get, staging-db-project/*, allow +p, role:staging-db-admin, applications, override, staging-db-project/*, allow +p, role:staging-db-admin, applications, sync, staging-db-project/*, allow +p, role:staging-db-admin, applications, update, staging-db-project/*, allow +p, role:staging-db-admin, logs, get, staging-db-project/*, allow +p, role:staging-db-admin, exec, create, staging-db-project/*, allow +p, role:staging-db-admin, projects, get, staging-db-project, allow +g, db-admins, role:staging-db-admin ``` -This example defines a *role* called `staging-db-admins` with *nine permissions* that allow that role to perform the *actions* (`create`/`delete`/`get`/`override`/`sync`/`update` applications, `get` logs, `create` exec and `get` appprojects) against `*` (all) objects in the `staging-db-admins` Argo CD AppProject. +This example defines a *role* called `staging-db-admin` with nine *permissions* that allow users with that role to perform the following *actions*: + +* `create`, `delete`, `get`, `override`, `sync` and `update` for applications in the `staging-db-project` project, +* `get` logs for objects in the `staging-db-project` project, +* `create` exec for objects in the `staging-db-project` project, and +* `get` for the project named `staging-db-project`. + +!!! note + The `scopes` field controls which OIDC scopes to examine during rbac + enforcement (in addition to `sub` scope). If omitted, defaults to: + `'[groups]'`. The scope value can be a string, or a list of strings. + +Following example shows targeting `email` as well as `groups` from your OIDC provider. + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-rbac-cm + namespace: argocd + labels: + app.kubernetes.io/name: argocd-rbac-cm + app.kubernetes.io/part-of: argocd +data: + policy.csv: | + p, my-org:team-alpha, applications, sync, my-project/*, allow + g, my-org:team-beta, role:admin + g, user@example.org, role:admin + policy.default: role:readonly + scopes: '[groups, email]' +``` + +For more information on `scopes` please review the [User Management Documentation](user-management/index.md). + +## Policy CSV Composition + +It is possible to provide additional entries in the `argocd-rbac-cm` +configmap to compose the final policy csv. In this case the key must +follow the pattern `policy..csv`. Argo CD will concatenate +all additional policies it finds with this pattern below the main one +('policy.csv'). The order of additional provided policies are +determined by the key string. Example: if two additional policies are +provided with keys `policy.A.csv` and `policy.B.csv`, it will first +concatenate `policy.A.csv` and then `policy.B.csv`. + +This is useful to allow composing policies in config management tools +like Kustomize, Helm, etc. + +The example below shows how a Kustomize patch can be provided in an +overlay to add additional configuration to an existing RBAC policy. + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-rbac-cm + namespace: argocd +data: + policy.tester-overlay.csv: | + p, role:tester, applications, *, */*, allow + p, role:tester, projects, *, *, allow + g, my-org:team-qa, role:tester +``` ## Anonymous Access @@ -184,38 +294,43 @@ Given the example from the above ConfigMap, which defines the role `role:org-admin`, and is stored on your local system as `argocd-rbac-cm-yaml`, you can test whether that role can do something like follows: -```shell +```console $ argocd admin settings rbac can role:org-admin get applications --policy-file argocd-rbac-cm.yaml Yes + $ argocd admin settings rbac can role:org-admin get clusters --policy-file argocd-rbac-cm.yaml Yes + $ argocd admin settings rbac can role:org-admin create clusters 'somecluster' --policy-file argocd-rbac-cm.yaml No + $ argocd admin settings rbac can role:org-admin create applications 'someproj/someapp' --policy-file argocd-rbac-cm.yaml Yes ``` Another example, given the policy above from `policy.csv`, which defines the -role `role:staging-db-admins` and associates the group `db-admins` with it. +role `role:staging-db-admin` and associates the group `db-admins` with it. Policy is stored locally as `policy.csv`: You can test against the role: -```shell -# Plain policy, without a default role defined -$ argocd admin settings rbac can role:staging-db-admins get applications --policy-file policy.csv +```console +$ # Plain policy, without a default role defined +$ argocd admin settings rbac can role:staging-db-admin get applications --policy-file policy.csv No -$ argocd admin settings rbac can role:staging-db-admins get applications 'staging-db-admins/*' --policy-file policy.csv + +$ argocd admin settings rbac can role:staging-db-admin get applications 'staging-db-project/*' --policy-file policy.csv Yes -# Argo CD augments a builtin policy with two roles defined, the default role -# being 'role:readonly' - You can include a named default role to use: -$ argocd admin settings rbac can role:staging-db-admins get applications --policy-file policy.csv --default-role role:readonly + +$ # Argo CD augments a builtin policy with two roles defined, the default role +$ # being 'role:readonly' - You can include a named default role to use: +$ argocd admin settings rbac can role:staging-db-admin get applications --policy-file policy.csv --default-role role:readonly Yes ``` Or against the group defined: -```shell -$ argocd admin settings rbac can db-admins get applications 'staging-db-admins/*' --policy-file policy.csv +```console +$ argocd admin settings rbac can db-admins get applications 'staging-db-project/*' --policy-file policy.csv Yes ``` diff --git a/docs/operator-manual/reconcile.md b/docs/operator-manual/reconcile.md new file mode 100644 index 0000000000000..a956cd9cf7b28 --- /dev/null +++ b/docs/operator-manual/reconcile.md @@ -0,0 +1,113 @@ +# Reconcile Optimization + +By default, an Argo CD Application is refreshed every time a resource that belongs to it changes. + +Kubernetes controllers often update the resources they watch periodically, causing continuous reconcile operation on the Application +and a high CPU usage on the `argocd-application-controller`. Argo CD allows you to optionally ignore resource updates on specific fields +for [tracked resources](../user-guide/resource_tracking.md). + +When a resource update is ignored, if the resource's [health status](./health.md) does not change, the Application that this resource belongs to will not be reconciled. + +## System-Level Configuration + +Argo CD allows ignoring resource updates at a specific JSON path, using [RFC6902 JSON patches](https://tools.ietf.org/html/rfc6902) and [JQ path expressions](https://stedolan.github.io/jq/manual/#path(path_expression)). It can be configured for a specified group and kind +in `resource.customizations` key of the `argocd-cm` ConfigMap. + +!!!important "Enabling the feature" + The feature is behind a flag. To enable it, set `resource.ignoreResourceUpdatesEnabled` to `"true"` in the `argocd-cm` ConfigMap. + +Following is an example of a customization which ignores the `refreshTime` status field of an [`ExternalSecret`](https://external-secrets.io/main/api/externalsecret/) resource: + +```yaml +data: + resource.customizations.ignoreResourceUpdates.external-secrets.io_ExternalSecret: | + jsonPointers: + - /status/refreshTime + # JQ equivalent of the above: + # jqPathExpressions: + # - .status.refreshTime +``` + +It is possible to configure `ignoreResourceUpdates` to be applied to all tracked resources in every Application managed by an Argo CD instance. In order to do so, resource customizations can be configured like in the example below: + +```yaml +data: + resource.customizations.ignoreResourceUpdates.all: | + jsonPointers: + - /status +``` + +### Using ignoreDifferences to ignore reconcile + +It is possible to use existing system-level `ignoreDifferences` customizations to ignore resource updates as well. Instead of copying all configurations, +the `ignoreDifferencesOnResourceUpdates` setting can be used to add all ignored differences as ignored resource updates: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cm +data: + resource.compareoptions: | + ignoreDifferencesOnResourceUpdates: true +``` + +## Default Configuration + +By default, the metadata fields `generation`, `resourceVersion` and `managedFields` are always ignored for all resources. + +## Finding Resources to Ignore + +The application controller logs when a resource change triggers a refresh. You can use these logs to find +high-churn resource kinds and then inspect those resources to find which fields to ignore. + +To find these logs, search for `"Requesting app refresh caused by object update"`. The logs include structured +fields for `api-version` and `kind`. Counting the number of refreshes triggered, by api-version/kind should +reveal the high-churn resource kinds. + +!!!note + These logs are at the `debug` level. Configure the application-controller's log level to `debug`. + +Once you have identified some resources which change often, you can try to determine which fields are changing. Here is +one approach: + +```shell +kubectl get -o yaml > /tmp/before.yaml +# Wait a minute or two. +kubectl get -o yaml > /tmp/after.yaml +diff /tmp/before.yaml /tmp/after +``` + +The diff can give you a sense for which fields are changing and should perhaps be ignored. + +## Checking Whether Resource Updates are Ignored + +Whenever Argo CD skips a refresh due to an ignored resource update, the controller logs the following line: +"Ignoring change of object because none of the watched resource fields have changed". + +Search the application-controller logs for this line to confirm that your resource ignore rules are being applied. + +!!!note + These logs are at the `debug` level. Configure the application-controller's log level to `debug`. + +## Examples + +### argoproj.io/Application + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cm +data: + resource.customizations.ignoreResourceUpdates.argoproj.io_Application: | + jsonPointers: + # Ignore when ownerReferences change, for example when a parent ApplicationSet changes often. + - /metadata/ownerReferences + # Ignore reconciledAt, since by itself it doesn't indicate any important change. + - /status/reconciledAt + jqPathExpressions: + # Ignore lastTransitionTime for conditions; helpful when SharedResourceWarnings are being regularly updated but not + # actually changing in content. + - .status.conditions[].lastTransitionTime +``` diff --git a/docs/operator-manual/resource_actions.md b/docs/operator-manual/resource_actions.md index 833eef4ffa534..b720f589ae8d0 100644 --- a/docs/operator-manual/resource_actions.md +++ b/docs/operator-manual/resource_actions.md @@ -9,15 +9,33 @@ Operators can add actions to custom resources in form of a Lua script and expand Argo CD supports custom resource actions written in [Lua](https://www.lua.org/). This is useful if you: - * Have a custom resource for which Argo CD does not provide any built-in actions. - * Have a commonly performed manual task that might be error prone if executed by users via `kubectl` +* Have a custom resource for which Argo CD does not provide any built-in actions. +* Have a commonly performed manual task that might be error prone if executed by users via `kubectl` +The resource actions act on a single object. You can define your own custom resource actions in the `argocd-cm` ConfigMap. +### Custom Resource Action Types + +#### An action that modifies the source resource + +This action modifies and returns the source resource. +This kind of action was the only one available till 2.8, and it is still supported. + +#### An action that produces a list of new or modified resources + +**An alpha feature, introduced in 2.8.** + +This action returns a list of impacted resources, each impacted resource has a K8S resource and an operation to perform on. +Currently supported operations are "create" and "patch", "patch" is only supported for the source resource. +Creating new resources is possible, by specifying a "create" operation for each such resource in the returned list. +One of the returned resources can be the modified source object, with a "patch" operation, if needed. +See the definition examples below. + ### Define a Custom Resource Action in `argocd-cm` ConfigMap -Custom resource actions can be defined in `resource.customizations.actions.` field of `argocd-cm`. Following example demonstrates a set of custom actions for `CronJob` resources. +Custom resource actions can be defined in `resource.customizations.actions.` field of `argocd-cm`. Following example demonstrates a set of custom actions for `CronJob` resources, each such action returns the modified CronJob. The customizations key is in the format of `resource.customizations.actions.`. ```yaml @@ -52,4 +70,114 @@ resource.customizations.actions.batch_CronJob: | The `discovery.lua` script must return a table where the key name represents the action name. You can optionally include logic to enable or disable certain actions based on the current object state. -Each action name must be represented in the list of `definitions` with an accompanying `action.lua` script to control the resource modifications. The `obj` is a global variable which contains the resource. Each action script must return an optionally modified version of the resource. In this example, we are simply setting `.spec.suspend` to either `true` or `false`. +Each action name must be represented in the list of `definitions` with an accompanying `action.lua` script to control the resource modifications. The `obj` is a global variable which contains the resource. Each action script returns an optionally modified version of the resource. In this example, we are simply setting `.spec.suspend` to either `true` or `false`. + +#### Creating new resources with a custom action + +!!! important + Creating resources via the Argo CD UI is an intentional, strategic departure from GitOps principles. We recommend + that you use this feature sparingly and only for resources that are not part of the desired state of the + application. + +The resource the action is invoked on would be referred to as the `source resource`. +The new resource and all the resources implicitly created as a result, must be permitted on the AppProject level, otherwise the creation will fail. + +##### Creating a source resource child resources with a custom action + +If the new resource represents a k8s child of the source resource, the source resource ownerReference must be set on the new resource. +Here is an example Lua snippet, that takes care of constructing a Job resource that is a child of a source CronJob resource - the `obj` is a global variable, which contains the source resource: + +```lua +-- ... +ownerRef = {} +ownerRef.apiVersion = obj.apiVersion +ownerRef.kind = obj.kind +ownerRef.name = obj.metadata.name +ownerRef.uid = obj.metadata.uid +job = {} +job.metadata = {} +job.metadata.ownerReferences = {} +job.metadata.ownerReferences[1] = ownerRef +-- ... +``` + +##### Creating independent child resources with a custom action + +If the new resource is independent of the source resource, the default behavior of such new resource is that it is not known by the App of the source resource (as it is not part of the desired state and does not have an `ownerReference`). +To make the App aware of the new resource, the `app.kubernetes.io/instance` label (or other ArgoCD tracking label, if configured) must be set on the resource. +It can be copied from the source resource, like this: + +```lua +-- ... +newObj = {} +newObj.metadata = {} +newObj.metadata.labels = {} +newObj.metadata.labels["app.kubernetes.io/instance"] = obj.metadata.labels["app.kubernetes.io/instance"] +-- ... +``` + +While the new resource will be part of the App with the tracking label in place, it will be immediately deleted if auto prune is set on the App. +To keep the resource, set `Prune=false` annotation on the resource, with this Lua snippet: + +```lua +-- ... +newObj.metadata.annotations = {} +newObj.metadata.annotations["argocd.argoproj.io/sync-options"] = "Prune=false" +-- ... +``` + +(If setting `Prune=false` behavior, the resource will not be deleted upon the deletion of the App, and will require a manual cleanup). + +The resource and the App will now appear out of sync - which is the expected ArgoCD behavior upon creating a resource that is not part of the desired state. + +If you wish to treat such an App as a synced one, add the following resource annotation in Lua code: + +```lua +-- ... +newObj.metadata.annotations["argocd.argoproj.io/compare-options"] = "IgnoreExtraneous" +-- ... +``` + +#### An action that produces a list of resources - a complete example: + +```yaml +resource.customizations.actions.ConfigMap: | + discovery.lua: | + actions = {} + actions["do-things"] = {} + return actions + definitions: + - name: do-things + action.lua: | + -- Create a new ConfigMap + cm1 = {} + cm1.apiVersion = "v1" + cm1.kind = "ConfigMap" + cm1.metadata = {} + cm1.metadata.name = "cm1" + cm1.metadata.namespace = obj.metadata.namespace + cm1.metadata.labels = {} + -- Copy ArgoCD tracking label so that the resource is recognized by the App + cm1.metadata.labels["app.kubernetes.io/instance"] = obj.metadata.labels["app.kubernetes.io/instance"] + cm1.metadata.annotations = {} + -- For Apps with auto-prune, set the prune false on the resource, so it does not get deleted + cm1.metadata.annotations["argocd.argoproj.io/sync-options"] = "Prune=false" + -- Keep the App synced even though it has a resource that is not in Git + cm1.metadata.annotations["argocd.argoproj.io/compare-options"] = "IgnoreExtraneous" + cm1.data = {} + cm1.data.myKey1 = "myValue1" + impactedResource1 = {} + impactedResource1.operation = "create" + impactedResource1.resource = cm1 + + -- Patch the original cm + obj.metadata.labels["aKey"] = "aValue" + impactedResource2 = {} + impactedResource2.operation = "patch" + impactedResource2.resource = obj + + result = {} + result[1] = impactedResource1 + result[2] = impactedResource2 + return result +``` \ No newline at end of file diff --git a/docs/operator-manual/secret-management.md b/docs/operator-manual/secret-management.md index 54795fe4cc56c..aa224e20ff742 100644 --- a/docs/operator-manual/secret-management.md +++ b/docs/operator-manual/secret-management.md @@ -10,7 +10,7 @@ Here are some ways people are doing GitOps secrets: * [Bitnami Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets) * [External Secrets Operator](https://github.com/external-secrets/external-secrets) * [Hashicorp Vault](https://www.vaultproject.io) -* [Banzai Cloud Bank-Vaults](https://github.com/banzaicloud/bank-vaults) +* [Bank-Vaults](https://bank-vaults.dev/) * [Helm Secrets](https://github.com/jkroepke/helm-secrets) * [Kustomize secret generator plugins](https://github.com/kubernetes-sigs/kustomize/blob/fd7a353df6cece4629b8e8ad56b71e30636f38fc/examples/kvSourceGoPlugin.md#secret-values-from-anywhere) * [aws-secret-operator](https://github.com/mumoshu/aws-secret-operator) @@ -18,6 +18,7 @@ Here are some ways people are doing GitOps secrets: * [argocd-vault-plugin](https://github.com/argoproj-labs/argocd-vault-plugin) * [argocd-vault-replacer](https://github.com/crumbhole/argocd-vault-replacer) * [Kubernetes Secrets Store CSI Driver](https://github.com/kubernetes-sigs/secrets-store-csi-driver) +* [Vals-Operator](https://github.com/digitalis-io/vals-operator) For discussion, see [#1364](https://github.com/argoproj/argo-cd/issues/1364) diff --git a/docs/operator-manual/security.md b/docs/operator-manual/security.md index 593030e1756e4..9d05c45cb7c74 100644 --- a/docs/operator-manual/security.md +++ b/docs/operator-manual/security.md @@ -30,7 +30,7 @@ in one of the following ways: ## Authorization Authorization is performed by iterating the list of group membership in a user's JWT groups claims, -and comparing each group against the roles/rules in the [RBAC](../rbac) policy. Any matched rule +and comparing each group against the roles/rules in the [RBAC](./rbac.md) policy. Any matched rule permits access to the API request. ## TLS @@ -45,7 +45,7 @@ Communication with Redis is performed over plain HTTP by default. TLS can be set Git and helm repositories are managed by a stand-alone service, called the repo-server. The repo-server does not carry any Kubernetes privileges and does not store credentials to any services (including git). The repo-server is responsible for cloning repositories which have been permitted -and trusted by Argo CD operators, and generating kubernetes manifests at a given path in the +and trusted by Argo CD operators, and generating Kubernetes manifests at a given path in the repository. For performance and bandwidth efficiency, the repo-server maintains local clones of these repositories so that subsequent commits to the repository are efficiently downloaded. @@ -109,7 +109,7 @@ The information is used to reconstruct a REST config and kubeconfig to the clust services. To rotate the bearer token used by Argo CD, the token can be deleted (e.g. using kubectl) which -causes kubernetes to generate a new secret with a new bearer token. The new token can be re-inputted +causes Kubernetes to generate a new secret with a new bearer token. The new token can be re-inputted to Argo CD by re-running `argocd cluster add`. Run the following commands against the *_managed_* cluster: @@ -144,7 +144,7 @@ argocd cluster rm https://your-kubernetes-cluster-addr ## Cluster RBAC -By default, Argo CD uses a [clusteradmin level role](https://github.com/argoproj/argo-cd/blob/master/manifests/base/application-controller/argocd-application-controller-role.yaml) +By default, Argo CD uses a [clusteradmin level role](https://github.com/argoproj/argo-cd/blob/master/manifests/base/application-controller-roles/argocd-application-controller-role.yaml) in order to: 1. watch & operate on cluster state @@ -173,7 +173,7 @@ kubectl edit clusterrole argocd-application-controller ``` !!! tip - If you want to deny ArgoCD access to a kind of resource then add it as an [excluded resource](declarative-setup.md#resource-exclusion). + If you want to deny Argo CD access to a kind of resource then add it as an [excluded resource](declarative-setup.md#resource-exclusion). ## Auditing diff --git a/docs/operator-manual/server-commands/argocd-application-controller.md b/docs/operator-manual/server-commands/argocd-application-controller.md index 85145b5540ef2..61c0c32119895 100644 --- a/docs/operator-manual/server-commands/argocd-application-controller.md +++ b/docs/operator-manual/server-commands/argocd-application-controller.md @@ -1,3 +1,5 @@ +# `argocd-application-controller` Command Reference + ## argocd-application-controller Run ArgoCD Application Controller @@ -13,56 +15,71 @@ argocd-application-controller [flags] ### Options ``` - --app-hard-resync int Time period in seconds for application hard resync. - --app-resync int Time period in seconds for application resync. (default 180) - --app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s) - --application-namespaces strings List of additional namespaces that applications are allowed to be reconciled from - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --as-uid string UID to impersonate for the operation - --certificate-authority string Path to a cert file for the certificate authority - --client-certificate string Path to a client certificate file for TLS - --client-key string Path to a client key file for TLS - --cluster string The name of the kubeconfig cluster to use - --context string The name of the kubeconfig context to use - --default-cache-expiration duration Cache expiration default (default 24h0m0s) - --gloglevel int Set the glog logging level - -h, --help help for argocd-application-controller - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to a kube config. Only required if out-of-cluster - --kubectl-parallelism-limit int Number of allowed concurrent kubectl fork/execs. Any value less the 1 means no limit. (default 20) - --logformat string Set the logging format. One of: text|json (default "text") - --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") - --metrics-application-labels strings List of Application labels that will be added to the argocd_application_labels metric - --metrics-cache-expiration duration Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s) - --metrics-port int Start metrics server on given port (default 8082) - -n, --namespace string If present, the namespace scope for this CLI request - --operation-processors int Number of application operation processors (default 10) - --otlp-address string OpenTelemetry collector address to send traces to - --password string Password for basic authentication to the API server - --persist-resource-health Enables storing the managed resources health in the Application CRD (default true) - --proxy-url string If provided, this URL will be used to connect via proxy - --redis string Redis server hostname and port (e.g. argocd-redis:6379). - --redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation. - --redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt). - --redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt). - --redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: none, gzip) (default "none") - --redis-insecure-skip-tls-verify Skip Redis server certificate validation. - --redis-use-tls Use TLS when connecting to Redis. - --redisdb int Redis database. - --repo-server string Repo server address. (default "argocd-repo-server:8081") - --repo-server-plaintext Disable TLS on connections to repo server - --repo-server-strict-tls Whether to use strict validation of the TLS cert presented by the repo server - --repo-server-timeout-seconds int Repo server RPC call timeout seconds. (default 60) - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --self-heal-timeout-seconds int Specifies timeout between application self heal attempts (default 5) - --sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379). - --sentinelmaster string Redis sentinel master group name. (default "master") - --server string The address and port of the Kubernetes API server - --status-processors int Number of application status processors (default 20) - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server + --app-hard-resync int Time period in seconds for application hard resync. + --app-resync int Time period in seconds for application resync. (default 180) + --app-resync-jitter int Maximum time period in seconds to add as a delay jitter for application resync. + --app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s) + --application-namespaces strings List of additional namespaces that applications are allowed to be reconciled from + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --as-uid string UID to impersonate for the operation + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --context string The name of the kubeconfig context to use + --default-cache-expiration duration Cache expiration default (default 24h0m0s) + --disable-compression If true, opt-out of response compression for all requests to the server + --dynamic-cluster-distribution-enabled Enables dynamic cluster distribution. + --gloglevel int Set the glog logging level + -h, --help help for argocd-application-controller + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --kubectl-parallelism-limit int Number of allowed concurrent kubectl fork/execs. Any value less than 1 means no limit. (default 20) + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + --metrics-application-labels strings List of Application labels that will be added to the argocd_application_labels metric + --metrics-cache-expiration duration Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s) + --metrics-port int Start metrics server on given port (default 8082) + -n, --namespace string If present, the namespace scope for this CLI request + --operation-processors int Number of application operation processors (default 10) + --otlp-address string OpenTelemetry collector address to send traces to + --otlp-attrs strings List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value) + --otlp-headers stringToString List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2) (default []) + --otlp-insecure OpenTelemetry collector insecure mode (default true) + --password string Password for basic authentication to the API server + --persist-resource-health Enables storing the managed resources health in the Application CRD (default true) + --proxy-url string If provided, this URL will be used to connect via proxy + --redis string Redis server hostname and port (e.g. argocd-redis:6379). + --redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation. + --redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt). + --redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt). + --redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip") + --redis-insecure-skip-tls-verify Skip Redis server certificate validation. + --redis-use-tls Use TLS when connecting to Redis. + --redisdb int Redis database. + --repo-error-grace-period-seconds int Grace period in seconds for ignoring consecutive errors while communicating with repo server. (default 180) + --repo-server string Repo server address. (default "argocd-repo-server:8081") + --repo-server-plaintext Disable TLS on connections to repo server + --repo-server-strict-tls Whether to use strict validation of the TLS cert presented by the repo server + --repo-server-timeout-seconds int Repo server RPC call timeout seconds. (default 60) + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --self-heal-timeout-seconds int Specifies timeout between application self heal attempts (default 5) + --sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379). + --sentinelmaster string Redis sentinel master group name. (default "master") + --server string The address and port of the Kubernetes API server + --server-side-diff-enabled Feature flag to enable ServerSide diff. Default ("false") + --sharding-method string Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] (default "legacy") + --status-processors int Number of application status processors (default 20) + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server + --wq-backoff-factor float Set Workqueue Per Item Rate Limiter Backoff Factor, default is 1.5 (default 1.5) + --wq-basedelay-ns duration Set Workqueue Per Item Rate Limiter Base Delay duration in nanoseconds, default 1000000 (1ms) (default 1ms) + --wq-bucket-qps float Set Workqueue Rate Limiter Bucket QPS, default set to MaxFloat64 which disables the bucket limiter (default 1.7976931348623157e+308) + --wq-bucket-size int Set Workqueue Rate Limiter Bucket Size, default 500 (default 500) + --wq-cooldown-ns duration Set Workqueue Per Item Rate Limiter Cooldown duration in ns, default 0(per item rate limiter disabled) + --wq-maxdelay-ns duration Set Workqueue Per Item Rate Limiter Max Delay duration in nanoseconds, default 1000000000 (1s) (default 1s) ``` diff --git a/docs/operator-manual/server-commands/argocd-dex.md b/docs/operator-manual/server-commands/argocd-dex.md index 89a53d4c31b56..af0eeae4a7604 100644 --- a/docs/operator-manual/server-commands/argocd-dex.md +++ b/docs/operator-manual/server-commands/argocd-dex.md @@ -1,3 +1,5 @@ +# `argocd-dex` Command Reference + ## argocd-dex argocd-dex tools used by Argo CD diff --git a/docs/operator-manual/server-commands/argocd-dex_gendexcfg.md b/docs/operator-manual/server-commands/argocd-dex_gendexcfg.md index 02118d2deef6e..a889b64133a93 100644 --- a/docs/operator-manual/server-commands/argocd-dex_gendexcfg.md +++ b/docs/operator-manual/server-commands/argocd-dex_gendexcfg.md @@ -1,3 +1,5 @@ +# `argocd-dex gendexcfg` Command Reference + ## argocd-dex gendexcfg Generates a dex config from Argo CD settings @@ -17,6 +19,7 @@ argocd-dex gendexcfg [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server --disable-tls Disable TLS on the HTTP endpoint -h, --help help for gendexcfg --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure diff --git a/docs/operator-manual/server-commands/argocd-dex_rundex.md b/docs/operator-manual/server-commands/argocd-dex_rundex.md index d4c25963e9196..b2d453feba613 100644 --- a/docs/operator-manual/server-commands/argocd-dex_rundex.md +++ b/docs/operator-manual/server-commands/argocd-dex_rundex.md @@ -1,3 +1,5 @@ +# `argocd-dex rundex` Command Reference + ## argocd-dex rundex Runs dex generating a config using settings from the Argo CD configmap and secret @@ -17,6 +19,7 @@ argocd-dex rundex [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server --disable-tls Disable TLS on the HTTP endpoint -h, --help help for rundex --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure diff --git a/docs/operator-manual/server-commands/argocd-repo-server.md b/docs/operator-manual/server-commands/argocd-repo-server.md index 322886acbba50..0f824f494f2af 100644 --- a/docs/operator-manual/server-commands/argocd-repo-server.md +++ b/docs/operator-manual/server-commands/argocd-repo-server.md @@ -1,3 +1,5 @@ +# `argocd-repo-server` Command Reference + ## argocd-repo-server Run ArgoCD Repository Server @@ -13,15 +15,23 @@ argocd-repo-server [flags] ### Options ``` + --address string Listen on given address for incoming connections (default "0.0.0.0") --allow-oob-symlinks Allow out-of-bounds symlinks in repositories (not recommended) --default-cache-expiration duration Cache expiration default (default 24h0m0s) + --disable-helm-manifest-max-extracted-size Disable maximum size of helm manifest archives when extracted --disable-tls Disable TLS on the gRPC endpoint + --helm-manifest-max-extracted-size string Maximum size of helm manifest archives when extracted (default "1G") + --helm-registry-max-index-size string Maximum size of registry index file (default "1G") -h, --help help for argocd-repo-server --logformat string Set the logging format. One of: text|json (default "text") --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") --max-combined-directory-manifests-size string Max combined size of manifest files in a directory-type Application (default "10M") + --metrics-address string Listen on given address for metrics (default "0.0.0.0") --metrics-port int Start metrics server on given port (default 8084) --otlp-address string OpenTelemetry collector address to send traces to + --otlp-attrs strings List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value) + --otlp-headers stringToString List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2) (default []) + --otlp-insecure OpenTelemetry collector insecure mode (default true) --parallelismlimit int Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit. --plugin-tar-exclude stringArray Globs to filter when sending tarballs to plugins. --port int Listen on given port for incoming connections (default 8081) @@ -29,12 +39,13 @@ argocd-repo-server [flags] --redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation. --redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt). --redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt). - --redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: none, gzip) (default "none") + --redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip") --redis-insecure-skip-tls-verify Skip Redis server certificate validation. --redis-use-tls Use TLS when connecting to Redis. --redisdb int Redis database. --repo-cache-expiration duration Cache expiration for repo state, incl. app lists, app details, manifest generation, revision meta-data (default 24h0m0s) --revision-cache-expiration duration Cache expiration for cached revision (default 3m0s) + --revision-cache-lock-timeout duration Cache TTL for locks to prevent duplicate requests on revisions, set to 0 to disable (default 10s) --sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379). --sentinelmaster string Redis sentinel master group name. (default "master") --streamed-manifest-max-extracted-size string Maximum size of streamed manifest archives when extracted (default "1G") diff --git a/docs/operator-manual/server-commands/argocd-server.md b/docs/operator-manual/server-commands/argocd-server.md index 461ac194fa341..659a19de3d3e1 100644 --- a/docs/operator-manual/server-commands/argocd-server.md +++ b/docs/operator-manual/server-commands/argocd-server.md @@ -1,3 +1,5 @@ +# `argocd-server` Command Reference + ## argocd-server Run the ArgoCD API server @@ -10,70 +12,101 @@ The API server is a gRPC/REST server which exposes the API consumed by the Web U argocd-server [flags] ``` +### Examples + +``` + # Start the Argo CD API server with default settings + $ argocd-server + + # Start the Argo CD API server on a custom port and enable tracing + $ argocd-server --port 8888 --otlp-address localhost:4317 +``` + ### Options ``` - --app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s) - --application-namespaces strings List of additional namespaces where application resources can be managed in - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --as-uid string UID to impersonate for the operation - --basehref string Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from / (default "/") - --certificate-authority string Path to a cert file for the certificate authority - --client-certificate string Path to a client certificate file for TLS - --client-key string Path to a client key file for TLS - --cluster string The name of the kubeconfig cluster to use - --connection-status-cache-expiration duration Cache expiration for cluster/repo connection status (default 1h0m0s) - --content-security-policy value Set Content-Security-Policy header in HTTP responses to value. To disable, set to "". (default "frame-ancestors 'self';") - --context string The name of the kubeconfig context to use - --default-cache-expiration duration Cache expiration default (default 24h0m0s) - --dex-server string Dex server address (default "argocd-dex-server:5556") - --dex-server-plaintext Use a plaintext client (non-TLS) to connect to dex server - --dex-server-strict-tls Perform strict validation of TLS certificates when connecting to dex server - --disable-auth Disable client authentication - --enable-gzip Enable GZIP compression - --enable-proxy-extension Enable Proxy Extension feature - --gloglevel int Set the glog logging level - -h, --help help for argocd-server - --insecure Run server without TLS - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to a kube config. Only required if out-of-cluster - --logformat string Set the logging format. One of: text|json (default "text") - --login-attempts-expiration duration Cache expiration for failed login attempts (default 24h0m0s) - --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") - --metrics-port int Start metrics on given port (default 8083) - -n, --namespace string If present, the namespace scope for this CLI request - --oidc-cache-expiration duration Cache expiration for OIDC state (default 3m0s) - --otlp-address string OpenTelemetry collector address to send traces to - --password string Password for basic authentication to the API server - --port int Listen on given port (default 8080) - --proxy-url string If provided, this URL will be used to connect via proxy - --redis string Redis server hostname and port (e.g. argocd-redis:6379). - --redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation. - --redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt). - --redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt). - --redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: none, gzip) (default "none") - --redis-insecure-skip-tls-verify Skip Redis server certificate validation. - --redis-use-tls Use TLS when connecting to Redis. - --redisdb int Redis database. - --repo-server string Repo server address (default "argocd-repo-server:8081") - --repo-server-plaintext Use a plaintext client (non-TLS) to connect to repository server - --repo-server-strict-tls Perform strict validation of TLS certificates when connecting to repo server - --repo-server-timeout-seconds int Repo server RPC call timeout seconds. (default 60) - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --rootpath string Used if Argo CD is running behind reverse proxy under subpath different from / - --sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379). - --sentinelmaster string Redis sentinel master group name. (default "master") - --server string The address and port of the Kubernetes API server - --staticassets string Directory path that contains additional static assets (default "/shared/app") - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --tlsciphers string The list of acceptable ciphers to be used when establishing TLS connections. Use 'list' to list available ciphers. (default "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:TLS_RSA_WITH_AES_256_GCM_SHA384") - --tlsmaxversion string The maximum SSL/TLS version that is acceptable (one of: 1.0|1.1|1.2|1.3) (default "1.3") - --tlsminversion string The minimum SSL/TLS version that is acceptable (one of: 1.0|1.1|1.2|1.3) (default "1.2") - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server - --x-frame-options value Set X-Frame-Options header in HTTP responses to value. To disable, set to "". (default "sameorigin") + --address string Listen on given address (default "0.0.0.0") + --api-content-types string Semicolon separated list of allowed content types for non GET api requests. Any content type is allowed if empty. (default "application/json") + --app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s) + --application-namespaces strings List of additional namespaces where application resources can be managed in + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --as-uid string UID to impersonate for the operation + --basehref string Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from / (default "/") + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --connection-status-cache-expiration duration Cache expiration for cluster/repo connection status (default 1h0m0s) + --content-security-policy value Set Content-Security-Policy header in HTTP responses to value. To disable, set to "". (default "frame-ancestors 'self';") + --context string The name of the kubeconfig context to use + --default-cache-expiration duration Cache expiration default (default 24h0m0s) + --dex-server string Dex server address (default "argocd-dex-server:5556") + --dex-server-plaintext Use a plaintext client (non-TLS) to connect to dex server + --dex-server-strict-tls Perform strict validation of TLS certificates when connecting to dex server + --disable-auth Disable client authentication + --disable-compression If true, opt-out of response compression for all requests to the server + --enable-gzip Enable GZIP compression (default true) + --enable-proxy-extension Enable Proxy Extension feature + --gloglevel int Set the glog logging level + -h, --help help for argocd-server + --insecure Run server without TLS + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --logformat string Set the logging format. One of: text|json (default "text") + --login-attempts-expiration duration Cache expiration for failed login attempts (default 24h0m0s) + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + --metrics-address string Listen for metrics on given address (default "0.0.0.0") + --metrics-port int Start metrics on given port (default 8083) + -n, --namespace string If present, the namespace scope for this CLI request + --oidc-cache-expiration duration Cache expiration for OIDC state (default 3m0s) + --otlp-address string OpenTelemetry collector address to send traces to + --otlp-attrs strings List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value) + --otlp-headers stringToString List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2) (default []) + --otlp-insecure OpenTelemetry collector insecure mode (default true) + --password string Password for basic authentication to the API server + --port int Listen on given port (default 8080) + --proxy-url string If provided, this URL will be used to connect via proxy + --redis string Redis server hostname and port (e.g. argocd-redis:6379). + --redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation. + --redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt). + --redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt). + --redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip") + --redis-insecure-skip-tls-verify Skip Redis server certificate validation. + --redis-use-tls Use TLS when connecting to Redis. + --redisdb int Redis database. + --repo-cache-expiration duration Cache expiration for repo state, incl. app lists, app details, manifest generation, revision meta-data (default 24h0m0s) + --repo-server string Repo server address (default "argocd-repo-server:8081") + --repo-server-default-cache-expiration duration Cache expiration default (default 24h0m0s) + --repo-server-plaintext Use a plaintext client (non-TLS) to connect to repository server + --repo-server-redis string Redis server hostname and port (e.g. argocd-redis:6379). + --repo-server-redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation. + --repo-server-redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt). + --repo-server-redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt). + --repo-server-redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip") + --repo-server-redis-insecure-skip-tls-verify Skip Redis server certificate validation. + --repo-server-redis-use-tls Use TLS when connecting to Redis. + --repo-server-redisdb int Redis database. + --repo-server-sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379). + --repo-server-sentinelmaster string Redis sentinel master group name. (default "master") + --repo-server-strict-tls Perform strict validation of TLS certificates when connecting to repo server + --repo-server-timeout-seconds int Repo server RPC call timeout seconds. (default 60) + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --revision-cache-expiration duration Cache expiration for cached revision (default 3m0s) + --revision-cache-lock-timeout duration Cache TTL for locks to prevent duplicate requests on revisions, set to 0 to disable (default 10s) + --rootpath string Used if Argo CD is running behind reverse proxy under subpath different from / + --sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379). + --sentinelmaster string Redis sentinel master group name. (default "master") + --server string The address and port of the Kubernetes API server + --staticassets string Directory path that contains additional static assets (default "/shared/app") + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --tlsciphers string The list of acceptable ciphers to be used when establishing TLS connections. Use 'list' to list available ciphers. (default "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:TLS_RSA_WITH_AES_256_GCM_SHA384") + --tlsmaxversion string The maximum SSL/TLS version that is acceptable (one of: 1.0|1.1|1.2|1.3) (default "1.3") + --tlsminversion string The minimum SSL/TLS version that is acceptable (one of: 1.0|1.1|1.2|1.3) (default "1.2") + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server + --x-frame-options value Set X-Frame-Options header in HTTP responses to value. To disable, set to "". (default "sameorigin") ``` ### SEE ALSO diff --git a/docs/operator-manual/server-commands/argocd-server_version.md b/docs/operator-manual/server-commands/argocd-server_version.md index 413ea6be9c3f3..2659c99e87219 100644 --- a/docs/operator-manual/server-commands/argocd-server_version.md +++ b/docs/operator-manual/server-commands/argocd-server_version.md @@ -1,3 +1,5 @@ +# `argocd-server version` Command Reference + ## argocd-server version Print version information @@ -24,6 +26,7 @@ argocd-server version [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster -n, --namespace string If present, the namespace scope for this CLI request diff --git a/docs/operator-manual/signed-release-assets.md b/docs/operator-manual/signed-release-assets.md index d5aa36dc9eea1..b574876345b5b 100644 --- a/docs/operator-manual/signed-release-assets.md +++ b/docs/operator-manual/signed-release-assets.md @@ -1,31 +1,156 @@ -# Verification of Argo CD signatures - -All Argo CD container images are signed by cosign. Checksums are created for the CLI binaries and then signed to ensure integrity. +# Verification of Argo CD Artifacts ## Prerequisites -- Cosign [installation instructions](https://docs.sigstore.dev/cosign/installation) -- Obtain or have a copy of ```argocd-cosign.pub```, which can be located in the assets section of the [release page](https://github.com/argoproj/argo-cd/releases) - -Once you have installed cosign, you can use ```argocd-cosign.pub``` to verify the signed assets or container images. +- cosign `v2.0.0` or higher [installation instructions](https://docs.sigstore.dev/cosign/installation) +- slsa-verifier [installation instructions](https://github.com/slsa-framework/slsa-verifier#installation) +- crane [installation instructions](https://github.com/google/go-containerregistry/blob/main/cmd/crane/README.md) (for container verification only) +*** +## Release Assets +| Asset | Description | +|--------------------------|-------------------------------| +| argocd-darwin-amd64 | CLI Binary | +| argocd-darwin-arm64 | CLI Binary | +| argocd-linux_amd64 | CLI Binary | +| argocd-linux_arm64 | CLI Binary | +| argocd-linux_ppc64le | CLI Binary | +| argocd-linux_s390x | CLI Binary | +| argocd-windows_amd64 | CLI Binary | +| argocd-cli.intoto.jsonl | Attestation of CLI binaries | +| argocd-sbom.intoto.jsonl | Attestation of SBOM | +| cli_checksums.txt | Checksums of binaries | +| sbom.tar.gz | Sbom | +| sbom.tar.gz.pem | Certificate used to sign sbom | +| sbom.tar.gz.sig | Signature of sbom | +*** ## Verification of container images -```bash -cosign verify --key argocd-cosign.pub quay.io/argoproj/argocd: +Argo CD container images are signed by [cosign](https://github.com/sigstore/cosign) using identity-based ("keyless") signing and transparency. Executing the following command can be used to verify the signature of a container image: -Verification for quay.io/argoproj/argocd: -- +```bash +cosign verify \ +--certificate-identity-regexp https://github.com/argoproj/argo-cd/.github/workflows/image-reuse.yaml@refs/tags/v \ +--certificate-oidc-issuer https://token.actions.githubusercontent.com \ +quay.io/argoproj/argocd:v2.7.0 | jq +``` +The command should output the following if the container image was correctly verified: +```bash The following checks were performed on each of these signatures: - * The cosign claims were validated - * The signatures were verified against the specified public key -... + - The cosign claims were validated + - Existence of the claims in the transparency log was verified offline + - Any certificates were verified against the Fulcio roots. +[ + { + "critical": { + "identity": { + "docker-reference": "quay.io/argoproj/argo-cd" + }, + "image": { + "docker-manifest-digest": "sha256:63dc60481b1b2abf271e1f2b866be8a92962b0e53aaa728902caa8ac8d235277" + }, + "type": "cosign container image signature" + }, + "optional": { + "1.3.6.1.4.1.57264.1.1": "https://token.actions.githubusercontent.com", + "1.3.6.1.4.1.57264.1.2": "push", + "1.3.6.1.4.1.57264.1.3": "a6ec84da0eaa519cbd91a8f016cf4050c03323b2", + "1.3.6.1.4.1.57264.1.4": "Publish ArgoCD Release", + "1.3.6.1.4.1.57264.1.5": "argoproj/argo-cd", + "1.3.6.1.4.1.57264.1.6": "refs/tags/", + ... +``` + +*** +## Verification of container image with SLSA attestations + +A [SLSA](https://slsa.dev/) Level 3 provenance is generated using [slsa-github-generator](https://github.com/slsa-framework/slsa-github-generator). + +The following command will verify the signature of an attestation and how it was issued. It will contain the payloadType, payload, and signature. + +Run the following command as per the [slsa-verifier documentation](https://github.com/slsa-framework/slsa-verifier/tree/main#containers): + +```bash +# Get the immutable container image to prevent TOCTOU attacks https://github.com/slsa-framework/slsa-verifier#toctou-attacks +IMAGE=quay.io/argoproj/argocd:v2.7.0 +IMAGE="${IMAGE}@"$(crane digest "${IMAGE}") +# Verify provenance, including the tag to prevent rollback attacks. +slsa-verifier verify-image "$IMAGE" \ + --source-uri github.com/argoproj/argo-cd \ + --source-tag v2.7.0 +``` + +If you only want to verify up to the major or minor verion of the source repository tag (instead of the full tag), use the `--source-versioned-tag` which performs semantic versioning verification: + +```shell +slsa-verifier verify-image "$IMAGE" \ + --source-uri github.com/argoproj/argo-cd \ + --source-versioned-tag v2 # Note: May use v2.7 for minor version verification. ``` -## Verification of signed assets + +The attestation payload contains a non-forgeable provenance which is base64 encoded and can be viewed by passing the `--print-provenance` option to the commands above: ```bash -cosign verify-blob --key cosign.pub --signature $(cat argocd--checksums.sig) argocd-$VERSION-checksums.txt -Verified OK +slsa-verifier verify-image "$IMAGE" \ + --source-uri github.com/argoproj/argo-cd \ + --source-tag v2.7.0 \ + --print-provenance | jq ``` -## Admission controllers -Cosign is compatible with several types of admission controllers. Please see the [Cosign documentation](https://docs.sigstore.dev/cosign/overview/#kubernetes-integrations) for supported controllers +If you prefer using cosign, follow these [instructions](https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#cosign). + +!!! tip + `cosign` or `slsa-verifier` can both be used to verify image attestations. + Check the documentation of each binary for detailed instructions. + +*** + +## Verification of CLI artifacts with SLSA attestations + +A single attestation (`argocd-cli.intoto.jsonl`) from each release is provided. This can be used with [slsa-verifier](https://github.com/slsa-framework/slsa-verifier#verification-for-github-builders) to verify that a CLI binary was generated using Argo CD workflows on GitHub and ensures it was cryptographically signed. + +```bash +slsa-verifier verify-artifact argocd-linux-amd64 \ + --provenance-path argocd-cli.intoto.jsonl \ + --source-uri github.com/argoproj/argo-cd \ + --source-tag v2.7.0 +``` + +If you only want to verify up to the major or minor verion of the source repository tag (instead of the full tag), use the `--source-versioned-tag` which performs semantic versioning verification: + +```shell +slsa-verifier verify-artifact argocd-linux-amd64 \ + --provenance-path argocd-cli.intoto.jsonl \ + --source-uri github.com/argoproj/argo-cd \ + --source-versioned-tag v2 # Note: May use v2.7 for minor version verification. +``` + +The payload is a non-forgeable provenance which is base64 encoded and can be viewed by passing the `--print-provenance` option to the commands above: + +```bash +slsa-verifier verify-artifact argocd-linux-amd64 \ + --provenance-path argocd-cli.intoto.jsonl \ + --source-uri github.com/argoproj/argo-cd \ + --source-tag v2.7.0 \ + --print-provenance | jq +``` + +## Verification of Sbom + +A single attestation (`argocd-sbom.intoto.jsonl`) from each release is provided along with the sbom (`sbom.tar.gz`). This can be used with [slsa-verifier](https://github.com/slsa-framework/slsa-verifier#verification-for-github-builders) to verify that the SBOM was generated using Argo CD workflows on GitHub and ensures it was cryptographically signed. + +```bash +slsa-verifier verify-artifact sbom.tar.gz \ + --provenance-path argocd-sbom.intoto.jsonl \ + --source-uri github.com/argoproj/argo-cd \ + --source-tag v2.7.0 +``` + +*** +## Verification on Kubernetes + +### Policy controllers +!!! note + We encourage all users to verify signatures and provenances with your admission/policy controller of choice. Doing so will verify that an image was built by us before it's deployed on your Kubernetes cluster. + +Cosign signatures and SLSA provenances are compatible with several types of admission controllers. Please see the [cosign documentation](https://docs.sigstore.dev/cosign/overview/#kubernetes-integrations) and [slsa-github-generator](https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#verification) for supported controllers. diff --git a/docs/operator-manual/tested-kubernetes-versions.md b/docs/operator-manual/tested-kubernetes-versions.md new file mode 100644 index 0000000000000..897620296a515 --- /dev/null +++ b/docs/operator-manual/tested-kubernetes-versions.md @@ -0,0 +1,6 @@ +| Argo CD version | Kubernetes versions | +|-----------------|---------------------| +| 2.7 | v1.26, v1.25, v1.24, v1.23 | +| 2.6 | v1.24, v1.23, v1.22 | +| 2.5 | v1.24, v1.23, v1.22 | + diff --git a/docs/operator-manual/tls.md b/docs/operator-manual/tls.md index 3b80e765f17dd..43409fc568f43 100644 --- a/docs/operator-manual/tls.md +++ b/docs/operator-manual/tls.md @@ -224,7 +224,10 @@ to not use TLS at all. In this case, you will need to: * Configure `argocd-repo-server` with TLS on the gRPC API disabled by specifying - the `--disable-tls` parameter to the pod container's startup arguments + the `--disable-tls` parameter to the pod container's startup arguments. + Also, consider restricting listening addresses to the loopback interface by specifying + `--listen 127.0.0.1` parameter, so that insecure endpoint is not exposed on + the pod's network interfaces, but still available to the side-car container. * Configure `argocd-server` and `argocd-application-controller` to not use TLS for connections to the `argocd-repo-server` by specifying the parameter `--repo-server-plaintext` to the pod container's startup arguments diff --git a/docs/operator-manual/troubleshooting.md b/docs/operator-manual/troubleshooting.md index 884045410b0b8..0e0159e5def4f 100644 --- a/docs/operator-manual/troubleshooting.md +++ b/docs/operator-manual/troubleshooting.md @@ -25,7 +25,7 @@ argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argo **Health Assessment** -Argo CD provides built-in [health assessment](./health.md) for several kubernetes resources which can be further +Argo CD provides built-in [health assessment](./health.md) for several Kubernetes resources which can be further customized by writing your own health checks in [Lua](https://www.lua.org/). The health checks are configured in the `resource.customizations` field of `argocd-cm` ConfigMap. diff --git a/docs/operator-manual/ui-customization.md b/docs/operator-manual/ui-customization.md new file mode 100644 index 0000000000000..c6b463e577f50 --- /dev/null +++ b/docs/operator-manual/ui-customization.md @@ -0,0 +1,9 @@ +# UI Customization + +## Default Application Details View + +By default, the Application Details will show the `Tree` view. + +This can be configured on an Application basis, by setting the `pref.argocd.argoproj.io/default-view` annotation, accepting one of: `tree`, `pods`, `network`, `list` as values. + +For the Pods view, the default grouping mechanism can be configured using the `pref.argocd.argoproj.io/default-pod-sort` annotation, accepting one of: `node`, `parentResource`, `topLevelResource` as values. \ No newline at end of file diff --git a/docs/operator-manual/upgrading/1.8-2.0.md b/docs/operator-manual/upgrading/1.8-2.0.md index 8b623f70c73a6..fa74d15420d3e 100644 --- a/docs/operator-manual/upgrading/1.8-2.0.md +++ b/docs/operator-manual/upgrading/1.8-2.0.md @@ -1,4 +1,4 @@ -# v1.8 to v2.0 +# v1.8 to 2.0 ## Redis Upgraded to v6.2.1 diff --git a/docs/operator-manual/upgrading/2.10-2.11.md b/docs/operator-manual/upgrading/2.10-2.11.md new file mode 100644 index 0000000000000..4cf5c8ed02b0b --- /dev/null +++ b/docs/operator-manual/upgrading/2.10-2.11.md @@ -0,0 +1,5 @@ +# v2.10 to 2.11 + +## initiatedBy added in Application CRD + +In order to address [argoproj/argo-cd#16612](https://github.com/argoproj/argo-cd/issues/16612), initiatedBy has been added in the Application CRD. \ No newline at end of file diff --git a/docs/operator-manual/upgrading/2.3-2.4.md b/docs/operator-manual/upgrading/2.3-2.4.md index f877063226419..6e8c70ff8d9ab 100644 --- a/docs/operator-manual/upgrading/2.3-2.4.md +++ b/docs/operator-manual/upgrading/2.3-2.4.md @@ -1,5 +1,26 @@ # v2.3 to 2.4 +## Known Issues + +### Broken `project` filter before 2.4.27 + +Argo CD 2.4.0 introduced a breaking API change, renaming the `project` filter to `projects`. + +#### Impact to API clients + +A similar issue applies to other API clients which communicate with the Argo CD API server via its REST API. If the +client uses the `project` field to filter projects, the filter will not be applied. **The failing project filter could +have detrimental consequences if, for example, you rely on it to list Applications to be deleted.** + +#### Impact to CLI clients + +CLI clients older that v2.4.0 rely on client-side filtering and are not impacted by this bug. + +#### How to fix the problem + +Upgrade to Argo CD >=2.4.27, >=2.5.15, or >=2.6.6. This version of Argo CD will accept both `project` and `projects` as +valid filters. + ## KSonnet support is removed Ksonnet was deprecated in [2019](https://github.com/ksonnet/ksonnet/pull/914/files) and is no longer maintained. diff --git a/docs/operator-manual/upgrading/2.4-2.5.md b/docs/operator-manual/upgrading/2.4-2.5.md index f1e7d74dc7819..8971c7cd8e3a4 100644 --- a/docs/operator-manual/upgrading/2.4-2.5.md +++ b/docs/operator-manual/upgrading/2.4-2.5.md @@ -1,5 +1,57 @@ # v2.4 to 2.5 +## Known Issues + +### Broken `project` filter before 2.5.15 + +Argo CD 2.4.0 introduced a breaking API change, renaming the `project` filter to `projects`. + +#### Impact to API clients + +A similar issue applies to other API clients which communicate with the Argo CD API server via its REST API. If the +client uses the `project` field to filter projects, the filter will not be applied. **The failing project filter could +have detrimental consequences if, for example, you rely on it to list Applications to be deleted.** + +#### Impact to CLI clients + +CLI clients older that v2.4.0 rely on client-side filtering and are not impacted by this bug. + +#### How to fix the problem + +Upgrade to Argo CD >=2.4.27, >=2.5.15, or >=2.6.6. This version of Argo CD will accept both `project` and `projects` as +valid filters. + +### Broken matrix-nested git files generator in 2.5.14 + +Argo CD 2.5.14 introduced a bug in the matrix-nested git files generator. The bug only applies when the git files +generator is the second generator nested under a matrix. For example: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: guestbook +spec: + generators: + - matrix: + generators: + - clusters: {} + - git: + repoURL: https://git.example.com/org/repo.git + revision: HEAD + files: + - path: "defaults/*.yaml" + template: + # ... +``` + +The nested git files generator will produce no parameters, causing the matrix generator to also produce no parameters. +This will cause the ApplicationSet to produce no Applications. If the ApplicationSet controller is +[configured with the ability to delete applications](https://argo-cd.readthedocs.io/en/latest/operator-manual/applicationset/Controlling-Resource-Modification/), +it will delete all Applications which were previously created by the ApplicationSet. + +To avoid this issue, upgrade directly to >=2.5.15 or >= 2.6.6. + ## Configure RBAC to account for new `applicationsets` resource 2.5 introduces a new `applicationsets` [RBAC resource](https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/#rbac-resources-and-actions). @@ -34,7 +86,7 @@ p, role:org-admin, exec, create, *, allow ## argocd-cm plugins (CMPs) are deprecated Starting with Argo CD v2.5, installing config management plugins (CMPs) via the `argocd-cm` ConfigMap is deprecated. -Support will be removed in v2.6. +~~Support will be removed in v2.6.~~ Support will be removed in v2.7. You can continue to use the plugins by [installing them as sidecars](https://argo-cd.readthedocs.io/en/stable/user-guide/config-management-plugins/) on the repo-server Deployment. @@ -47,6 +99,8 @@ following message: > argocd-cm plugins are deprecated, and support will be removed in v2.6. Upgrade your plugin to be installed via sidecar. https://argo-cd.readthedocs.io/en/stable/user-guide/config-management-plugins/ +**NOTE:** removal of argocd-cm plugin support was delayed to v2.7. Update your logs scan to use `v2.7` instead of `v2.6`. + If you run `argocd app list` as admin, the list of Applications using deprecated plugins will be logged as a warning. ## Dex server TLS configuration @@ -97,14 +151,14 @@ When using `argocd app diff --local`, code from the repo server is run on the us In order to support CMPs and reduce local requirements, we have implemented *server-side generation* of local manifests via the `--server-side-generate` argument. For example, `argocd app diff --local repoDir --server-side-generate` will upload the contents of `repoDir` to the repo server and run your manifest generation pipeline against it, the same as it would for a Git repo. -In v2.6, the `--server-side-generate` argument will become the default and client-side generation will be removed. +In ~~v2.6~~ v2.7, the `--server-side-generate` argument will become the default, ~~and client-side generation will be removed~~ and client-side generation will be supported as an alternative. !!! warning The semantics of *where* Argo will start generating manifests within a repo has changed between client-side and server-side generation. With client-side generation, the application's path (`spec.source.path`) was ignored and the value of `--local-repo-root` was effectively used (by default `/` relative to `--local`). For example, given an application that has an application path of `/manifests`, you would have had to run `argocd app diff --local yourRepo/manifests`. This behavior did not match the repo server's process of downloading the full repo/chart and then beginning generation in the path specified in the application manifest. - When switching to server-side generation, `--local` should point to the root of your repo *without* including your `spec.source.path`. This is especially important to keep in mind when `--server-side-generate` becomes the default in v2.6. Existing scripts utilizing `diff --local` may break in v2.6 if `spec.source.path` was not `/`. + When switching to server-side generation, `--local` should point to the root of your repo *without* including your `spec.source.path`. This is especially important to keep in mind when `--server-side-generate` becomes the default in v2.7. Existing scripts utilizing `diff --local` may break in v2.7 if `spec.source.path` was not `/`. ## Upgraded Kustomize Version @@ -130,3 +184,21 @@ This note is just for clarity. No action is required. We [expected](../upgrading/2.3-2.4.md#enable-logs-rbac-enforcement) to enable logs RBAC enforcement by default in 2.5. We have decided not to do that in the 2.x series due to disruption for users of [Project Roles](../../user-guide/projects.md#project-roles). + +## `argocd app create` for old CLI versions fails with API version >=2.5.16 + +Starting with Argo CD 2.5.16, the API returns `PermissionDenied` instead of `NotFound` for Application `GET` requests if +the Application does not exist. + +The Argo CD CLI before versions starting with version 2.5.0-rc1 and before versions 2.5.16 and 2.6.7 does a `GET` +request before the `POST` request in `argocd app create`. The command does not gracefully handle the `PermissionDenied` +response and will therefore fail to create/update the Application. + +To solve the issue, upgrade the CLI to at least 2.5.16, or 2.6.7. + +CLIs older than 2.5.0-rc1 are unaffected. + +## Golang upgrade in 2.5.20 + +In 2.5.20, we upgrade the Golang version used to build Argo CD from 1.18 to 1.19. If you use Argo CD as a library, you +may need to upgrade your Go version. diff --git a/docs/operator-manual/upgrading/2.5-2.6.md b/docs/operator-manual/upgrading/2.5-2.6.md index 65864c88896ab..57f1373721445 100644 --- a/docs/operator-manual/upgrading/2.5-2.6.md +++ b/docs/operator-manual/upgrading/2.5-2.6.md @@ -1,5 +1,57 @@ # v2.5 to 2.6 +## Known Issues + +### Broken `project` filter before 2.6.6 + +Argo CD 2.4.0 introduced a breaking API change, renaming the `project` filter to `projects`. + +#### Impact to API clients + +A similar issue applies to other API clients which communicate with the Argo CD API server via its REST API. If the +client uses the `project` field to filter projects, the filter will not be applied. **The failing project filter could +have detrimental consequences if, for example, you rely on it to list Applications to be deleted.** + +#### Impact to CLI clients + +CLI clients older that v2.4.0 rely on client-side filtering and are not impacted by this bug. + +#### How to fix the problem + +Upgrade to Argo CD >=2.4.27, >=2.5.15, or >=2.6.6. This version of Argo CD will accept both `project` and `projects` as +valid filters. + +### Broken matrix-nested git files generator in 2.6.5 + +Argo CD 2.6.5 introduced a bug in the matrix-nested git files generator. The bug only applies when the git files +generator is the second generator nested under a matrix. For example: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: guestbook +spec: + generators: + - matrix: + generators: + - clusters: {} + - git: + repoURL: https://git.example.com/org/repo.git + revision: HEAD + files: + - path: "defaults/*.yaml" + template: + # ... +``` + +The nested git files generator will produce no parameters, causing the matrix generator to also produce no parameters. +This will cause the ApplicationSet to produce no Applications. If the ApplicationSet controller is +[configured with the ability to delete applications](https://argo-cd.readthedocs.io/en/latest/operator-manual/applicationset/Controlling-Resource-Modification/), +it will delete all Applications which were previously created by the ApplicationSet. + +To avoid this issue, upgrade directly to >=2.5.15 or >= 2.6.6. + ## ApplicationSets: `^` behavior change in Sprig's semver functions Argo CD 2.5 introduced [Go templating in ApplicationSets](https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/GoTemplate/). Go templates have access to the Sprig function library. @@ -10,4 +62,36 @@ Masterminds/semver v3 changed the behavior of the `^` prefix in semantic version ## Applications with suspended jobs now marked "Suspended" instead of "Progressing" Prior to Argo CD v2.6, an Application managing a suspended Job would be marked as "Progressing". This was confusing/unexpected behavior for many. Starting with v2.6, Argo CD will mark such Applications as "Suspended". -If you have processes which rely on the previous behavior (for example, a CI job with an argocd app wait call), update those before upgrading to v2.6. \ No newline at end of file +If you have processes which rely on the previous behavior (for example, a CI job with an argocd app wait call), update those before upgrading to v2.6. + +## The API Server now requires tokens to include the `aud` claim by default + +Argo CD v2.6 now requires that the `aud` claim be present in the token used to authenticate to the API Server. This is a +security improvement, as it prevents tokens from being used against the API Server which were not intended for it. + +If you rely on an OIDC provider which does not provide a `aud` claim, you can disable this requirement by setting the +`skipAudienceCheckWhenTokenHasNoAudience` flag to `true` in your Argo CD OIDC configuration. (See the +[OIDC configuration documentation](https://argo-cd.readthedocs.io/en/stable/operator-manual/user-management/#existing-oidc-provider) +for an example.) + +## Removal of argocd-cm plugin support delayed until 2.7 + +Support for argocd-cm plugins was previously scheduled for 2.6. At the time, sidecar plugins could not be specified by +name. Argo CD v2.6 introduces support for specifying sidecar plugins by name. + +Removal of argocd-cm plugin support has been delayed until 2.7 to provide a transition time for users who need to +specify plugins by name. + +## `argocd app create` for old CLI versions fails with API version >=2.6.7 + +Starting with Argo CD 2.6.7, the API returns `PermissionDenied` instead of `NotFound` for Application `GET` requests if +the Application does not exist. + +The Argo CD CLI before versions starting with version 2.5.0-rc1 and before versions 2.5.16 and 2.6.7 does a `GET` +request before the `POST` request in `argocd app create`. The command does not gracefully handle the `PermissionDenied` +response and will therefore fail to create/update the Application. + +To solve the issue, upgrade the CLI to at least 2.5.16, or 2.6.7. + +CLIs older than 2.5.0-rc1 are unaffected. + diff --git a/docs/operator-manual/upgrading/2.6-2.7.md b/docs/operator-manual/upgrading/2.6-2.7.md new file mode 100644 index 0000000000000..fa7fba02bf1b7 --- /dev/null +++ b/docs/operator-manual/upgrading/2.6-2.7.md @@ -0,0 +1,108 @@ +# v2.6 to 2.7 + +## Configure RBAC to account for new `extensions` resource + +2.7 introduces the new [Proxy Extensions][1] feature with a new `extensions` +[RBAC resource][2]. + +When you upgrade to 2.7, RBAC policies with `*` in the *resource* +field and `*` in the action field, it will automatically grant the +`extensions` privilege. + +The Proxy Extension feature is disabled by default, however it is +recommended to check your RBAC configurations to enforce the least +necessary privileges. + +Example +Old: + +```csv +p, role:org-admin, *, *, *, allow +``` + +New: + +```csv +p, role:org-admin, clusters, create, my-proj/*, allow +p, role:org-admin, projects, create, my-proj/*, allow +p, role:org-admin, applications, create, my-proj/*, allow +p, role:org-admin, repositories, create, my-proj/*, allow +p, role:org-admin, certificates, create, my-proj/*, allow +p, role:org-admin, accounts, create, my-proj/*, allow +p, role:org-admin, gpgkeys, create, my-proj/*, allow +# If you don't want to grant the new permission, don't include the following line +p, role:org-admin, extensions, invoke, my-proj/*, allow +``` + +## Upgraded Helm Version + +Note that bundled Helm version has been upgraded from 3.10.3 to 3.11.2. + +## Upgraded Kustomize Version + +Note that bundled Kustomize version has been upgraded from 4.5.7 to 5.0.1. + +## Notifications: `^` behavior change in Sprig's semver functions +Argo CD 2.7 upgrades Sprig templating specifically within Argo CD notifications to v3. That upgrade includes an upgrade of [Masterminds/semver](https://github.com/Masterminds/semver/releases) to v3. + +Masterminds/semver v3 changed the behavior of the `^` prefix in semantic version constraints. If you are using sprig template functions in your notifications templates which include references to [Sprig's semver functions](https://masterminds.github.io/sprig/semver.html) and use the `^` prefix, read the [Masterminds/semver changelog](https://github.com/Masterminds/semver/releases/tag/v3.0.0) to understand how your notifications' behavior may change. + +## Tini as entrypoint + +The manifests are now using [`tini` as entrypoint][3], instead of `entrypoint.sh`. Until 2.8, `entrypoint.sh` is retained for upgrade compatibility. This means that the deployment manifests have to be updated after upgrading to 2.7, and before upgrading to 2.8 later. In case the manifests are updated before moving to 2.8, the containers will not be able to start. + +[1]: ../../developer-guide/extensions/proxy-extensions.md +[2]: https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/#the-extensions-resource +[3]: https://github.com/argoproj/argo-cd/pull/12707 + + +## Deep Links template updates + +Deep Links now allow you to access other values like `cluster`, `project`, `application` and `resource` in the url and condition templates for specific categories of links. +The templating syntax has also been updated to be prefixed with the type of resource you want to access for example previously if you had a `resource.links` config like : +```yaml + resource.links: | + - url: https://mycompany.splunk.com?search={{.metadata.name}} + title: Splunk + if: kind == "Pod" || kind == "Deployment" +``` +This would become : +```yaml + resource.links: | + - url: https://mycompany.splunk.com?search={{.resource.metadata.name}}&env={{.project.metadata.label.env}} + title: Splunk + if: resource.kind == "Pod" || resource.kind == "Deployment" +``` + +Read the full [documentation](../deep_links.md) to see all possible combinations of values accessible fo each category of links. + +## Support of `helm.sh/resource-policy` annotation + +Argo CD now supports the `helm.sh/resource-policy` annotation to control the deletion of resources. The behavior is the same as the behavior of +`argocd.argoproj.io/sync-options: Delete=false` annotation: if the annotation is present and set to `keep`, the resource will not be deleted +when the application is deleted. + +## Check your Kustomize patches for `--redis` changes + +Starting in Argo CD 2.7, the install manifests no longer pass the Redis server name via `--redis`. + +If your environment uses Kustomize JSON patches to modify the Redis server name, the patch might break when you upgrade +to the 2.7 manifests. If it does, you can remove the patch and instead set the Redis server name via the `redis.server` +field in the argocd-cmd-params-cm ConfigMap. That value will be passed to the necessary components via `valueFrom` +environment variables. + +## `argocd applicationset` CLI incompatibilities for ApplicationSets with list generators + +If you are running Argo CD v2.7.0-2.7.2 server-side, then CLI versions outside that range will incorrectly handle list +generators. That is because the gRPC interface for those versions used the `elements` field number for the new +`elementsYaml` field. + +If you are running the Argo CD CLI versions v2.7.0-2.7.2 with a server-side version of v2.7.3 or later, then the CLI +will send the contents of the `elements` field to the server, which will interpret it as the `elementsYaml` field. This +will cause the ApplicationSet to fail at runtime with an error similar to this: + +``` +error unmarshling decoded ElementsYaml error converting YAML to JSON: yaml: control characters are not allowed +``` + +Be sure to use CLI version v2.7.3 or later with server-side version v2.7.3 or later. diff --git a/docs/operator-manual/upgrading/2.7-2.8.md b/docs/operator-manual/upgrading/2.7-2.8.md new file mode 100644 index 0000000000000..c42a97a1f429c --- /dev/null +++ b/docs/operator-manual/upgrading/2.7-2.8.md @@ -0,0 +1,73 @@ +# v2.7 to 2.8 + +## Support dropped for argocd-cm plugins + +Config Management Plugins installed via the argocd-cm ConfigMap will not work starting with v2.8. + +See the [migration guide](https://argo-cd.readthedocs.io/en/stable/operator-manual/config-management-plugins/#migrating-from-argocd-cm-plugins) +to upgrade your plugin. + +## Tini as entrypoint + +With the 2.8 release `entrypoint.sh` will be removed from the containers, +because starting with 2.7, the implicit entrypoint is set to `tini` in the +`Dockerfile` explicitly, and the Kubernetes manifests has been updated to use +it. Simply updating the containers without updating the deployment manifests +will result in pod startup failures, as the old manifests are relying on +`entrypoint.sh` instead of `tini`. Please make sure the manifests are updated +properly before moving to 2.8. + +## Filtering applied to cluster `List` API endpoint + +Prior to `v2.8`, the `List` endpoint on the `ClusterService` did **not** filter +clusters when responding, despite accepting query parameters. This bug has +been addressed, and query parameters are now taken into account to filter the +resulting list of clusters. + +## Configure RBAC to account for new actions + +2.8 introduces three new actions: + +* Create a Job from a CronJob +* Create a Workflow from a CronWorkflow +* Create a Workflow from a WorkflowTemplate + +When you upgrade to 2.8, RBAC policies with `applications` in the *resource* +field and `*` or `action/*` in the action field, it will automatically grant the +ability to use these new actions. + +If you would like to avoid granting these new permissions, you can update your RBAC policies to be more specific. + +### Example + +Old: + +```csv +p, role:action-runner, applications, actions/, *, allow +``` + +New: + +```csv +p, role:action-runner, applications, action/argoproj.io/Rollout/abort, *, allow +p, role:action-runner, applications, action/argoproj.io/Rollout/promote-full, *, allow +p, role:action-runner, applications, action/argoproj.io/Rollout/retry, *, allow +p, role:action-runner, applications, action/argoproj.io/Rollout/resume, *, allow +p, role:action-runner, applications, action/argoproj.io/Rollout/restart, *, allow +p, role:action-runner, applications, action/argoproj.io/AnalysisRun/terminate, *, allow +p, role:action-runner, applications, action/apps/DaemonSet/restart, *, allow +p, role:action-runner, applications, action/apps/StatefulSet/restart, *, allow +p, role:action-runner, applications, action/apps/Deployment/pause, *, allow +p, role:action-runner, applications, action/apps/Deployment/resume, *, allow +p, role:action-runner, applications, action/apps/Deployment/restart, *, allow + +# If you don't want to grant the new permissions, don't include the following lines +p, role:action-runner, applications, action/argoproj.io/WorkflowTemplate/create-workflow, *, allow +p, role:action-runner, applications, action/argoproj.io/CronWorkflow/create-workflow, *, allow +p, role:action-runner, applications, action/batch/CronJob/create-job, *, allow +``` + +## Change default file open mode + +In version 2.7, the CMP plugin was changed to open Git/Helm files with all executable bits set (unless `preserveFileMode` was specified). +Version 2.8 removes the executable bits in cases where they are not necessary. diff --git a/docs/operator-manual/upgrading/2.8-2.9.md b/docs/operator-manual/upgrading/2.8-2.9.md new file mode 100644 index 0000000000000..ef99e09587814 --- /dev/null +++ b/docs/operator-manual/upgrading/2.8-2.9.md @@ -0,0 +1,5 @@ +# v2.8 to 2.9 + +## Upgraded Kustomize Version + +Note that bundled Kustomize version has been upgraded from 5.1.0 to 5.2.1. diff --git a/docs/operator-manual/upgrading/2.9-2.10.md b/docs/operator-manual/upgrading/2.9-2.10.md new file mode 100644 index 0000000000000..7fddc75ab7e86 --- /dev/null +++ b/docs/operator-manual/upgrading/2.9-2.10.md @@ -0,0 +1,16 @@ +# v2.9 to 2.10 + +## `managedNamespaceMetadata` no longer preserves client-side-applied labels or annotations + +Argo CD 2.10 upgraded kubectl from 1.24 to 1.26. This upgrade introduced a change where client-side-applied labels and +annotations are no longer preserved when using a server-side kubectl apply. This change affects the +`managedNamespaceMetadata` field of the `Application` CRD. Previously, labels and annotations applied via a client-side +apply would be preserved when `managedNamespaceMetadata` was enabled. Now, those existing labels and annotation will be +removed. + +To avoid unexpected behavior, follow the [client-side to server-side resource upgrade guide](https://kubernetes.io/docs/reference/using-api/server-side-apply/#upgrading-from-client-side-apply-to-server-side-apply) +before enabling `managedNamespaceMetadata` on an existing namespace. + +## Upgraded Helm Version + +Note that bundled Helm version has been upgraded from 3.13.2 to 3.14.3. diff --git a/docs/operator-manual/upgrading/overview.md b/docs/operator-manual/upgrading/overview.md index 0f95b89066c86..742c7b191b57a 100644 --- a/docs/operator-manual/upgrading/overview.md +++ b/docs/operator-manual/upgrading/overview.md @@ -37,6 +37,11 @@ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/ +* [v2.9 to v2.10](./2.9-2.10.md) +* [v2.8 to v2.9](./2.8-2.9.md) +* [v2.7 to v2.8](./2.7-2.8.md) +* [v2.6 to v2.7](./2.6-2.7.md) +* [v2.5 to v2.6](./2.5-2.6.md) * [v2.4 to v2.5](./2.4-2.5.md) * [v2.3 to v2.4](./2.3-2.4.md) * [v2.2 to v2.3](./2.2-2.3.md) diff --git a/docs/operator-manual/user-management/google.md b/docs/operator-manual/user-management/google.md index 7113e51018ca2..366a1e9863d76 100644 --- a/docs/operator-manual/user-management/google.md +++ b/docs/operator-manual/user-management/google.md @@ -142,17 +142,6 @@ data: ## OpenID Connect plus Google Groups using Dex ---- -!!! warning "Limited group information" - - When using this feature you'll only receive the list of groups the user is a direct member. - - So, lets say you have this hierarchy of groups and subgroups: - `all@example.com --> tech@example.com --> devs@example.com --> you@example.com` - The only group you would receive through Dex would be `devs@example.com` - ---- - We're going to use Dex's `google` connector to get additional Google Groups information from your users, allowing you to use group membership on your RBAC, i.e., giving `admin` role to the whole `sysadmins@yourcompany.com` group. This connector uses two different credentials: @@ -211,7 +200,7 @@ Go through the same steps as in [OpenID Connect using Dex](#openid-connect-using defaultMode: 420 secretName: argocd-google-groups-json -3. Edit `argocd-cm` and add the following `dex.config` to the data section, replacing `clientID` and `clientSecret` with the values you saved before, `adminEmail` with the address for the admin user you're going to impersonate, and editing `redirectURI` with your Argo CD domain: +3. Edit `argocd-cm` and add the following `dex.config` to the data section, replacing `clientID` and `clientSecret` with the values you saved before, `adminEmail` with the address for the admin user you're going to impersonate, and editing `redirectURI` with your Argo CD domain (note that the `type` is now `google` instead of `oidc`): dex.config: | connectors: @@ -229,6 +218,20 @@ Go through the same steps as in [OpenID Connect using Dex](#openid-connect-using 5. Login to Argo CD and go to the "User info" section, were you should see the groups you're member ![User info](../../assets/google-groups-membership.png) 6. Now you can use groups email addresses to give RBAC permissions +7. Dex (> v2.31.0) can also be configure to fetch transitive group membership as follows: + + dex.config: | + connectors: + - config: + redirectURI: https://argocd.example.com/api/dex/callback + clientID: XXXXXXXXXXXXX.apps.googleusercontent.com + clientSecret: XXXXXXXXXXXXX + serviceAccountFilePath: /tmp/oidc/googleAuth.json + adminEmail: admin-email@example.com + fetchTransitiveGroupMembership: True + type: google + id: google + name: Google ### References diff --git a/docs/operator-manual/user-management/identity-center.md b/docs/operator-manual/user-management/identity-center.md new file mode 100644 index 0000000000000..0fd78b1aaf62f --- /dev/null +++ b/docs/operator-manual/user-management/identity-center.md @@ -0,0 +1,79 @@ +# Identity Center (AWS SSO) + +!!! note "Are you using this? Please contribute!" + If you're using this IdP please consider [contributing](../../developer-guide/site.md) to this document. + +A working Single Sign-On configuration using Identity Center (AWS SSO) has been achieved using the following method: + +* [SAML (with Dex)](#saml-with-dex) + +## SAML (with Dex) + +1. Create a new SAML application in Identity Center and download the certificate. + * ![Identity Center SAML App 1](../../assets/identity-center-1.png) + * ![Identity Center SAML App 2](../../assets/identity-center-2.png) +2. Click `Assign Users` after creating the application in Identity Center, and select the users or user groups you wish to grant access to this application. + * ![Identity Center SAML App 3](../../assets/identity-center-3.png) +3. Copy the Argo CD URL into the `data.url` field in the `argocd-cm` ConfigMap. + + data: + url: https://argocd.example.com + +4. Configure Attribute mappings. + + !!! note "Group attribute mapping is not officially!" + Group attribute mapping is not officially supported in the AWS docs, however the workaround is currently working. + + * ![Identity Center SAML App 4](../../assets/identity-center-4.png) + * ![Identity Center SAML App 5](../../assets/identity-center-5.png) + + + +5. Download the CA certificate to use in the `argocd-cm` configuration. + * If using the `caData` field, you'll need to base64-encode the entire certificate, including the `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` stanzas (e.g., `base64 my_cert.pem`). + * If using the `ca` field and storing the CA certificate separately as a secret, you will need to mount the secret onto the `dex` container in the `argocd-dex-server` Deployment. + * ![Identity Center SAML App 6](../../assets/identity-center-6.png) +6. Edit the `argocd-cm` and configure the `data.dex.config` section: + + +```yaml +dex.config: | + logger: + level: debug + format: json + connectors: + - type: saml + id: aws + name: "AWS IAM Identity Center" + config: + # You need value of Identity Center APP SAML (IAM Identity Center sign-in URL) + ssoURL: https://portal.sso.yourregion.amazonaws.com/saml/assertion/id + # You need `caData` _OR_ `ca`, but not both. + caData: + # Path to mount the secret to the dex container + entityIssuer: https://external.path.to.argocd.io/api/dex/callback + redirectURI: https://external.path.to.argocd.io/api/dex/callback + usernameAttr: email + emailAttr: email + groupsAttr: groups +``` + + +### Connect Identity Center Groups to Argo CD Roles +Argo CD recognizes user memberships in Identity Center groups that match the **Group Attribute Statements** regex. + + In the example above, the regex `argocd-*` is used, making Argo CD aware of a group named `argocd-admins`. + +Modify the `argocd-rbac-cm` ConfigMap to connect the `ArgoCD-administrators` Identity Center group to the builtin Argo CD `admin` role. + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-rbac-cm +data: + policy.csv: | + g, , role:admin + scopes: '[groups, email]' +``` + diff --git a/docs/operator-manual/user-management/index.md b/docs/operator-manual/user-management/index.md index c0a70eae47429..c002b77ada5ed 100644 --- a/docs/operator-manual/user-management/index.md +++ b/docs/operator-manual/user-management/index.md @@ -3,7 +3,7 @@ Once installed Argo CD has one built-in `admin` user that has full access to the system. It is recommended to use `admin` user only for initial configuration and then switch to local users or configure SSO integration. -## Local users/accounts (v1.5) +## Local users/accounts The local users/accounts feature serves two main use-cases: @@ -44,6 +44,24 @@ Each user might have two capabilities: * apiKey - allows generating authentication tokens for API access * login - allows to login using UI +### Delete user + +In order to delete a user, you must remove the corresponding entry defined in the `argocd-cm` ConfigMap: + +Example: + +```bash +kubectl patch -n argocd cm argocd-cm --type='json' -p='[{"op": "remove", "path": "/data/accounts.alice"}]' +``` + +It is recommended to also remove the password entry in the `argocd-secret` Secret: + +Example: + +```bash +kubectl patch -n argocd secrets argocd-secret --type='json' -p='[{"op": "remove", "path": "/data/accounts.alice.password"}]' +``` + ### Disable admin user As soon as additional users are created it is recommended to disable `admin` user: @@ -183,7 +201,7 @@ data: id: acme-github name: Acme GitHub config: - hostName: github.acme.com + hostName: github.acme.example.com clientID: abcdefghijklmnopqrst clientSecret: $dex.acme.clientSecret # Alternatively $:dex.acme.clientSecret orgs: @@ -224,7 +242,7 @@ data: id: oidc name: OIDC config: - issuer: https://example-OIDC-provider.com + issuer: https://example-OIDC-provider.example.com clientID: aaaabbbbccccddddeee clientSecret: $dex.oidc.clientSecret ``` @@ -246,7 +264,7 @@ data: id: oidc name: OIDC config: - issuer: https://example-OIDC-provider.com + issuer: https://example-OIDC-provider.example.com clientID: aaaabbbbccccddddeee clientSecret: $dex.oidc.clientSecret insecureEnableGroups: true @@ -276,7 +294,7 @@ data: id: oidc name: OIDC config: - issuer: https://example-OIDC-provider.com + issuer: https://example-OIDC-provider.example.com clientID: aaaabbbbccccddddeee clientSecret: $dex.oidc.clientSecret insecureEnableGroups: true @@ -301,6 +319,19 @@ data: issuer: https://dev-123456.oktapreview.com clientID: aaaabbbbccccddddeee clientSecret: $oidc.okta.clientSecret + + # Optional list of allowed aud claims. If omitted or empty, defaults to the clientID value above (and the + # cliClientID, if that is also specified). If you specify a list and want the clientID to be allowed, you must + # explicitly include it in the list. + # Token verification will pass if any of the token's audiences matches any of the audiences in this list. + allowedAudiences: + - aaaabbbbccccddddeee + - qqqqwwwweeeerrrrttt + + # Optional. If false, tokens without an audience will always fail validation. If true, tokens without an audience + # will always pass validation. + # Defaults to true for Argo CD < 2.6.0. Defaults to false for Argo CD >= 2.6.0. + skipAudienceCheckWhenTokenHasNoAudience: true # Optional set of OIDC scopes to request. If omitted, defaults to: ["openid", "profile", "email", "groups"] requestedScopes: ["openid", "profile", "email", "groups"] @@ -313,6 +344,12 @@ data: # for the 'localhost' (CLI) client to Dex. This field is optional. If omitted, the CLI will # use the same clientID as the Argo CD server cliClientID: vvvvwwwwxxxxyyyyzzzz + + # PKCE authentication flow processes authorization flow from browser only - default false + # uses the clientID + # make sure the Identity Provider (IdP) is public and doesn't need clientSecret + # make sure the Identity Provider (IdP) has this redirect URI registered: https://argocd.example.com/pkce/verify + enablePKCEAuthentication: true ``` !!! note @@ -350,6 +387,20 @@ For a simple case this can be: oidc.config: | requestedIDTokenClaims: {"groups": {"essential": true}} ``` + +### Retrieving group claims when not in the token + +Some OIDC providers don't return the group information for a user in the ID token, even if explicitly requested using the `requestedIDTokenClaims` setting (Okta for example). They instead provide the groups on the user info endpoint. With the following config, Argo CD queries the user info endpoint during login for groups information of a user: + +```yaml +oidc.config: | + enableUserInfoGroups: true + userInfoPath: /userinfo + userInfoCacheExpiration: "5m" +``` + +**Note: If you omit the `userInfoCacheExpiration` setting or if it's greater than the expiration of the ID token, the argocd-server will cache group information as long as the ID token is valid!** + ### Configuring a custom logout URL for your OIDC provider Optionally, if your OIDC provider exposes a logout API and you wish to configure a custom logout URL for the purposes of invalidating @@ -358,18 +409,18 @@ any active session post logout, you can do so by specifying it as follows: ```yaml oidc.config: | name: example-OIDC-provider - issuer: https://example-OIDC-provider.com + issuer: https://example-OIDC-provider.example.com clientID: xxxxxxxxx clientSecret: xxxxxxxxx requestedScopes: ["openid", "profile", "email", "groups"] requestedIDTokenClaims: {"groups": {"essential": true}} - logoutURL: https://example-OIDC-provider.com/logout?id_token_hint={{token}} + logoutURL: https://example-OIDC-provider.example.com/logout?id_token_hint={{token}} ``` By default, this would take the user to their OIDC provider's login page after logout. If you also wish to redirect the user back to Argo CD after logout, you can specify the logout URL as follows: ```yaml ... - logoutURL: https://example-OIDC-provider.com/logout?id_token_hint={{token}}&post_logout_redirect_uri={{logoutRedirectURL}} + logoutURL: https://example-OIDC-provider.example.com/logout?id_token_hint={{token}}&post_logout_redirect_uri={{logoutRedirectURL}} ``` You are not required to specify a logoutRedirectURL as this is automatically generated by ArgoCD as your base ArgoCD url + Rootpath @@ -405,7 +456,7 @@ Add a `rootCA` to your `oidc.config` which contains the PEM encoded root certifi #### Example -SSO `clientSecret` can thus be stored as a kubernetes secret with the following manifests +SSO `clientSecret` can thus be stored as a Kubernetes secret with the following manifests `argocd-secret`: ```yaml @@ -449,7 +500,7 @@ data: #### Alternative -If you want to store sensitive data in **another** Kubernetes `Secret`, instead of `argocd-secret`. ArgoCD knows to check the keys under `data` in your Kubernetes `Secret` for a corresponding key whenever a value in a configmap starts with `$`, then your Kubernetes `Secret` name and `:` (colon). +If you want to store sensitive data in **another** Kubernetes `Secret`, instead of `argocd-secret`. ArgoCD knows to check the keys under `data` in your Kubernetes `Secret` for a corresponding key whenever a value in a configmap or secret starts with `$`, then your Kubernetes `Secret` name and `:` (colon). Syntax: `$:` diff --git a/docs/operator-manual/user-management/keycloak.md b/docs/operator-manual/user-management/keycloak.md index bc88c436e8ef0..6f0c99de0dec2 100644 --- a/docs/operator-manual/user-management/keycloak.md +++ b/docs/operator-manual/user-management/keycloak.md @@ -9,19 +9,24 @@ to determine privileges in Argo. ## Creating a new client in Keycloak First we need to setup a new client. Start by logging into your keycloak server, select the realm you want to use (`master` by default) -and then go to __Clients__ and click the __create__ button top right. +and then go to __Clients__ and click the __Create client__ button at the top. ![Keycloak add client](../../assets/keycloak-add-client.png "Keycloak add client") -Configure the client by setting the __Access Type__ to _confidential_ and set the Valid Redirect URIs to the callback url for your ArgoCD -hostname. It should be https://{hostname}/auth/callback (you can also leave the default less secure https://{hostname}/* ). You can also set the -__Base URL__ to _/applications_. +Enable the __Client authentication__. -If you want to allow command line access, __Access Type__ must be set to _public_ and you also need to add http://localhost:8085/auth/callback in the list of Valid Redirect URIs. Then users can login using `argocd login {hostname} --sso`. +![Keycloak add client Step 2](../../assets/keycloak-add-client_2.png "Keycloak add client Step 2") + +Configure the client by setting the __Root URL__, __Web origins__, __Admin URL__ to the hostname (https://{hostname}). + +Also you can set __Home URL__ to your _/applications_ path and __Valid Post logout redirect URIs__ to "+". + +The Valid Redirect URIs should be set to https://{hostname}/auth/callback (you can also set the less secure https://{hostname}/* for testing/development purposes, +but it's not recommended in production). ![Keycloak configure client](../../assets/keycloak-configure-client.png "Keycloak configure client") -Make sure to click __Save__. You should now have a new tab called __Credentials__. You can copy the Secret that we'll use in our ArgoCD +Make sure to click __Save__. There should be a tab called __Credentials__. You can copy the Secret that we'll use in our ArgoCD configuration. ![Keycloak client secret](../../assets/keycloak-client-secret.png "Keycloak client secret") @@ -34,21 +39,18 @@ To do this we'll start by creating a new __Client Scope__ called _groups_. ![Keycloak add scope](../../assets/keycloak-add-scope.png "Keycloak add scope") Once you've created the client scope you can now add a Token Mapper which will add the groups claim to the token when the client requests -the groups scope. Make sure to set the __Name__ as well as the __Token Claim Name__ to _groups_. +the groups scope. In the Tab "Mappers", click on "Configure a new mapper" and choose __Group Membership__. +Make sure to set the __Name__ as well as the __Token Claim Name__ to _groups_. Also disable the "Full group path". ![Keycloak groups mapper](../../assets/keycloak-groups-mapper.png "Keycloak groups mapper") -We can now configure the client to provide the _groups_ scope. You can now assign the _groups_ scope either to the __Assigned Default Client Scopes__ -or to the __Assigned Optional Client Scopes__. If you put it in the Optional category you will need to make sure that ArgoCD requests the scope in -it's OIDC configuration. +We can now configure the client to provide the _groups_ scope. Go back to the client we've created earlier and go to the Tab "Client Scopes". +Click on "Add client scope", choose the _groups_ scope and add it either to the __Default__ or to the __Optional__ Client Scope. If you put it in the Optional +category you will need to make sure that ArgoCD requests the scope in its OIDC configuration. Since we will always want group information, I recommend +using the Default category. ![Keycloak client scope](../../assets/keycloak-client-scope.png "Keycloak client scope") -Since we will always want group information, I recommend using the Default category. Make sure you click __Add selected__ -and that the _groups_ claim is in the correct list on the __right__. - -![Keycloak client scope selected](../../assets/keycloak-client-scope-selected.png "Keycloak client scope selected") - Create a group called _ArgoCDAdmins_ and have your current user join the group. ![Keycloak user group](../../assets/keycloak-user-group.png "Keycloak user group") @@ -94,8 +96,9 @@ data: ``` Make sure that: + - __issuer__ ends with the correct realm (in this example _master_) -- __issuer__ on Keycloak releases older than version 17 the URL must include /auth (in this expample /auth/realms/master) +- __issuer__ on Keycloak releases older than version 17 the URL must include /auth (in this example /auth/realms/master) - __clientID__ is set to the Client ID you configured in Keycloak - __clientSecret__ points to the right key you created in the _argocd-secret_ Secret - __requestedScopes__ contains the _groups_ claim if you didn't add it to the Default scopes diff --git a/docs/operator-manual/user-management/microsoft.md b/docs/operator-manual/user-management/microsoft.md index 33a6b3e945940..486d647fde3d0 100644 --- a/docs/operator-manual/user-management/microsoft.md +++ b/docs/operator-manual/user-management/microsoft.md @@ -1,13 +1,16 @@ # Microsoft -* [Azure AD SAML Enterprise App Auth using Dex](#azure-ad-saml-enterprise-app-auth-using-dex) -* [Azure AD App Registration Auth using OIDC](#azure-ad-app-registration-auth-using-oidc) -* [Azure AD App Registration Auth using Dex](#azure-ad-app-registration-auth-using-dex) +!!! note "" + Entra ID was formerly known as Azure AD. -## Azure AD SAML Enterprise App Auth using Dex -### Configure a new Azure AD Enterprise App +* [Entra ID SAML Enterprise App Auth using Dex](#entra-id-saml-enterprise-app-auth-using-dex) +* [Entra ID App Registration Auth using OIDC](#entra-id-app-registration-auth-using-oidc) +* [Entra ID App Registration Auth using Dex](#entra-id-app-registration-auth-using-dex) -1. From the `Azure Active Directory` > `Enterprise applications` menu, choose `+ New application` +## Entra ID SAML Enterprise App Auth using Dex +### Configure a new Entra ID Enterprise App + +1. From the `Microsoft Entra ID` > `Enterprise applications` menu, choose `+ New application` 2. Select `Non-gallery application` 3. Enter a `Name` for the application (e.g. `Argo CD`), then choose `Add` 4. Once the application is created, open it from the `Enterprise applications` menu. @@ -31,9 +34,9 @@ - *Keep a copy of the encoded output to be used in the next section.* 9. From the `Single sign-on` menu, copy the `Login URL` parameter, to be used in the next section. -### Configure Argo to use the new Azure AD Enterprise App +### Configure Argo to use the new Entra ID Enterprise App -1. Edit `argocd-cm` and add the following `dex.config` to the data section, replacing the `caData`, `my-argo-cd-url` and `my-login-url` your values from the Azure AD App: +1. Edit `argocd-cm` and add the following `dex.config` to the data section, replacing the `caData`, `my-argo-cd-url` and `my-login-url` your values from the Entra ID App: data: url: https://my-argo-cd-url @@ -56,7 +59,7 @@ groupsAttr: Group 2. Edit `argocd-rbac-cm` to configure permissions, similar to example below. - - Use Azure AD `Group IDs` for assigning roles. + - Use Entra ID `Group IDs` for assigning roles. - See [RBAC Configurations](../rbac.md) for more detailed scenarios. # example policy @@ -70,11 +73,11 @@ p, role:org-admin, repositories, delete, *, allow g, "84ce98d1-e359-4f3b-85af-985b458de3c6", role:org-admin # (azure group assigned to role) -## Azure AD App Registration Auth using OIDC -### Configure a new Azure AD App registration -#### Add a new Azure AD App registration +## Entra ID App Registration Auth using OIDC +### Configure a new Entra ID App registration +#### Add a new Entra ID App registration -1. From the `Azure Active Directory` > `App registrations` menu, choose `+ New registration` +1. From the `Microsoft Entra ID` > `App registrations` menu, choose `+ New registration` 2. Enter a `Name` for the application (e.g. `Argo CD`). 3. Specify who can use the application (e.g. `Accounts in this organizational directory only`). 4. Enter Redirect URI (optional) as follows (replacing `my-argo-cd-url` with your Argo URL), then choose `Add`. @@ -92,29 +95,29 @@ - **Redirect URI:** `http://localhost:8085/auth/callback` ![Azure App registration's Authentication](../../assets/azure-app-registration-authentication.png "Azure App registration's Authentication") -#### Add credentials a new Azure AD App registration +#### Add credentials a new Entra ID App registration 1. From the `Certificates & secrets` menu, choose `+ New client secret` 2. Enter a `Name` for the secret (e.g. `ArgoCD-SSO`). - Make sure to copy and save generated value. This is a value for the `client_secret`. ![Azure App registration's Secret](../../assets/azure-app-registration-secret.png "Azure App registration's Secret") -#### Setup permissions for Azure AD Application +#### Setup permissions for Entra ID Application 1. From the `API permissions` menu, choose `+ Add a permission` 2. Find `User.Read` permission (under `Microsoft Graph`) and grant it to the created application: - ![Azure AD API permissions](../../assets/azure-api-permissions.png "Azure AD API permissions") + ![Entra ID API permissions](../../assets/azure-api-permissions.png "Entra ID API permissions") 3. From the `Token Configuration` menu, choose `+ Add groups claim` - ![Azure AD token configuration](../../assets/azure-token-configuration.png "Azure AD token configuration") + ![Entra ID token configuration](../../assets/azure-token-configuration.png "Entra ID token configuration") -### Associate an Azure AD group to your Azure AD App registration +### Associate an Entra ID group to your Entra ID App registration -1. From the `Azure Active Directory` > `Enterprise applications` menu, search the App that you created (e.g. `Argo CD`). - - An Enterprise application with the same name of the Azure AD App registration is created when you add a new Azure AD App registration. +1. From the `Microsoft Entra ID` > `Enterprise applications` menu, search the App that you created (e.g. `Argo CD`). + - An Enterprise application with the same name of the Entra ID App registration is created when you add a new Entra ID App registration. 2. From the `Users and groups` menu of the app, add any users or groups requiring access to the service. ![Azure Enterprise SAML Users](../../assets/azure-enterprise-users.png "Azure Enterprise SAML Users") -### Configure Argo to use the new Azure AD App registration +### Configure Argo to use the new Entra ID App registration 1. Edit `argocd-cm` and configure the `data.oidc.config` and `data.url` section: @@ -173,7 +176,7 @@ Refer to [operator-manual/argocd-rbac-cm.yaml](https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/argocd-rbac-cm.yaml) for all of the available variables. -## Azure AD App Registration Auth using Dex +## Entra ID App Registration Auth using Dex Configure a new AD App Registration, as above. Then, add the `dex.config` to `argocd-cm`: @@ -200,9 +203,9 @@ data: 1. Open a new browser tab and enter your ArgoCD URI: https://`` ![Azure SSO Web Log In](../../assets/azure-sso-web-log-in-via-azure.png "Azure SSO Web Log In") -3. Click `LOGIN VIA AZURE` button to log in with your Azure Active Directory account. You’ll see the ArgoCD applications screen. +3. Click `LOGIN VIA AZURE` button to log in with your Microsoft Entra ID account. You’ll see the ArgoCD applications screen. ![Azure SSO Web Application](../../assets/azure-sso-web-application.png "Azure SSO Web Application") -4. Navigate to User Info and verify Group ID. Groups will have your group’s Object ID that you added in the `Setup permissions for Azure AD Application` step. +4. Navigate to User Info and verify Group ID. Groups will have your group’s Object ID that you added in the `Setup permissions for Entra ID Application` step. ![Azure SSO Web User Info](../../assets/azure-sso-web-user-info.png "Azure SSO Web User Info") ### Log in to ArgoCD using CLI diff --git a/docs/operator-manual/user-management/okta.md b/docs/operator-manual/user-management/okta.md index 09d7099d19954..308254759de6e 100644 --- a/docs/operator-manual/user-management/okta.md +++ b/docs/operator-manual/user-management/okta.md @@ -118,34 +118,81 @@ data: ## OIDC (without Dex) -!!! warning "Do you want groups for RBAC later?" - If you want `groups` scope returned from Okta you need to unfortunately contact support to enable [API Access Management with Okta](https://developer.okta.com/docs/concepts/api-access-management/) or [_just use SAML above!_](#saml-with-dex) +!!! warning "Okta groups for RBAC" + If you want `groups` scope returned from Okta, you will need to enable [API Access Management with Okta](https://developer.okta.com/docs/concepts/api-access-management/). This addon is free, and automatically enabled, on Okta developer edition. However, it's an optional add-on for production environments, with an additional associated cost. - Next you may need the API Access Management feature, which the support team can enable for your OktaPreview domain for testing, to enable "custom scopes" and a separate endpoint to use instead of the "public" `/oauth2/v1/authorize` API Access Management endpoint. This might be a paid feature if you want OIDC unfortunately. The free alternative I found was SAML. + You may alternately add a "groups" scope and claim to the default authorization server, and then filter the claim in the Okta application configuration. It's not clear if this requires the Authorization Server add-on. + + If this is not an option for you, use the [SAML (with Dex)](#saml-with-dex) option above instead. + +!!! note + These instructions and screenshots are of Okta version 2023.05.2 E. You can find the current version in the Okta website footer. + +First, create the OIDC integration: + +1. On the `Okta Admin` page, navigate to the Okta Applications at `Applications > Applications.` +1. Choose `Create App Integration`, and choose `OIDC`, and then `Web Application` in the resulting dialogues. + ![Okta OIDC app dialogue](../../assets/okta-create-oidc-app.png) +1. Update the following: + 1. `App Integration name` and `Logo` - set these to suit your needs; they'll be displayed in the Okta catalogue. + 1. `Sign-in redirect URLs`: Add `https://argocd.example.com/auth/callback`; replacing `argocd.example.com` with your ArgoCD web interface URL. Also add `http://localhost:8085/auth/callback` if you would like to be able to login with the CLI. + 1. `Sign-out redirect URIs`: Add `https://argocd.example.com`; substituting the correct domain name as above. + 1. Either assign groups, or choose to skip this step for now. + 1. Leave the rest of the options as-is, and save the integration. + ![Okta app settings](../../assets/okta-app.png) +1. Copy the `Client ID` and the `Client Secret` from the newly created app; you will need these later. + +Next, create a custom Authorization server: 1. On the `Okta Admin` page, navigate to the Okta API Management at `Security > API`. - ![Okta API Management](../../assets/api-management.png) -1. Choose your `default` authorization server. -1. Click `Scopes > Add Scope` - 1. Add a scope called `groups`. - ![Groups Scope](../../assets/groups-scope.png) -1. Click `Claims > Add Claim.` - 1. Add a claim called `groups` - 1. Choose the matching options you need, one example is: - * e.g. to match groups starting with `argocd-` you'd return an `ID Token` using your scope name from step 3 (e.g. `groups`) where the groups name `matches` the `regex` `argocd-.*` - ![Groups Claim](../../assets/groups-claim.png) -1. Edit the `argocd-cm` and configure the `data.oidc.config` section: +1. Click `Add Authorization Server`, and assign it a name and a description. The `Audience` should match your ArgoCD URL - `https://argocd.example.com` +1. Click `Scopes > Add Scope`: + 1. Add a scope called `groups`. Leave the rest of the options as default. + ![Groups Scope](../../assets/okta-groups-scope.png) +1. Click `Claims > Add Claim`: + 1. Add a claim called `groups`. + 1. Adjust the `Include in token type` to `ID Token`, `Always`. + 1. Adjust the `Value type` to `Groups`. + 1. Add a filter that will match the Okta groups you want passed on to ArgoCD; for example `Regex: argocd-.*`. + 1. Set `Include in` to `groups` (the scope you created above). + ![Groups Claim](../../assets/okta-groups-claim.png) +1. Click on `Access Policies` > `Add Policy.` This policy will restrict how this authorization server is used. + 1. Add a name and description. + 1. Assign the policy to the client (application integration) you created above. The field should auto-complete as you type. + 1. Create the policy. + ![Auth Policy](../../assets/okta-auth-policy.png) +1. Add a rule to the policy: + 1. Add a name; `default` is a reasonable name for this rule. + 1. Fine-tune the settings to suit your organization's security posture. Some ideas: + 1. uncheck all the grant types except the Authorization Code. + 1. Adjust the token lifetime to govern how long a session can last. + 1. Restrict refresh token lifetime, or completely disable it. + ![Default rule](../../assets/okta-auth-rule.png) +1. Finally, click `Back to Authorization Servers`, and copy the `Issuer URI`. You will need this later. + +If you haven't yet created Okta groups, and assigned them to the application integration, you should do that now: + +1. Go to `Directory > Groups` +1. For each group you wish to add: + 1. Click `Add Group`, and choose a meaningful name. It should match the regex or pattern you added to your custom `group` claim. + 1. Click on the group (refresh the page if the new group didn't show up in the list). + 1. Assign Okta users to the group. + 1. Click on `Applications` and assign the OIDC application integration you created to this group. + 1. Repeat as needed. + +Finally, configure ArgoCD itself. Edit the `argocd-cm` configmap: ```yaml +url: https://argocd.example.com oidc.config: | name: Okta - issuer: https://yourorganization.oktapreview.com - clientID: 0oaltaqg3oAIf2NOa0h3 - clientSecret: ZXF_CfUc-rtwNfzFecGquzdeJ_MxM4sGc8pDT2Tg6t + # this is the authorization server URI + issuer: https://example.okta.com/oauth2/aus9abcdefgABCDEFGd7 + clientID: 0oa9abcdefgh123AB5d7 + clientSecret: ABCDEFG1234567890abcdefg requestedScopes: ["openid", "profile", "email", "groups"] requestedIDTokenClaims: {"groups": {"essential": true}} ``` - - +You may want to store the `clientSecret` in a Kubernetes secret; see [how to deal with SSO secrets](./index.md/#sensitive-data-and-sso-client-secrets ) for more details. diff --git a/docs/operator-manual/user-management/openunison.md b/docs/operator-manual/user-management/openunison.md index 469d85f14935b..fecaafd074aa1 100644 --- a/docs/operator-manual/user-management/openunison.md +++ b/docs/operator-manual/user-management/openunison.md @@ -19,7 +19,7 @@ metadata: spec: accessTokenSkewMillis: 120000 accessTokenTimeToLive: 1200000 - authChainName: LoginService + authChainName: login-service clientId: argocd codeLastMileKeyName: lastmile-oidc codeTokenSkewMilis: 60000 diff --git a/docs/operator-manual/user-management/zitadel.md b/docs/operator-manual/user-management/zitadel.md new file mode 100644 index 0000000000000..08841983bc95f --- /dev/null +++ b/docs/operator-manual/user-management/zitadel.md @@ -0,0 +1,210 @@ +# Zitadel +Please also consult the [Zitadel Documentation](https://zitadel.com/docs). +## Integrating Zitadel and ArgoCD +These instructions will take you through the entire process of getting your ArgoCD application authenticating and authorizing with Zitadel. You will create an application within Zitadel and configure ArgoCD to use Zitadel for authentication using roles set in Zitadel to determine privileges in ArgoCD. + +The following steps are required to integrate ArgoCD with Zitadel: +1. Create a new project and a new application in Zitadel +2. Configure the application in Zitadel +3. Set up roles in Zitadel +4. Set up an action in Zitadel +5. Configure ArgoCD configmaps +6. Test the setup + +The following values will be used in this example: +- Zitadel FQDN: `auth.example.com` +- Zitadel Project: `argocd-project` +- Zitadel Application: `argocd-application` +- Zitadel Action: `groupsClaim` +- ArgoCD FQDN: `argocd.example.com` +- ArgoCD Administrator Role: `argocd_administrators` +- ArgoCD User Role: `argocd_users` + +You may choose different values in your setup; these are used to keep the guide consistent. + +## Setting up your project and application in Zitadel +First, we will create a new project within Zitadel. Go to **Projects** and select **Create New Project**. +You should now see the following screen. + +![Zitadel Project](../../assets/zitadel-project.png "Zitadel Project") + +Check the following options: +- Assert Roles on Authentication +- Check authorization on Authentication + +![Zitadel Project Settings](../../assets/zitadel-project-settings.png "Zitadel Project Settings") + +### Roles + +Go to **Roles** and click **New**. Create the following two roles. Use the specified values below for both fields **Key** and **Group**. +- `argocd_administrators` +- `argocd_users` + +Your roles should now look like this: + +![Zitadel Project Roles](../../assets/zitadel-project-roles.png "Zitadel Project Roles") + +### Authorizations + +Next, go to **Authorizations** and assign your user the role `argocd_administrators`. +Click **New**, enter the name of your user and click **Continue**. Select the role `argocd_administrators` and click **Save**. + +Your authorizations should now look like this: + +![Zitadel Project Authorizations](../../assets/zitadel-project-authorizations.png "Zitadel Project Authorizations") + +### Creating an application + +Go to **General** and create a new application. Name the application `argocd-application`. + +For type of the application, select **WEB** and click continue. + +![Zitadel Application Setup Step 1](../../assets/zitadel-application-1.png "Zitadel Application Setup Step 1") + +Select **CODE** and continue. + +![Zitadel Application Setup Step 2](../../assets/zitadel-application-2.png "Zitadel Application Setup Step 2") + +Next, we will set up the redirect and post-logout URIs. Set the following values: +- Redirect URI: `https://argocd.example.com/auth/callback` +- Post Logout URI: `https://argocd.example.com` + +The post logout URI is optional. In the example setup users will be taken back to the ArgoCD login page after logging out. + +![Zitadel Application Setup Step 3](../../assets/zitadel-application-3.png "Zitadel Application Setup Step 3") + +Verify your configuration on the next screen and click **Create** to create the application. + +![Zitadel Application Setup Step 4](../../assets/zitadel-application-4.png "Zitadel Application Setup Step 4") + +After clicking **Create** you will be shown the `ClientId` and the `ClientSecret` for your application. Make sure to copy the ClientSecret as you will not be able to retrieve it after closing this window. +For our example, the following values are used: +- ClientId: `227060711795262483@argocd-project` +- ClientSecret: `UGvTjXVFAQ8EkMv2x4GbPcrEwrJGWZ0sR2KbwHRNfYxeLsDurCiVEpa5bkgW0pl0` + +![Zitadel Application Secrets](../../assets/zitadel-application-secrets.png "Zitadel Application Secrets") + +Once you have saved the ClientSecret in a safe place, click **Close** to complete creating the application. + +Go to **Token Settings** and enable the following options: +- User roles inside ID Token +- User Info inside ID Token + +![Zitadel Application Settings](../../assets/zitadel-application-settings.png "Zitadel Application Settings") + +## Setting up an action in Zitadel + +To include the role of the user in the token issued by Zitadel, we will need to set up a Zitadel Action. The authorization in ArgoCD will be determined by the role contained within the auth token. +Go to **Actions**, click **New** and choose `groupsClaim` as the name of your action. + +Paste the following code into the action: + +```javascript +/** + * sets the roles an additional claim in the token with roles as value an project as key + * + * The role claims of the token look like the following: + * + * // added by the code below + * "groups": ["{roleName}", "{roleName}", ...], + * + * Flow: Complement token, Triggers: Pre Userinfo creation, Pre access token creation + * + * @param ctx + * @param api + */ +function groupsClaim(ctx, api) { + if (ctx.v1.user.grants === undefined || ctx.v1.user.grants.count == 0) { + return; + } + + let grants = []; + ctx.v1.user.grants.grants.forEach((claim) => { + claim.roles.forEach((role) => { + grants.push(role); + }); + }); + + api.v1.claims.setClaim("groups", grants); +} +``` + +Check **Allowed To Fail** and click **Add** to add your action. + +*Note: If **Allowed To Fail** is not checked and a user does not have a role assigned, it may be possible that the user is no longer able to log in to Zitadel as the login flow fails when the action fails.* + +Next, add your action to the **Complement Token** flow. Select the **Complement Token** flow from the dropdown and click **Add trigger**. +Add your action to both triggers **Pre Userinfo creation** and **Pre access token creation**. + +Your Actions page should now look like the following screenshot: + +![Zitadel Actions](../../assets/zitadel-actions.png "Zitadel Actions") + + +## Configuring the ArgoCD configmaps + +Next, we will configure two ArgoCD configmaps: +- [argocd-cm.yaml](https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/argocd-cm.yaml) +- [argocd-rbac-cm.yaml](https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/argocd-rbac-cm.yaml) + +Configure your configmaps as follows while making sure to replace the relevant values such as `url`, `issuer`, `clientID`, `clientSecret` and `logoutURL` with ones matching your setup. + +### argocd-cm.yaml +```yaml +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cm + namespace: argocd + labels: + app.kubernetes.io/part-of: argocd +data: + admin.enabled: "false" + url: https://argocd.example.com + oidc.config: | + name: Zitadel + issuer: https://auth.example.com + clientID: 227060711795262483@argocd-project + clientSecret: UGvTjXVFAQ8EkMv2x4GbPcrEwrJGWZ0sR2KbwHRNfYxeLsDurCiVEpa5bkgW0pl0 + requestedScopes: + - openid + - profile + - email + - groups + logoutURL: https://auth.example.com/oidc/v1/end_session +``` + +### argocd-rbac-cm.yaml +```yaml +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-rbac-cm + namespace: argocd + labels: + app.kubernetes.io/part-of: argocd +data: + scopes: '[groups]' + policy.csv: | + g, argocd_administrators, role:admin + g, argocd_users, role:readonly + policy.default: '' +``` + +The roles specified under `policy.csv` must match the roles configured in Zitadel. +The Zitadel role `argocd_administrators` will be assigned the ArgoCD role `admin` granting admin access to ArgoCD. +The Zitadel role `argocd_users` will be assigned the ArgoCD role `readonly` granting read-only access to ArgoCD. + +Deploy your ArgoCD configmaps. ArgoCD and Zitadel should now be set up correctly to allow users to log in to ArgoCD using Zitadel. + +## Testing the setup + +Go to your ArgoCD instance. You should now see the **LOG IN WITH ZITADEL** button above the usual username/password login. + +![Zitadel ArgoCD Login](../../assets/zitadel-argocd-login.png "Zitadel ArgoCD Login") + +After logging in with your Zitadel user go to **User Info**. If everything is set up correctly you should now see the group `argocd_administrators` as shown below. + +![Zitadel ArgoCD User Info](../../assets/zitadel-argocd-user-info.png "Zitadel ArgoCD User Info") diff --git a/docs/operator-manual/webhook.md b/docs/operator-manual/webhook.md index 9a93d6ff0208c..a0e6c8deba1b2 100644 --- a/docs/operator-manual/webhook.md +++ b/docs/operator-manual/webhook.md @@ -4,7 +4,7 @@ Argo CD polls Git repositories every three minutes to detect changes to the manifests. To eliminate this delay from polling, the API server can be configured to receive webhook events. Argo CD supports -Git webhook notifications from GitHub, GitLab, Bitbucket, Bitbucket Server and Gogs. The following explains how to configure +Git webhook notifications from GitHub, GitLab, Bitbucket, Bitbucket Server, Azure DevOps and Gogs. The following explains how to configure a Git webhook for GitHub, but the same process should be applicable to other providers. !!! note @@ -12,19 +12,28 @@ a Git webhook for GitHub, but the same process should be applicable to other pro the same. A hook event for a push to branch `x` will trigger a refresh for an app pointing at the same repo with `targetRevision: refs/tags/x`. -### 1. Create The WebHook In The Git Provider +## 1. Create The WebHook In The Git Provider In your Git provider, navigate to the settings page where webhooks can be configured. The payload URL configured in the Git provider should use the `/api/webhook` endpoint of your Argo CD instance (e.g. `https://argocd.example.com/api/webhook`). If you wish to use a shared secret, input an arbitrary value in the secret. This value will be used when configuring the webhook in the next step. +## Github + ![Add Webhook](../assets/webhook-config.png "Add Webhook") !!! note When creating the webhook in GitHub, the "Content type" needs to be set to "application/json". The default value "application/x-www-form-urlencoded" is not supported by the library used to handle the hooks -### 2. Configure Argo CD With The WebHook Secret (Optional) +## Azure DevOps + +![Add Webhook](../assets/azure-devops-webhook-config.png "Add Webhook") + +Azure DevOps optionally supports securing the webhook using basic authentication. To use it, specify the username and password in the webhook configuration and configure the same username/password in `argocd-secret` Kubernetes secret in +`webhook.azuredevops.username` and `webhook.azuredevops.password` keys. + +## 2. Configure Argo CD With The WebHook Secret (Optional) Configuring a webhook shared secret is optional, since Argo CD will still refresh applications related to the Git repository, even with unauthenticated webhook events. This is safe to do since @@ -32,24 +41,26 @@ the contents of webhook payloads are considered untrusted, and will only result application (a process which already occurs at three-minute intervals). If Argo CD is publicly accessible, then configuring a webhook secret is recommended to prevent a DDoS attack. -In the `argocd-secret` kubernetes secret, configure one of the following keys with the Git +In the `argocd-secret` Kubernetes secret, configure one of the following keys with the Git provider's webhook secret configured in step 1. | Provider | K8s Secret Key | -|-----------------| ---------------------------------| +|-----------------|----------------------------------| | GitHub | `webhook.github.secret` | | GitLab | `webhook.gitlab.secret` | | BitBucket | `webhook.bitbucket.uuid` | | BitBucketServer | `webhook.bitbucketserver.secret` | | Gogs | `webhook.gogs.secret` | +| Azure DevOps | `webhook.azuredevops.username` | +| | `webhook.azuredevops.password` | -Edit the Argo CD kubernetes secret: +Edit the Argo CD Kubernetes secret: ```bash kubectl edit secret argocd-secret -n argocd ``` -TIP: for ease of entering secrets, kubernetes supports inputting secrets in the `stringData` field, +TIP: for ease of entering secrets, Kubernetes supports inputting secrets in the `stringData` field, which saves you the trouble of base64 encoding the values and copying it to the `data` field. Simply copy the shared webhook secret created in step 1, to the corresponding GitHub/GitLab/BitBucket key under the `stringData` field: @@ -79,6 +90,20 @@ stringData: # gogs server webhook secret webhook.gogs.secret: shhhh! it's a gogs server secret + + # azuredevops username and password + webhook.azuredevops.username: admin + webhook.azuredevops.password: secret-password ``` After saving, the changes should take effect automatically. + +### Alternative + +If you want to store webhook data in **another** Kubernetes `Secret`, instead of `argocd-secret`. ArgoCD knows to check the keys under `data` in your Kubernetes `Secret` starts with `$`, then your Kubernetes `Secret` name and `:` (colon). + +Syntax: `$:` + +> NOTE: Secret must have label `app.kubernetes.io/part-of: argocd` + +For more information refer to the corresponding section in the [User Management Documentation](user-management/index.md#alternative). diff --git a/docs/proposals/002-ui-extensions.md b/docs/proposals/002-ui-extensions.md index 8fa02d25fd11c..583888da68c66 100644 --- a/docs/proposals/002-ui-extensions.md +++ b/docs/proposals/002-ui-extensions.md @@ -63,7 +63,7 @@ As an operator, I would like to configure Argo CD to perform pre-defined actions ## Proposal -A new `ArgoCDExtension` CRD would be introduced which will allow operators configure Argo CD to understand how to handle and visualize custom resources. Visualizing a object requires javascript to render the object, and health/actions require lua scripts. Aas such, the extension CR would need to point to some location where the javascript/lua code would be hosted. +A new `ArgoCDExtension` CRD would be introduced which will allow operators configure Argo CD to understand how to handle and visualize custom resources. Visualizing a object requires javascript to render the object, and health/actions require lua scripts. As such, the extension CR would need to point to some location where the javascript/lua code would be hosted. It is proposed that a git repository be used to contain the javascript code, as well as the lua scripts necessary to assess health or perform actions of a resource. diff --git a/docs/proposals/004-scalability-benchmarking.md b/docs/proposals/004-scalability-benchmarking.md new file mode 100644 index 0000000000000..43c44771850a7 --- /dev/null +++ b/docs/proposals/004-scalability-benchmarking.md @@ -0,0 +1,122 @@ +--- +title: Argo CD Scalability Benchmarking +authors: + - "@andklee" + - "@csantanapr" + - "@morey-tech" + - "@nimakaviani" +sponsors: + - +reviewers: + - + - +approvers: + - + - +creation-date: 2023-02-28 +last-updated: 2023-02-28 +--- + +# Argo CD Scalability Benchmarking + +## Open Questions [optional] +* What are the scalability factors to prioritize when benchmarking Argo CD? + * Reconciliation time for all, one, or several applications in the cluster. + +## Motivation +Users of Argo CD are interested to know how to scale Argo CD, what configuration tweaks and deployment options they have, and how far they can push resources (in terms of the number of supported applications, Git repositories, managing clusters, etc.). + +While the Argo CD documentation [discusses options](https://argo-cd.readthedocs.io/en/stable/operator-manual/high_availability/#scaling-up) to scale up, the actual process is not clear and, as articulated [in this thread](https://github.com/argoproj/argo-cd/issues/9633), oftentimes a point of confusion for users. + +By running large-scale benchmarking, we aim at helping the Argo CD community with the following: + +* Give confidence to organizations running at a significant scale (_needs to be quantified_) that Argo CD can support their use-case, with empirical evidence. +* Create clear guidelines for scaling Argo CD based on the key scalability factors. +* Provide recommendations for which topology is best suited for users based on their needs. +* Determine what work, if any, is needed to improve the scalability of Argo CD. + +### Goals +1. Create a standard set of repeatable benchmarking procedures to objectively measure the limitations of Argo CD. + 1. This may result in a new `argo-cd-benchmarking` repo under `argoproj-labs` so that anyone can easily replicate it and the development of the procedures can happen outside of the lifecycle for Argo CD (unlike [the current `gen-resources` hack](https://github.com/argoproj/argo-cd/tree/master/hack/gen-resources) in the main project). + 2. Include detailed test scenarios that account for key scalability factors that allow for easy tweaking of the parameters to simplify testing of alternative scenarios. +2. Determine the baseline for when tweaking is required on the default configuration (resource allocations and replicas). + 1. One cluster, Applications in In-cluster, default resource allocations. +3. Quantify how tweaking existing parameters (replicas, sharding, parallelism, etc) impacts the performance of Argo CD. +4. Provide a set of metrics and thresholds that will provide a basis for automatically scaling the Argo CD components and alerting for when performance is being impacted by limitations. +5. All tooling, recommendations, examples, and scenarios will be vendor-agnostic. +### Non-Goals +* This proposal does not intend to cover the implementation of any auto-scaling based on the metrics and thresholds determined by the scalability benchmarking. A separate proposal will be used for any auto-scaling enhancements. +* This proposal does not intend to add testing that determines how a change impacts the scalability of Argo CD based on the benchmarks. +* We do not intend to analyze the cost implications of running different topologies and purely focus on scalability requirements from a technology perspective. + +### Initial Members +The initial members for this proposal and their affiliations are: +| Name | Company | +|-----------------------------------------------------|-----------| +| [Andrew Lee](https://github.com/andklee) | AWS | +| [Carlos Santana](https://github.com/csantanapr) | AWS | +| [Nicholas Morey](https://github.com/morey-tech) | Akuity | +| [Nima Kaviani](https://github.com/nimakaviani) | AWS | + +With the introduction of [the proposed Scalability SIG](https://github.com/argoproj/argoproj/pull/192), the members participating in the proposal may change. + +Any community member is welcome to participate in the work for this proposal. Either by joining the Scalability SIG or through contributing to the proposed `argoproj-labs/argo-cd-benchmarking` repository containing the tooling. + +## Proposal +1. Create new `argo-cd-benchmarking` repo under `argoproj-labs` and add the authors of this proposal as maintainers. +2. Create a set of key scalability factors to use as testing parameters. For example: + 1. Number of Applications. + 2. Number of resources managed by an Application. + 3. Number of resources in a cluster. + 4. The size of the resources in a cluster and managed by an Application. + 5. Churn rate for resources in the cluster (how often resources change). + 6. Number of clusters. + 7. Number of repositories being monitored. + 8. Size of the repositories. + 9. The tooling (e.g., directory/raw manifests, Helm, Kustomize). +3. Determine the metrics that reflect limitations in scalability factors. + 1. Application sync time for x number of apps + 2. Emptying the queues (app_reconciliation_queue, app_operation_processing_queue) +4. Create automated testing procedures for Argo CD that take the key scalability factors as testing parameters. +5. Test the default installation of Argo CD to determine the limit based on the key scalability factors. +6. Create test scenarios that reflect the common topologies (Argo CD 1-1 with clusters, Argo CD 1-many with clusters). +7. Determine the thresholds for the metrics identified earlier to capture when performance is being impacted. + 1. Contribute back Grafana thresholds and alerts for Prometheus + +### Use cases +Each use case will cover a specific topology with N permutations based on the key scalability factors. They will be measured using the metrics given in the proposal. + +We intend to focus on two key topologies: one Argo CD per cluster and one Argo CD for all remote clusters. All other variations are an extension of these. + +#### Topology 1: One Argo CD per cluster. +The exact key scalability factors used in each permutation will be determined once we get to the testing. + +#### Topology 2: One Argo CD for all remote clusters. +This will capture the impact of network throughput on performance. + +#### Topology 3: One Namespaced Argo CD. +This instance will only manage namespace-level resources to determine the impact of monitoring cluster-scoped resources (related to the effect of resource churn in the cluster). + +### Implementation Details/Notes/Constraints [optional] +There is already some [tooling in the Argo CD repository](https://github.com/argoproj/argo-cd/pull/8037/files) for scalability testing. We plan to build on the existing effort and further push the boundaries of testing it. + +* Automatically set up supporting tooling for capturing metrics (Grafana, Prometheus) +* Use a local Gitea in the cluster to support many repositories used in testing, and avoid performance variance by depending on the performance of an external git SaaS (ie GitHub) +* Simulate the cluster and nodes using vcluster or kwok, in addition to using a cloud provider with a real cluster fleet. + * The simulated clusters and nodes are intended to make the testing accessible, but ultimately the infrastructure should be easily changed to test more realistic scenarios. Once the benchmarking tooling is functional, we can determine if the simulated components skew the results. + +Once we have the benchmarking tooling, we can determine if the simulated components skew the results compared to the real world. + +AWS intends to provide the infrastructure required to benchmark large-scale scenarios. + +### Security Considerations +There is no intention to change the security model of Argo CD and therefore this project has no direct security considerations. + +### Risks and Mitigations + +## Consider including folks that also work outside your immediate sub-project. + +## Drawbacks + +## Alternatives +* Implementing Argo CD into an environment than waiting for scaling issues to arise. Monitoring the metrics to understand what the limitations are to address them. Using arbitrary resource allocation and replica counts to avoid running into limitations. diff --git a/docs/proposals/applicationset-plugin-generator.md b/docs/proposals/applicationset-plugin-generator.md new file mode 100644 index 0000000000000..616ef13efcd2b --- /dev/null +++ b/docs/proposals/applicationset-plugin-generator.md @@ -0,0 +1,216 @@ +--- +title: applicationset-plugin-generator +authors: + - "@binboum" + - "@scrocquesel" +sponsors: + - TBD +reviewers: + - TBD +approvers: + - "@alexmt" + - TBD + +creation-date: 2022-03-21 +last-updated: 2022-03-21 +--- + +# ApplicationSet `plugin` generator + +Provide a generator that request its values through a RPC call. + +## Summary + +ApplicationSet generators are useful for modeling templates using external data sources to deploy applications. + +Today, generators have been developed based on the needs of the community, and when a new need arises, it's necessary to modify the Appset codebase. + +The proposal here is to have a "plugin" generator that would allow extending the codebase according to specific needs, without having to modify it directly. + +## Motivation + +Using the current generators, we sometimes encounter a need that arises, which may or may not be useful for the community. In such cases, several procedures need to be undertaken to make the modification, and sometimes it may be rejected because it's not in everyone's interest. + +The plugin approach also reduces the burden on community developers by externalizing feature requests into plugins that are outside the Appset controller's scope. From a security and scalability perspective, this can be advantageous. + +With this approach, it becomes possible to offer a catalog of plugins and encourage people with specific needs to develop standalone plugins that are independent of the controller's codebase. + +### Goals + +Empowering community developers to develop and use plugins that extend the list of generators can be a significant advantage. It would be possible to offer a page listing plugins maintained by the community, which can help promote the development of a rich ecosystem of plugins for various use cases. This can enhance the overall user experience by providing more options for generating application templates. + +Additionally, allowing developers to create plugins and share them with the community can foster innovation and encourage experimentation with new features and functionalities. It can also reduce the workload on the Appset development team, enabling them to focus on core features and functionalities. + +Overall, giving autonomy to community developers through plugins is a practical way to enhance the Appset platform and provide more value to users. + +### Non-Goals + +The concept of the plugin should not undermine the spirit of GitOps by externalizing data outside of Git. The goal is to be complementary in specific contexts. + +For example, when using one of the PullRequest generators, it's impossible to retrieve parameters related to the CI (only the commit hash is available), which limits the possibilities. By using a plugin, it's possible to retrieve the necessary parameters from a separate data source and use them to extend the functionality of the generator. This approach allows for greater flexibility and can help overcome limitations imposed by GitOps. + +Overall, the use of plugins should be considered as a way to enhance the capabilities of existing tools and processes rather than as a replacement for them. By leveraging plugins, developers can take advantage of the strengths of different tools and technologies, resulting in a more robust and flexible development process. + +## Proposal + +### Add a new `generator` plugin + +``` +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: fb-plugin + namespace: argo-system +spec: + generators: + - plugin: + configMapRef: fb-plugin + name: feature-branch-plugin + params: + repo: "my-repo" + branch: "my-branch" + requeueAfterSeconds: 10 + template: +... +``` + +### Add a configMap to configure the plugin + +The configMap name must match the configMapRef value in the plugin configuration. The configMap must be in the namespace of argo. + +``` +apiVersion: v1 +kind: ConfigMap +metadata: + name: fb-plugin + namespace: argo-system +data: + token: $plugin.myplugin.token # Alternatively $:plugin.myplugin.token + baseUrl: http://myplugin.plugin.svc.cluster.local +``` + +- token is used a bearer token in the RPC request. It could be a [sensitive reference](https://argo-cd.readthedocs.io/en/stable/operator-manual/user-management/#sensitive-data-and-sso-client-secrets). + +### Reconciliation logic + +Here is a diagram describing what the plugin generator should do to get the params to return: + +```mermaid +sequenceDiagram + alt generator is plugin + Generator->>K8S: Get configmap {configMapRef} + K8S-->>Generator: (url,token) + Generator->>Plugin endpoint: POST {url}/v1/generator.getParams
Authorization: Bearer {token}
Content-Type: application/json
{params} + Plugin endpoint-->>Generator: []map{string}interface{} + end +``` + + +### Use cases + +#### Use case 1: +As a user, I would like to enrich PullRequest generator params with digests of images generated by the pull request CI pipeline. + +I could define a generator matrix like + +```yaml + generators: + - matrix: + generators: + - pullRequest: + github: + owner: binboum + repo: argo-test + labels: + - preview-matrix + tokenRef: + secretName: github-secret + key: token + - plugin: + configMapRef: cm-plugin + name: plugin-matrix + params: + repo: "argo-test" + branch: "{{.branch}}" +``` + +When pullRequest returns a new PR matching my labels, the plugin will be called with the branch name and would return a set of digests like + +```json +[ + { + "digestFront": "xxxxxxxx", + "digestBack": "xxxxxxxx", + } +] +``` + +Values can then be used in the template section : + +```yaml + template: + metadata: + name: "fb-matrix-{{.branch}}" + spec: + source: + repoURL: "git@github.com:binboum/argo-test.git" + targetRevision: "HEAD" + path: charts/app-client + helm: + releaseName: feature-test-matrix-{{.branch}} + valueFiles: + - values.yaml + values: | + front: + image: registry.my/argo-test/front:{{.branch}}@{{ .digestFront }} + back: + image: registry.my/argo-test/back:{{.branch}}@{{ .digestBack }} + destination: + server: https://kubernetes.default.svc + namespace: "{{.branch}}" +``` + +### Detailed examples + +### Security Considerations + +* Plugin server only has access to the params content. When deployed outside of the applicationset controller pod, operator must ensure the communication between applicationset controller and the plugin server is properly secured (https/network policy...). A few authentication mechanism are handled to help the plugin server authenticate the request. +* For now, the response payload is considered trusted and returned params are used as-is upstream + +### Risks and Mitigations + +TBD + +### Upgrade / Downgrade Strategy + +On the evolution of the plugin, and calls : + +The RPC method is standardized with a versioning system, which allows for a version parameter to be included in the API call. This makes it possible to avoid breaking changes in case of architecture changes in the future. + +Thought that the contract interface with the plugin server is kept simple to reduce future changes and breaking changes + +## Drawbacks + +No idea + +## Alternatives + +1. A design similar to Argo Workflow executor plugin : + + ``` + generators: + - plugin: + hello: {} + ``` + + A set of ConfigMaps or a specific CRDs to express configuration of the plugin endpoint would be walk by ApplicationSet server. For each configuration, call the plugin endpoint with the content of plugin until one return a valid response. + + Reconciliation should be fast as fast as possible and trying out every endpoint to figure out which one is able to handle the plugin payload could induce a lot of delay. + + Configuration rely on implicit and weakly typed convention which make the usage of the plugin less self documented. + +2. Plugin server as defacto sidecars + + Some magic could have inject a container image for the plugin in the ApplicationSet controller in a similar way, Argo Workflow does when creating a pod to execute a job. + + Require an external controler or manual configuration. The plugin would not scale independently of the ApplicationSet controller. \ No newline at end of file diff --git a/docs/proposals/config-management-plugin-v2.md b/docs/proposals/config-management-plugin-v2.md index d5d68cc0af942..549ed3967ef49 100644 --- a/docs/proposals/config-management-plugin-v2.md +++ b/docs/proposals/config-management-plugin-v2.md @@ -291,7 +291,7 @@ There aren't any major drawbacks to this proposal. Also, the advantages supersed However following are few minor drawbacks, * With addition of plugin.yaml, there will be more yamls to manage -* Operators need to be aware of the modified kubernetes manifests in the subsequent version. +* Operators need to be aware of the modified Kubernetes manifests in the subsequent version. * The format of the CMP manifest is a new "contract" that would need to adhere the usual Argo CD compatibility promises in future. diff --git a/docs/proposals/decouple-application-sync-user-using-impersonation.md b/docs/proposals/decouple-application-sync-user-using-impersonation.md new file mode 100644 index 0000000000000..e7e459a7059c0 --- /dev/null +++ b/docs/proposals/decouple-application-sync-user-using-impersonation.md @@ -0,0 +1,592 @@ +--- +title: Decouple Control plane and Application Sync privileges +authors: + - "@anandf" +sponsors: + - Red Hat +reviewers: + - "@blakepettersson" + - "@crenshaw-dev" + - "@jannfis" +approvers: + - "@alexmt" + - "@crenshaw-dev" + - "@jannfis" + +creation-date: 2023-06-23 +last-updated: 2024-02-06 +--- + +# Decouple Application Sync using Impersonation + +Application syncs in Argo CD have the same privileges as the Argo CD control plane. As a consequence, in a multi-tenant setup, the Argo CD control plane privileges needs to match the tenant that needs the highest privileges. As an example, if an Argo CD instance has 10 Applications and only one of them requires admin privileges, then the Argo CD control plane must have admin privileges in order to be able to sync that one Application. Argo CD provides a multi-tenancy model to restrict what each Application can do using `AppProjects`, even though the control plane has higher privileges, however that creates a large attack surface since if Argo CD is compromised, attackers would have cluster-admin access to the cluster. + +The goal of this proposal is to perform the Application sync as a different user using impersonation and use the service account provided in the cluster config purely for control plane operations. + +### What is Impersonation + +Impersonation is a feature in Kubernetes and enabled in the `kubectl` CLI client, using which, a user can act as another user through impersonation headers. For example, an admin could use this feature to debug an authorization policy by temporarily impersonating another user and seeing if a request was denied. + +Impersonation requests first authenticate as the requesting user, then switch to the impersonated user info. + +``` +kubectl --as ... +kubectl --as --as-group ... +``` + +## Open Questions [optional] + +- Should the restrictions imposed as part of the `AppProjects` be honored if the impersonation feature is enabled ? +>Yes, other restrictions implemented by `AppProject` related to whitelisting/blacklisting resources must continue to be honoured. +- Can an Application refer to a service account with elevated privileges like say `cluster-admin`, `admin`, and service accounts used for running the ArgoCD controllers itself ? +>Yes, this is possible as long as the ArgoCD admin user explicitly allows it through the `AppProject` configuration. +- Among the destinations configured in the `AppProject`, if there are multiple matches for a given destination, which destination option should be used ? +>If there are more than one matching destination, either with a glob pattern match or an exact match, then we use the first valid match to determine the service account to be used for the sync operation. +- Can the kubernetes audit trail events capture the impersonation. +>Yes, kubernetes audit trail events capture both the actual user and the impersonating user details and hence its possible to track who executed the commands and as which user permissions using the audit trails. +- Would the Sync hooks be using the impersonation service account. +>Yes, if the impersonation feature is enabled and customers use Sync hooks, then impersonation service account would be used for executing the hook jobs as well. +- If application resources have hardcoded namespaces in the git repository, would different service accounts be used for each resource during the sync operation ? +>The service account to be used for impersonation is determined on a per Application level rather than on per resource level. The value specified in `Application.spec.destination.namespace` would be used to determine the service account to be used for the sync operation of all resources present in the `Application`. + +## Summary + +In a multi team/multi tenant environment, an application team is typically granted access to a namespace to self-manage their Applications in a declarative way. Current implementation of ArgoCD requires the ArgoCD Administrator to create an `AppProject` with access settings configured to replicate the RBAC resources that are configured for each team. This approach requires duplication of effort and also requires syncing the access between both to maintain the security posture. It would be desirable for users to use the existing RBAC rules without having to revert to Argo CD API to create and manage these Applications. One namespace per team, or even one namespace per application is what we are looking to address as part of this proposal. + +## Motivation + +This proposal would allow ArgoCD administrators to manage the cluster permissions using kubernetes native RBAC implementation rather than using complex configurations in `AppProjects` to restrict access to individual applications. By decoupling the privileges required for application sync from the privileges required for ArgoCD control plane operations, the security requirement of providing least privileges can be achieved there by improving the security posture of ArgoCD. For implementing multi team/tenant use cases, this decoupling would be greatly beneficial. + +### Assumptions + +- Namespaces are pre-populated with one or more `ServiceAccounts` that define the permissions for each `AppProject`. +- Many users prefer to control access to kubernetes resources through kubernetes RBAC constructs instead of Argo specific constructs. +- Each tenant is generally given access to a specific namespace along with a service account, role or cluster role and role binding to control access to that namespace. +- `Applications` created by a tenant manage namespaced resources. +- An `AppProject` can either be mapped to a single tenant or multiple related tenants and the respective destinations that needs to be managed via the `AppProject`, needs to be configured. + + +### Goals +- Applications may only impersonate ServiceAccounts that live in the same namespace as the destination namespace configured in the application.If the service account is created in a different namespace, then the user can provide the service account name in the format `:` . ServiceAccount to be used for syncing each application is determined by the target destination configured in the `AppProject` associated with the `Application`. +- If impersonation feature is enabled, and no service account name is provided in the associated `AppProject`, then the default service account of the destination namespace of the `Application` should be used. +- Access restrictions implemented through properties in AppProject (if done) must have the existing behavior. From a security standpoint, any restrictions that were available before switching to a service account based approach should continue to exist even when the impersonation feature is enabled. + +### Non-Goals + +None + +## Proposal + +As part of this proposal, it would be possible for an ArgoCD Admin to specify a service account name in `AppProjects` CR for a single or a group of destinations. A destination is uniquely identified by a target cluster and a namespace combined. + +When applications gets synced, based on its destination (target cluster and namespace combination), the `defaultServiceAccount` configured in the `AppProject` will be selected and used for impersonation when executing the kubectl commands for the sync operation. + +We would be introducing a new element `destinationServiceAccounts` in `AppProject.spec`. This element is used for the sole purpose of specifying the impersonation configuration. The `defaultServiceAccount` configured for the `AppProject` would be used for the sync operation for a particular destination cluster and namespace. If impersonation feature is enabled and no specific service account is provided in the `AppProject` CR, then the `default` service account in the destination namespace would be used for impersonation. + +``` +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: my-project + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + description: Example Project + # Allow manifests to deploy from any Git repos + sourceRepos: + - '*' + destinations: + - * + destinationServiceAccounts: + - server: https://kubernetes.default.svc + namespace: guestbook + defaultServiceAccount: guestbook-deployer + - server: https://kubernetes.default.svc + namespace: guestbook-dev + defaultServiceAccount: guestbook-dev-deployer + - server: https://kubernetes.default.svc + namespace: guestbook-stage + defaultServiceAccount: guestbook-stage-deployer +``` + +### Structure of DestinationServiceAccount: +|Parameter| Type | Required/Optional| Description| +| ------ | ------ | ------- | -------- | +| server | string | Required | Server specifies the URL of the target cluster's Kubernetes control plane API. Glob patterns are supported. | +| namespace | string | Required | Namespace specifies the target namespace for the application's resources. Glob patterns are supported. | +| defaultServiceAccount | string | Required| DefaultServiceAccount specifies the service account to be impersonated when performing the `Application` sync operation.| + +**Note:** Only server URL for the target cluster is supported and target cluster name is not supported. + +### Future enhancements + +In a future release, we plan to support overriding of service accounts at the application level. In that case, we would be adding an element called `allowedServiceAccounts` to `AppProject.spec.destinationServiceAccounts[*]` + +### Use cases + +#### Use case 1: + +As a user, I would like to use kubernetes security constructs to restrict user access for application sync +So that, I can provide granular permissions based on the principle of least privilege required for syncing an application. + +#### Use case 2: + +As a user, I would like to configure a common service account for all applications associated to an AppProject +So that, I can use a generic convention of naming service accounts and avoid associating the service account per application. + +### Design considerations + +- Extending the `destinations` field under `AppProjects` was an option that was considered. But since the intent of it was to restrict the destinations that an associated `Application` can use, it was not used. Also the destination fields allowed negation operator (`!`) which would complicate the service account matching logic. The decision to create a new struct under `AppProject.Spec` for specifying the service account for each destination was considered a better alternative. + +- The field name `defaultServiceAccount` was chosen instead of `serviceAccount` as we wanted to support overriding of the service account at an `Application` at a later point in time and wanted to reserve the name `serviceAccount` for future extension. + +- Not supporting all impersonation options at the moment to keep the initial design to a minimum. Based on the need and feedback, support to impersonate users or groups can be added in future. + +### Implementation Details/Notes/Constraints + +#### Component : GitOps Engine + +- Fix GitOps Engine code to honor Impersonate configuration set in the Application sync context for all kubectl commands that are being executed. + +#### Component: ArgoCD API + +- Create a new struct type `DestinationServiceAccount` having fields `namespace`, `server` and `defaultServiceAccount` +- Create a new field `DestinationServiceAccounts` under a `AppProject.Spec` that takes in a list of `DestinationServiceAccount` objects. +- Add Documentation for newly introduced struct and its fields for `DestinationServiceAccount` and `DestinationServiceAccounts` under `AppProject.Spec` + +#### Component: ArgoCD Application Controller + +- Provide a configuration in `argocd-cm` which can be modified to enable the Impersonation feature. Set `applicationcontroller.enable.impersonation: true` in the Argo CD ConfigMap. Default value of `applicationcontroller.enable.impersonation` would be `false` and user has to explicitly override it to use this feature. +- Provide an option to override the Impersonation feature using environment variables. +Set `ARGOCD_APPLICATION_CONTROLLER_ENABLE_IMPERSONATION=true` in the Application controller environment variables. Default value of the environment variable must be `false` and user has to explicitly set it to `true` to use this feature. +- Provide an option to enable this feature using a command line flag `--enable-impersonation`. This new argument option needs to be added to the Application controller args. +- Fix Application Controller `sync.go` to set the Impersonate configuration from the AppProject CR to the `SyncContext` Object (rawConfig and restConfig field, need to understand which config is used for the actual sync and if both configs need to be impersonated.) + +#### Component: ArgoCD UI + +- Provide option to create `DestinationServiceAccount` with fields `namespace`, `server` and `defaultServiceAccount`. +- Provide option to add multiple `DestinationServiceAccounts` to an `AppProject` created/updated via the web console. +- Update the User Guide documentation on how to use these newly added fields from the web console. + +#### Component: ArgoCD CLI + +- Provide option to create `DestinationServiceAccount` with fields `namespace`, `server` and `defaultServiceAccount`. +- Provide option to add multiple `DestinationServiceAccounts` to an `AppProject` created/updated via the web console. +- Update the User Guide and other documentation where the CLI option usages are explained. + +#### Component: Documentation + +- Add note that this is a Beta feature in the documentation. +- Add a separate section for this feature under user-guide section. +- Update the ArgoCD CLI command reference documentation. +- Update the ArgoCD UI command reference documentation. + +### Detailed examples + +#### Example 1: Service account for application sync specified at the AppProject level for all namespaces + +In this specific scenario, service account name `generic-deployer` will get used for the application sync as the namespace `guestbook` matches the glob pattern `*`. + +- Install ArgoCD in the `argocd` namespace. +``` +kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/install.yaml -n argocd +``` + +- Enable the impersonation feature in ArgoCD. +``` +kubectl set env statefulset/argocd-application-controller ARGOCD_APPLICATION_CONTROLLER_ENABLE_IMPERSONATION=true +``` + +- Create a namespace called `guestbook` and a service account called `guestbook-deployer`. +``` +kubectl create namespace guestbook +kubectl create serviceaccount guestbook-deployer +``` + +- Create Role and RoleBindings and configure RBAC access for creating `Service` and `Deployment` objects in namespace `guestbook` for service account `guestbook-deployer`. +``` +kubectl create role guestbook-deployer-role --verb get,list,update,delete --resource pods,deployment,service +kubectl create rolebinding guestbook-deployer-rb --serviceaccount guestbook-deployer --role guestbook-deployer-role +``` + +- Create the `Application` in the `argocd` namespace and the required `AppProject` as below +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: guestbook + namespace: argocd +spec: + project: my-project + source: + repoURL: https://github.com/argoproj/argocd-example-apps.git + targetRevision: HEAD + path: guestbook + destination: + server: https://kubernetes.default.svc + namespace: guestbook +--- +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: my-project + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + description: Example Project + # Allow manifests to deploy from any Git repos + sourceRepos: + - '*' + destinations: + - namespace: * + server: https://kubernetes.default.svc + destinationServiceAccounts: + - namespace: * + server: https://kubernetes.default.svc + defaultServiceAccount: generic-deployer +``` + +#### Example 2: Service account for application sync specified at the AppProject level for specific namespaces + +In this specific scenario, service account name `guestbook-deployer` will get used for the application sync as the namespace `guestbook` matches the target namespace `guestbook`. + +- Install ArgoCD in the `argocd` namespace. +``` +kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/install.yaml -n argocd +``` + +- Enable the impersonation feature in ArgoCD. +``` +kubectl set env statefulset/argocd-application-controller ARGOCD_APPLICATION_CONTROLLER_ENABLE_IMPERSONATION=true +``` + +- Create a namespace called `guestbook` and a service account called `guestbook-deployer`. +``` +kubectl create namespace guestbook +kubectl create serviceaccount guestbook-deployer +``` +- Create Role and RoleBindings and configure RBAC access for creating `Service` and `Deployment` objects in namespace `guestbook` for service account `guestbook-deployer`. +``` +kubectl create role guestbook-deployer-role --verb get,list,update,delete --resource pods,deployment,service +kubectl create rolebinding guestbook-deployer-rb --serviceaccount guestbook-deployer --role guestbook-deployer-role +``` + +In this specific scenario, service account name `guestbook-deployer` will get used as it matches to the specific namespace `guestbook`. +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: guestbook + namespace: argocd +spec: + project: my-project + source: + repoURL: https://github.com/argoproj/argocd-example-apps.git + targetRevision: HEAD + path: guestbook + destination: + server: https://kubernetes.default.svc + namespace: guestbook +--- +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: my-project + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + description: Example Project + # Allow manifests to deploy from any Git repos + sourceRepos: + - '*' + destinations: + - namespace: guestbook + server: https://kubernetes.default.svc + - namespace: guestbook-ui + server: https://kubernetes.default.svc + destinationServiceAccounts: + - namespace: guestbook + server: https://kubernetes.default.svc + defaultServiceAccount: guestbook-deployer + - namespace: guestbook-ui + server: https://kubernetes.default.svc + defaultServiceAccount: guestbook-ui-deployer +``` + +#### Example 3: Remote destination with cluster-admin access and using different service account for the sync operation + +**Note**: In this example, we are relying on the default service account `argocd-manager` with `cluster-admin` privileges which gets created when adding a remote cluster destination using the ArgoCD CLI. + +- Install ArgoCD in the `argocd` namespace. +``` +kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/install.yaml -n argocd +``` + +- Enable the impersonation feature in ArgoCD. +``` +kubectl set env statefulset/argocd-application-controller ARGOCD_APPLICATION_CONTROLLER_ENABLE_IMPERSONATION=true +``` + +- Add the remote cluster as a destination to argocd +``` +argocd cluster add remote-cluster --name remote-cluster +``` +**Note:** The above command would create a service account named `argocd-manager` in `kube-system` namespace and `ClusterRole` named `argocd-manager-role` with full cluster admin access and a `ClusterRoleBinding` named `argocd-manager-role-binding` mapping the `argocd-manager-role` to the service account `remote-cluster` + +- In the remote cluster, create a namespace called `guestbook` and a service account called `guestbook-deployer`. +``` +kubectl ctx remote-cluster +kubectl create namespace guestbook +kubectl create serviceaccount guestbook-deployer +``` + +- In the remote cluster, create `Role` and `RoleBindings` and configure RBAC access for creating `Service` and `Deployment` objects in namespace `guestbook` for service account `guestbook-deployer`. + +``` +kubectl ctx remote-cluster +kubectl create role guestbook-deployer-role --verb get,list,update,delete --resource pods,deployment,service +kubectl create rolebinding guestbook-deployer-rb --serviceaccount guestbook-deployer --role guestbook-deployer-role +``` + +- Create the `Application` and `AppProject` for the `guestbook` application. +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: guestbook + namespace: argocd +spec: + project: my-project + source: + repoURL: https://github.com/argoproj/argocd-example-apps.git + targetRevision: HEAD + path: guestbook + destination: + server: https://kubernetes.default.svc + namespace: guestbook +--- +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: my-project + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + description: Example Project + # Allow manifests to deploy from any Git repos + sourceRepos: + - '*' + destinations: + - namespace: guestbook + server: https://kubernetes.default.svc + serviceAccountName: guestbook-deployer + destinationServiceAccounts: + - namespace: guestbook + server: https://kubernetes.default.svc + defaultServiceAccount: guestbook-deployer +``` + +#### Example 4: Remote destination with a custom service account for the sync operation + +**Note**: In this example, we are relying on a non default service account `guestbook` created in the target cluster and namespace for the sync operation. This use case is for handling scenarios where the remote cluster is managed by a different administrator and providing a service account with `cluster-admin` level access is not feasible. + +- Install ArgoCD in the `argocd` namespace. +``` +kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/install.yaml -n argocd +``` + +- Enable the impersonation feature in ArgoCD. +``` +kubectl set env statefulset/argocd-application-controller ARGOCD_APPLICATION_CONTROLLER_ENABLE_IMPERSONATION=true +``` + +- In the remote cluster, create a service account called `argocd-admin` +``` +kubectl ctx remote-cluster +kubectl create serviceaccount argocd-admin +kubectl create clusterrole argocd-admin-role --verb=impersonate --resource="users,groups,serviceaccounts" +kubectl create clusterrole argocd-admin-role-access-review --verb=create --resource="selfsubjectaccessreviews" +kubectl create clusterrolebinding argocd-admin-role-binding --serviceaccount argocd-admin --clusterrole argocd-admin-role +kubectl create clusterrolebinding argocd-admin-access-review-role-binding --serviceaccount argocd-admin --clusterrole argocd-admin-role +``` + +- In the remote cluster, create a namespace called `guestbook` and a service account called `guestbook-deployer`. +``` +kubectl ctx remote-cluster +kubectl create namespace guestbook +kubectl create serviceaccount guestbook-deployer +``` + +- In the remote cluster, create `Role` and `RoleBindings` and configure RBAC access for creating `Service` and `Deployment` objects in namespace `guestbook` for service account `guestbook-deployer`. +``` +kubectl create role guestbook-deployer-role --verb get,list,update,delete --resource pods,deployment,service +kubectl create rolebinding guestbook-deployer-rb --serviceaccount guestbook-deployer --role guestbook-deployer-role +``` + +In this specific scenario, service account name `guestbook-deployer` will get used as it matches to the specific namespace `guestbook`. +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: guestbook + namespace: argocd +spec: + project: my-project + source: + repoURL: https://github.com/argoproj/argocd-example-apps.git + targetRevision: HEAD + path: guestbook + destination: + server: https://kubernetes.default.svc + namespace: guestbook +--- +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: my-project + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + description: Example Project + # Allow manifests to deploy from any Git repos + sourceRepos: + - '*' + destinations: + - namespace: guestbook + server: https://kubernetes.default.svc + - namespace: guestbook-ui + server: https://kubernetes.default.svc + destinationServiceAccounts: + - namespace: guestbook + server: https://kubernetes.default.svc + defaultServiceAccount: guestbook-deployer + - namespace: guestbook-ui + server: https://kubernetes.default.svc + defaultServiceAccount: guestbook-ui-deployer +``` + +### Special cases + +#### Specifying service account in a different namespace + +By default, the service account would be looked up in the Application's destination namespace configured through `Application.Spec.Destination.Namespace` field. If the service account is in a different namespace, then users can provide the namespace of the service account explicitly in the format : +eg: +``` + ... + destinationServiceAccounts: + - server: https://kubernetes.default.svc + namespace: * + defaultServiceAccount: mynamespace:guestbook-deployer + ... +``` + +#### Multiple matches of destinations + +If there are multiple matches for a given destination, the first valid match in the list of `destinationServiceAccounts` would be used. + +eg: +Lets assume that the `AppProject` has the below `destinationServiceAccounts` configured. +``` + ... + destinationServiceAccounts: + - server: https://kubernetes.default.svc + namespace: guestbook-prod + defaultServiceAccount: guestbook-prod-deployer + - server: https://kubernetes.default.svc + namespace: guestbook-* + defaultServiceAccount: guestbook-generic-deployer + - server: https://kubernetes.default.svc + namespace: * + defaultServiceAccount: generic-deployer + ... +``` +- If the application destination namespace is `myns`, then the service account `generic-deployer` would be used as the first valid match is the glob pattern `*` and there are no other valid matches in the list. +- If the application destination namespace is `guestbook-dev` or `guestbook-stage`, then both glob patterns `*` and `guestbook-*` are valid matches, however `guestbook-*` pattern appears first and hence, the service account `guestbook-generic-deployer` would be used for the impersonation. +- If the application destination namespace is `guestbook-prod`, then there are three candidates, however the first valid match in the list is the one with service account `guestbook-prod-deployer` and that would be used for the impersonation. + +#### Application resources referring to multiple namespaces +If application resources have hardcoded namespaces in the git repository, would different service accounts be used for each resource during the sync operation ? + +The service account to be used for impersonation is determined on a per Application level rather than on per resource level. The value specified in `Application.spec.destination.namespace` would be used to determine the service account to be used for the sync operation of all resources present in the `Application`. + +### Security Considerations + +* How does this proposal impact the security aspects of Argo CD workloads ? +* Are there any unresolved follow-ups that need to be done to make the enhancement more robust ? + +### Risks and Mitigations + +#### Privilege Escalation + +There could be an issue of privilege escalation, if we allow users to impersonate without restrictions. This is mitigated by only allowing admin users to configure service account used for the sync operation at the `AppProject` level. + +Instead of allowing users to impersonate all possible users, administrators can restrict the users a particular service account can impersonate using the `resourceNames` field in the RBAC spec. + + +### Upgrade / Downgrade Strategy + +If applicable, how will the component be upgraded and downgraded? Make sure this is in the test +plan. + +Consider the following in developing an upgrade/downgrade strategy for this enhancement: + +- What changes (in invocations, configurations, API use, etc.) is an existing cluster required to + make on upgrade in order to keep previous behavior? +- What changes (in invocations, configurations, API use, etc.) is an existing cluster required to + make on upgrade in order to make use of the enhancement? + +- This feature would be implemented on an `opt-in` based on a feature flag and disabled by default. +- The new struct being added to `AppProject.Spec` would be introduced as an optional field and would be enabled only if the feature is enabled explicitly by a feature flag. If new property is used in the CR, but the feature flag is not enabled, then a warning message would be displayed during reconciliation of such CRs. + + +## Drawbacks + +- When using this feature, there is an overhead in creating namespaces, service accounts and the required RBAC policies and mapping the service accounts with the corresponding `AppProject` configuration. + +## Alternatives + +### Option 1 +Allow all options available in the `ImpersonationConfig` available to the user through the `AppProject` CRs. + +``` +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: my-project + namespace: argocd +spec: + description: Example Project + # Allow manifests to deploy from any Git repos + sourceRepos: + - '*' + destinations: + - namespace: * + server: https://kubernetes.default.svc + namespace: guestbook + impersonate: + user: system:serviceaccount:dev_ns:admin + uid: 1234 + groups: + - admin + - view + - edit +``` + +### Related issue + +https://github.com/argoproj/argo-cd/issues/7689 + + +### Related links + +https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation + +### Prior art + +https://github.com/argoproj/argo-cd/pull/3377 +https://github.com/argoproj/argo-cd/pull/7651 \ No newline at end of file diff --git a/docs/proposals/feature-bounties.md b/docs/proposals/feature-bounties.md new file mode 100644 index 0000000000000..7d8eb02b33804 --- /dev/null +++ b/docs/proposals/feature-bounties.md @@ -0,0 +1,50 @@ +--- +title: Offering Feature Bounties (Experimental) +authors: + - "@crenshaw-dev" + - "@todaywasawesome" +sponsors: + - "@jannfis" +reviewers: + - TBD +approvers: + - TBD + +creation-date: 2023-06-27 +--- +# Offering Feature Bounties (Experimental) + +## Summary +We'd like to have the ability to offer monetary rewards for significant features to be added to Argo. + +## Motivation +The Argo Project is driven by community contributions and in shared trust with maintainer companies. Sometimes there are important features worth investing in that represent substantial work and are tougher, or take longer to implement. + +By providing a financial incentive, we can spur additional development from the community and indepdent contributors. + +## Proposal +Add the ability to mark a proposal with a bounty and a specific amount. When a PR is successfully merged, release payment to the PR author(s). + +This proposal is experimental, meaning after trying a single bounty, we will review as a project and decide if we would like to continue this program. Accepting this proposal only constitutes the program for a single bounty as an experiment. + +### Guidelines and Rules + +#### Creating a Bounty +A bounty is a special proposal created under `docs/proposals/feature-bounties`. + +* A bounty proposal may only be created by an existing Argo maintainer. +* The proposal document must be reviewed in regular maintainer meetings and an invitation for feedback will provide 7-days to comment. +* Bounty should have approval with [lazy-consensus](https://community.apache.org/committers/lazyConsensus.html) +* Once a bounty is created, they must be honored. +* Bounty progress will be tracked in a GitHub issue linked in the proposal. +* Creating a bounty requires the funds be available and not already committed elsewhere. + +#### Claiming a Bounty +* Argo will pay out bounties once a pull request implementing the requested features/changes/fixes is merged. +* A bounty is limited to a single successful PR. +* Those interested in working on the bounty are encouraged to comment on the issue, and users may team up to split a bounty if they prefer but collaboration is not required and users should not shame eachother for their preferences to work alone or together. +* A comment of interest does not constitute a claim and will not be treated as such. +* The first pull request submitted that is ready for merge will be reviewed by maintainers. Maintainers will also consider any competing pull requests submitted within 24-hours. We expect this will be a very rare circumstance. If multiple, high-quality, merge ready pull requests are submitted, 3-5 Approvers for the sub-project will vote to decide the final pull request merged. + +### Funding +The Argo Project has a small amount of funds from HackerOne bounties that can provide for a few feature bounties. \ No newline at end of file diff --git a/docs/proposals/feature-bounties/hide-annotations.md b/docs/proposals/feature-bounties/hide-annotations.md new file mode 100644 index 0000000000000..47c9b943b8f71 --- /dev/null +++ b/docs/proposals/feature-bounties/hide-annotations.md @@ -0,0 +1,23 @@ +# Proposal: Allow Hiding Certain Annotations in the Argo CD Web UI + +Based on this issue: https://github.com/argoproj/argo-cd/issues/15693 + +Award amount: $100 + +## Solution + +!!! note + This is the proposed solution. The accepted PR may differ from this proposal. + +Add a new config item in argocd-cm: + +```yaml +hide.secret.annotations: | +- openshift.io/token-secret.value +``` + +This will hide the `openshift.io/token-secret.value` annotation from the UI. Behind the scenes, it would likely work the +same way as the `last-applied-configuration` annotation hiding works: https://github.com/argoproj/gitops-engine/blob/b0fffe419a0f0a40f9f2c0b6346b752ed6537385/pkg/diff/diff.go#L897 + +I considered whether we'd want to support hiding things besides annotations and in resources besides secrets, but +having reviewed existing issues, I think this narrow feature is sufficient. diff --git a/docs/proposals/native-oci-support.md b/docs/proposals/native-oci-support.md new file mode 100644 index 0000000000000..7ec0053729c2e --- /dev/null +++ b/docs/proposals/native-oci-support.md @@ -0,0 +1,135 @@ +--- +title: Argo CD first-class OCI support +authors: + - "@sabre1041" + - "@crenshaw-dev" + - "@todaywasawesome" + +sponsors: + - TBD +reviewers: + - "@alexmt" +approvers: + - "@alexmt" + +creation-date: 2023-05-09 +--- + +# Argo CD first-class OCI support + +Storing and retrieving manifests within in OCI registries + +## Summary + +Currently, Argo CD supports obtaining manifests from either a Git repository, a Helm chart repository, or a Helm chart stored within an OCI registry. Given that OCI registries are more frequently being used to store content aside from container images, introduce a mechanism for storing and retrieving manifests that can be used by any of the existing supported tools in any of the supported methods of representing assets that are to be applied to a Kubernetes environment. + + +## Motivation + +The industry is seeing a rapid adoption of OCI Artifacts as a method for storing and retrieving content. Adding support for sourcing resources stored in OCI artifacts not only provides immediate benefits, but opens up additional possible integrations in the future. + +**Dependency Reduction** + + At the present time, a user must have access to either a Git repository, or a remote Helm chart repository. Most users or enterprise organizations already have access to an OCI registry as it represents the primary source of image related content within a Kubernetes environment. By sourcing assets from OCI registries, no additional infrastructure is required in order to store a variety of content types simplifying the set of requirements in order to begin to fully leverage the capabilities of Argo CD. + +**Market Relevance** + +Argo CD continues to be one of the most popular GitOps tools in the industry. As the industry continues to evolve, other tools within the GitOps market have already began to adopt OCI artifacts as a source for storing and retrieving GitOps resources. + +### Goals + +* Enable the retrieval of resources stored as artifacts in OCI registries that are formatted in any of the supported options (Kustomize, Jsonnet, Helm, plain-manifest, CMPs, etc) +* Define a format for storing resources that can be processed by Argo CD as an OCI artifact including the composition and [Media Type(s)](https://github.com/opencontainers/image-spec/blob/main/media-types.md) +* Support the retrieval of artifacts from OCI registries using custom / self signed TLS certificates. +* Support the retrieval of artifacts from OCI registries requiring authentication. + +### Non-Goals + +* CLI Integration to package and publish resources in a format for storage in an OCI registry +* Attach metadata to OCI artifact manifest to provide additional details related to the content (such as original Git source [URL, revision]) + +## Proposal + +This is where we get down to details of what the proposal is about. + +### Use cases + +Add a list of detailed use cases this enhancement intends to take care of. + +#### Publishing and retrieval of content from OCI registries: + +As a user, I would like to make use of content that is represented by any of the supported options (Kustomize, Jsonnet, Helm, plain-manifest, etc) or those that could be consumed using a Config Management Plugin from an OCI registry. + +#### Authenticating to OCI registries: + +As a user, I would like to enforce proper security controls by requiring authentication to an OCI registry and configure Argo CD to be able to interact with this registry. + +#### CLI Integration: + +As a user, I would like the ability to produce, store and retrieve resources (pull/push) in a OCI registry using the Argo CD CLI. + +### Implementation Details/Notes/Constraints + +The Argo CD repo-server currently maintains two types of clients - Helm and git. By adding a third client, and invoking it in the same places as the other two, we can support OCI artifacts. + +It seems likely that we should create a new, common interface to represent all three clients. Then we can instantiate the client we need, toggling on whatever value in the repo config determines what kind of repo we're fetching from. + +#### Format of OCI Artifact + +An OCI artifact can contain any type of binary content. It is important that the content be formatted in a manner that can be consumed by Argo CD. + +#### Content + +Resources that is consumed by Argo CD can be represented by a series of files and folders. To be stored within an OCI artifact, these assets are stored within a compressed tar archive (.tar.gz) OCI layer. The [OCI Image Specification](https://specs.opencontainers.org/image-spec/) allows for metadata to be added through the use of annotations to provide attribute based details describing the included content. This level of detail is important as it satisfies many of the existing capabilities of Argo CD for tracking content, such as Git repository URL, branch name/revision. + + +#### Media Types + +The [OCI Image Specification](https://specs.opencontainers.org/image-spec/) makes extensive use of Media Types to identity the format of content. To provide not only a way that signifies the content of the OCI artifact contains Argo CD manifests, but to define the structure of the content. An understanding of the composition and requirements enable a broad ecosystem of tooling that can be used to produce and consume Argo CD resources within OCI registries. + +Two new Media Types will be used for this purpose as defined below: + +* `application/vnd.cncf.argoproj.argocd.content.v1.tar+gzip` - Primary asset stored within the OCI artifact containing a gzip compressed tar archive of Argo CD resources. Further details are outlined in the prior section. +* `application/vnd.cncf.argoproj.argocd.config.v1+json` - An [OCI Image Configuration](https://specs.opencontainers.org/image-spec/config/) + + +### Detailed examples + + +### Security Considerations + +The direct integration with an external endpoint from the core subsystem of Argo CD introduces several considerations as it relates to security. It is worthy to note that Argo CD currently does support sourcing Helm charts that are stored within OCI registries. However, this interaction is performed by Helm and its underlying library, [ORAS](https://oras.land), and not Argo CD itself. Capabilities included within this proposal can make use of the same libraries to facilitate the interaction. + +#### Credentials + +Security controls may be enforced within the OCI registry to enforce that clients authenticate. The introduction of additional mechanisms to authenticate against target systems is outside the scope of this proposal. However, an integration with existing capabilities and features, such as sourcing from _repository_ credentials is required. + + +### Risks and Mitigation's + +#### Overlap with existing Helm OCI integration + +Argo CD already includes support for sourcing Helm Charts from OCI registries and the retrieval is delegated to functionality provided by Helm. Considerations must be taken into account to determine whether the intent by the end user is to consume an OCI artifact containing Argo CD related resources or a Helm chart. One such method for addressing this concern is to inspect the `mediaType` of the OCI artifact. + + +### Upgrade / Downgrade Strategy + +If applicable, how will the component be upgraded and downgraded? Make sure this is in the test +plan. + +Consider the following in developing an upgrade/downgrade strategy for this enhancement: + +- What changes (in invocations, configurations, API use, etc.) is an existing cluster required to + make on upgrade in order to keep previous behavior? +- What changes (in invocations, configurations, API use, etc.) is an existing cluster required to + make on upgrade in order to make use of the enhancement? + +## Drawbacks + +* Sourcing content from an OCI registry may be perceived to be against GitOps principles as content is not sourced from a Git repository. This concern could be mitigated by attaching additional details related to the content (such as original Git source [URL, revision]). Though it should be noted that the GitOps principles only require a source of truth to be visioned and immutable which OCI registries support. + +## Alternatives + +### Config Management Plugin + +Content stored within OCI artifacts could be sourced using a Config Management Plugin which would not require changes to the core capabilities provided by Argo CD. However, this would be hacky and not represent itself within the Argo CD UI. diff --git a/docs/proposals/parameterized-config-management-plugins.md b/docs/proposals/parameterized-config-management-plugins.md index fa3061b2c3686..749f4efe63687 100644 --- a/docs/proposals/parameterized-config-management-plugins.md +++ b/docs/proposals/parameterized-config-management-plugins.md @@ -256,7 +256,7 @@ spec: array: [values.yaml] - name: helm-parameters map: - image.repository: my.company.com/gcr-proxy/heptio-images/ks-guestbook-demo + image.repository: my.example.com/gcr-proxy/heptio-images/ks-guestbook-demo image.tag: "0.1" ``` @@ -283,7 +283,7 @@ That command, when run by a CMP with the above Application manifest, will print { "name": "helm-parameters", "map": { - "image.repository": "my.company.com/gcr-proxy/heptio-images/ks-guestbook-demo", + "image.repository": "my.example.com/gcr-proxy/heptio-images/ks-guestbook-demo", "image.tag": "0.1" } } @@ -398,7 +398,7 @@ like this: "title": "Helm Parameters", "tooltip": "Parameters to override when generating manifests with Helm", "map": { - "image.repository": "my.company.com/gcr-proxy/heptio-images/ks-guestbook-demo", + "image.repository": "my.example.com/gcr-proxy/heptio-images/ks-guestbook-demo", "image.tag": "0.1" } } @@ -423,7 +423,7 @@ readability.) "title": "Helm Parameters", "tooltip": "Parameters to override when generating manifests with Helm", "map": { - "image.repository": "my.company.com/gcr-proxy/heptio-images/ks-guestbook-demo", + "image.repository": "my.example.com/gcr-proxy/heptio-images/ks-guestbook-demo", "image.tag": "0.1" } } @@ -493,11 +493,11 @@ type ParametersAnnouncement []ParameterAnnouncement - name: images collectionType: map array: # this gets ignored because collectionType is 'map' - - ubuntu:latest=docker.company.com/proxy/ubuntu:latest - - guestbook:v0.1=docker.company.com/proxy/guestbook:v0.1 + - ubuntu:latest=docker.example.com/proxy/ubuntu:latest + - guestbook:v0.1=docker.example.com/proxy/guestbook:v0.1 map: - ubuntu:latest: docker.company.com/proxy/ubuntu:latest - guestbook:v0.1: docker.company.com/proxy/guestbook:v0.1 + ubuntu:latest: docker.example.com/proxy/ubuntu:latest + guestbook:v0.1: docker.example.com/proxy/guestbook:v0.1 ``` 2. **Question**: What do we do if the CMP user sets more than one of `value`/`array`/`map` in the Application spec? @@ -513,11 +513,11 @@ type ParametersAnnouncement []ParameterAnnouncement parameters: - name: images array: # this gets sent to the CMP, but the CMP should ignore it - - ubuntu:latest=docker.company.com/proxy/ubuntu:latest - - guestbook:v0.1=docker.company.com/proxy/guestbook:v0.1 + - ubuntu:latest=docker.example.com/proxy/ubuntu:latest + - guestbook:v0.1=docker.example.com/proxy/guestbook:v0.1 map: - ubuntu:latest: docker.company.com/proxy/ubuntu:latest - guestbook:v0.1: docker.company.com/proxy/guestbook:v0.1 + ubuntu:latest: docker.example.com/proxy/ubuntu:latest + guestbook:v0.1: docker.example.com/proxy/guestbook:v0.1 ``` 3. **Question**: How will the UI know that adding more items to an array or a map is allowed? @@ -528,17 +528,17 @@ type ParametersAnnouncement []ParameterAnnouncement - name: images collectionType: map # users will be allowed to add new items, because this is a map map: - ubuntu:latest: docker.company.com/proxy/ubuntu:latest - guestbook:v0.1: docker.company.com/proxy/guestbook:v0.1 + ubuntu:latest: docker.example.com/proxy/ubuntu:latest + guestbook:v0.1: docker.example.com/proxy/guestbook:v0.1 ``` If the CMP author wants an immutable array or map, they should just break it into individual parameters. ```yaml - name: ubuntu:latest - string: docker.company.com/proxy/ubuntu:latest + string: docker.example.com/proxy/ubuntu:latest - name: guestbook:v0.1 - string: docker.company.com/proxy/guestbook:v0.1 + string: docker.example.com/proxy/guestbook:v0.1 ``` 4. **Question**: What do we do if a CMP announcement doesn't include a `collectionType`? @@ -799,8 +799,8 @@ spec: "title": "Image Overrides", "collectionType": "map", "map": { - "quay.io/argoproj/argocd": "docker.company.com/proxy/argoproj/argocd", - "ubuntu:latest": "docker.company.com/proxy/argoproj/argocd" + "quay.io/argoproj/argocd": "docker.example.com/proxy/argoproj/argocd", + "ubuntu:latest": "docker.example.com/proxy/argoproj/argocd" } } ] diff --git a/docs/proposals/project-repos-and-clusters.md b/docs/proposals/project-repos-and-clusters.md index 1f8258f47a72b..514c389048218 100644 --- a/docs/proposals/project-repos-and-clusters.md +++ b/docs/proposals/project-repos-and-clusters.md @@ -102,7 +102,7 @@ p, proj:my-project:admin, repositories, update, my-project/*, allow This provides extra flexibility so that admin can have stricter rules. e.g.: ``` -p, proj:my-project:admin, repositories, update, my-project/"https://github.my-company.com/*", allow +p, proj:my-project:admin, repositories, update, my-project/"https://github.example.com/*", allow ``` #### UI/CLI Changes diff --git a/docs/proposals/rebalancing-clusters-across-shards-dynamically.md b/docs/proposals/rebalancing-clusters-across-shards-dynamically.md new file mode 100644 index 0000000000000..63ed973004cf5 --- /dev/null +++ b/docs/proposals/rebalancing-clusters-across-shards-dynamically.md @@ -0,0 +1,142 @@ +--- +title: Neat-enhancement-idea +authors: + - "@ishitasequeira" # Authors' github accounts here. +sponsors: + - TBD # List all interested parties here. +reviewers: + - "@alexmt" + - TBD +approvers: + - "@alexmt" + - TBD + +creation-date: yyyy-mm-dd +last-updated: yyyy-mm-dd +--- + +# Neat Enhancement Idea + +Rebalance clusters across shards automatically on changes to the number of available shards. + + +## Open Questions [optional] + +This is where to call out areas of the design that require closure before deciding to implement the +design. + + +## Summary + +Current implementation of sharding uses StatefulSet for the application controller and the goal is to move towards an agile stateless Deployment. Although the application controller does not have any state to preserve, stateful sets were used to get predictable hostnames and the serial number in the hostname was used to get the shard id of a particular instance. Using StatefulSet has the following limitations: + +Any change done to the StatefulSet would cause all the child pods to restart in a serial fashion. This makes scaling up/down of the application controller slow as even existing healthy instances need to be restarted as well. +Scaling up or down happens one at a time. If there are 10 instances and if scaled to 20, then the scaling happens one at a time, causing considerable delay for the scaling to complete. + +Each shard replica knows about the total number of available shards by evaluating the environment variable ARGOCD_CONTROLLER_REPLICAS, which needs to be kept up-to-date with the actual number of available replicas (shards). If the number of replicas does not equal the number set in ARGOCD_CONTROLLER_REPLICAS, sharding will not work as intended, leading to both, unused and overused replicas. As this environment variable is set on the StatefulSet and propagated to the pods, all the pods in the StatefulSet need to be restarted in order to pick up the new number of total shards. + +The current sharding mechanism relies on predictable pod names for the application controller to determine which shard a given replica should impersonate, e.g. the first replica of the StatefulSet (argocd-application-controller-0) will be the first shard, the second replica (argocd-application-controller-1) will be the second and so forth. + +## Motivation + +If the number of available shards is changed (i.e. one or more application controller replicas are added or removed), all pods in the statefulset have to be restarted so that the managed clusters are redistributed over the available shards. Additionally, the application controller workload is deployed as a StatefulSet, which is not intended for dynamic horizontal scaling. + +### Goals + +- Improve the application controller’s ability to scale horizontally with a growing number of clusters +- Remove the need to run application controller as a StatefulSet workload + +### Non-Goals + +- Expand the scope of sharding to other assets than clusters (e.g. applications) +- Make a single shard highly available (e.g. by having 2 or more replicas by shard) + +## Proposal + +### Why use Deployments instead of StatefulSet: +StatefulSet is a Kubernetes resource that manages multiple pods that have unique identities, and are not interchangeable (unlike a regular Kubernetes Deployment, in which pods are stateless and can be destroyed and recreated as often as needed). + +Stateless applications scale horizontally very easily as compared to stateful applications due to the fact that infrastructure allows adding as many computing resources as needed. Changing the StatefulSet to Deployments for Application Controller will allow us to dynamically scale the replicas without restarting existing application controller pods. Also, the shard to application controller assignment would help in making sure the shards are scaled and distributed across the available healhty replicas of application controllers. + +### Distributing shards among Application Controllers: + +Inorder to be able to accurately know which shards are being managed by which application-controller, especially in scenarios of redistribution of load, addition/removal of `application controller`, etc., we would need to have a mechanism to assign clusters to the shards. + +In most scenarios, the service account used by the application controller has read access to all the resources in the cluster. Thus, instead of setting the environment variable ARGOCD_CONTROLLER_REPLICAS representing the number of replicas, the number of replicas can be read directly from the number of healthy replicas of the application controller deployment. + +For other scenarios, some users install controller with only `argocd-application-controller-role` role and use it to manage remote clusters only. In this case, we would need to update the `argocd-application-controller-role` role and allow controller inspect it's own deployment and find out the number of replicas. + +The application controllers will claim one of the available shards by checking which shard is not present in the ConfigMap or is assigned to an unhealthy controller. We will store the assignment list of Application Controller to Shard in ConfigMap. The mapping of Application Controller to Shard will store the below information: + +* Name/Id of the shard +* Name of the Application Controller currently managing the shard +* Last time of successful update to ConfigMap (Heartbeat) + +The mapping will be updated in ConfigMap every X (heartbeat interval) seconds with the help of heartbeat process performed by every application controller. If the heartbeat was not performed by the application controller for a certain time, the application controller is assumed to be unhealthy and the number of healthy/managed shards would be reduced, that is, the number of healthy replicas of the application controller deployment changes. + +The heartbeat interval will be a configurable parameter initialized while setting up the application controller. This way, users will be able to control the frequency at which they want the heartbeat process to take place. + +As part of the readiness probe, we will also add a check whether application controller was able to claim a shard successfully or not. If the shard claim failed, the readiness probe will fail marking the controller as unhealthy. Anytime the number of healthy replicas of application controllers is different from the number of application controllers to shard mappings, we would re-distribute the clusters among the healthy replicas again. We can summarize the above statement using the below formula: + +``` +Number of Replicas ≠ Count of {Application Controller, Shard} mapping +``` + +The below logic can be used to perform application controller to shard assignment: + +1) If a new application controller is added, that is, a new shard is added, we would perform the re-distribution of clusters among the shards with the existing sharding algorithm being used. + +2) In scenarios when one of the application controllers is identified to be unhealthy, we will not trigger the re-ditribution of clusters across shards. The new instance of the application controller will claim this unassigned shard and start managing the shard. + +How will this work? +* The application controller will query the ConfigMap for the status of all the application controllers and last updated heartbeat timestamps. +* It will check if any application controller is flagged as Unhealthy or has not updated its status in ConfigMap during the heartbeat process for a certain period of time. +* If the status for an application controller was already flagged as Unhealthy, we will not re-trigger the redistribution of clusters across healthy shards. The new application controller will come online and try to claim this unassigned shard. +* If the status is not flagged and an application controller has not updated the last active timestamp in a long time, then we mark the Application Controller as Unhealthy and unassign the shard in the ConfigMap. + +*Note:* We will continue to use the cluster to shard assignment approach being used today. + +### Pros +* Every Application Controller would be able to take action on finding the distribution of load. +* Every Application Controller will monitor the status of Unhealthy shard and would be able to take action or flag for action. + +### Cons + +* ~~Possibility of race conditions while flagging the shard as Unhealthy during the heartbeat process. Although this can be handled using the [distributed locks](https://redis.io/docs/manual/patterns/distributed-locks/) in Redis.~~ +As we are using ConfigMap, this Con get's removed. Kubernetes would give conflict errors in case multiple edits are tried on the ConfigMap at the same time. We can leverage this error messages to avoid race conditions. + +* ~~In scenarios when Redis becomes unavailable, the heartbeat mechanism will pause working till the redis comes back online again. This will also pause the dynamic redistribution of clusters till Redis comes back online. The redistribution of clusters will be triggered again when Redis comes back online.~~ We would not see this issue by using ConfigMap instead of Redis. + + +### Security Considerations + +* This would be a breaking change of converting StatefulSets to Deployments. Any automation done by customers which is based on the assumption that the controller is modelled as a StatefulSet would break with this change. + +* ~~We would rely on Redis to store the current Application Controller to Shard mapping. In case the Redis is not available, it would not affect the regular working of ArgoCD. The dynamic distribution of clusters among healthy shards would stop working with the heartbeat process till Redis comes back up online, but the application controllers will continue managing their workloads.~~ We would not rely on Redis by using ConfigMap avoiding this issue. + + +### Upgrade / Downgrade Strategy + +* Working ArgoCD itself should not affected. An initial restart of all the application controller pods is expected when we switch from StatefulSet to Deployment or vice-versa. + +* There would be some initial delays in the reconciliation process during the transistion from StatefulSet to Deployment. If someone is not using sharding at all, they should not face any issues. + +## Alternatives + +An alternative approach would be to use Leader Election strategy. By implementing leader election, one of the healthy application controllers will be appointed as leader. The leader controller will be responsible for assigning clusters to the shards and balancing load across the shards. + +The leader controller will continue sending heartbeats to every replica controller and monitor the health of the controllers. In case one of the replica controllers crashes, the leader will distribute the shards managed by the unhealthy replica among the healthy replicas. + +If the leader goes down, the leader election process will be initiated among the healthy candidates and one of the candidates will be marked as leader who will perform the heartbeat process and redistribution of resources. + +One of the possible examples for selecting the leader is by checking the load handled by each healthy candidate and selecting the candidate which has the least load / number of resources running on it. + +### Pros of Leader Election + +* We can refrain from performing multiple calls to ConfigMap about the load and status of the shards and store it in a local cache within the leader while updating data in ConfigMap on a timely manner (for e.g. every 10 mins). +* Single leaders can easily offer clients consistency because they can see and control all the changes made to the state of the system. + + +### Cons of Leader Election +* A single leader is a single point of failure. If the leader becomes bad, that is, does not distribute clusters properly across shards, it is very difficult to identify or fix the bad behavior and can become a single point of failure +* A single leader means a single point of scaling, both in data size and request rate. When a leader-elected system needs to grow beyond a single leader, it requires a complete re-architecture. diff --git a/docs/proposals/respect-rbac-for-resource-exclusions.md b/docs/proposals/respect-rbac-for-resource-exclusions.md new file mode 100644 index 0000000000000..fb4227638b6e7 --- /dev/null +++ b/docs/proposals/respect-rbac-for-resource-exclusions.md @@ -0,0 +1,74 @@ +--- +title: Respect RBAC for Resource Inclusions/Exclusions + +authors: +- "@gdsoumya" +- "@alexmt" + +sponsors: +- TBD + +reviewers: +- @jannfis + +approvers: +- @jannfis + +creation-date: 2023-05-03 + +--- + +# Enhancement Idea + +This is a proposal to provide the ability to configure argocd controller, to respect the current RBAC permissions +when handling resources besides the already existing resource inclusions and exclusions. + +## Summary + +Argo CD administrator will be able to configure in `argocd-cm`, whether to enable or disable(default) the feature where the controller will +only monitor resources that the current service account allows it to read. + +## Motivation + +Some users restrict the access of the argocd to specific resources using rbac and this feature will enable them to continue +using argocd without having to manually configure resource exclusions for all the resources that they don't want argocd to be managing. + +## Proposal + +The configuration for this will be present in the `argocd-cm`, we will add new boolean field `resource.respectRBAC` in the +cm which can be set to `true` to enable this feature, by default the feature is disabled. + +For the implementation there are 3 proposals : + +1. Modify `gitops-engine` pkg to make a `SelfSubjectAccessReview` request before adding any resource to the watch list, in this approach we are making an extra + api server call to check if controller has access to the resource, this does increase the no. of kubeapi calls made but is more accurate. +2. Modify `gitops-engine` pkg to check for forbidden/unauthorized errors when listing for resources, this is more efficient approach as the + no. of kubeapi calls made does not change, but there is a chance of false positives as similar errors can be returned from kubeapi server or env specific proxies in other situations +3. Combine approaches 1 and 2, in this controller will check the api response for the list call, and if it receives forbidden/unauthorized it will make the `SelfSubjectAccessReview` call. + This approach is accurate and at the same time, only makes extra api calls if the list calls fail in the first place. + +In all solutions, once controller determines that it does not have access to the resource it will stop monitoring it. + +### Implementation decision + +It was decided that we will go with approach 3 from the above list, but instead of a boolean flag we will have the `resource.respectRBAC` take 3 configuration options for the users : + - `strict` : This will perform both the checks i.e. whether the list call response is forbidden/unauthorized and if it is make the `SelfSubjectAccessReview` call to confirm. + - `normal` : This will only check whether the list call response is forbidden/unauthorized and skip `SelfSubjectAccessReview` call. + - unset/empty : This will disable the feature and controller will continue to monitor all resources. + +NOTE: By default `resource.respectRBAC` will be unset or `""` which disables the feature + +Users who are okay with an increase in kube api server calls can opt for strict option while users who are concerned with higher api calls can compromise on the accuracy and opt for the normal option. + +## Security Considerations and Risks + +There are no particular security risks associated with this change, this proposal rather improves the argocd controller +to not access/monitor resources that it does not have permission to access. + +## Upgrade / Downgrade Strategy + +There is no special upgrade strategy needed, all existing argocd configmaps will continue to work +and old configs without the `resource.respectRBAC` config will cause no change in argocd controllers behavior. + +While downgrading to older version, if the user had configured `resource.respectRBAC` previously this would be ignored completely +and argocd would revert to its default behavior of trying to monitor all resources. \ No newline at end of file diff --git a/docs/proposals/sync-timeout.md b/docs/proposals/sync-timeout.md new file mode 100644 index 0000000000000..5d8e5c3b3d86d --- /dev/null +++ b/docs/proposals/sync-timeout.md @@ -0,0 +1,126 @@ +--- +title: Neat-enhancement-idea +authors: + - "@alexmt" +sponsors: + - "@jessesuen" +reviewers: + - "@ishitasequeira" +approvers: + - "@gdsoumya" + +creation-date: 2023-12-16 +last-updated: 2023-12-16 +--- + +# Sync Operation Timeout & Termination Settings + +The Sync Operation Timeout & Termination Settings feature introduces new sync operation settings that control automatic sync operation termination. + +## Summary + + +The feature includes two types of settings: + +* The sync timeout allows users to set a timeout for the sync operation. If the sync operation exceeds this timeout, it will be terminated. + +* The Termination settings are an advanced set of options that enable terminating the sync operation earlier when a known resource is stuck in a +certain state for a specified amount of time. + +## Motivation + +Complex synchronization operations that involve sync hooks and sync waves can be time-consuming and may occasionally become stuck in a specific state +for an extended duration. In certain instances, these operations might indefinitely remain in this state. This situation becomes particularly inconvenient when the +synchronization is initiated by an automation tool like a CI/CD pipeline. In these scenarios, the automation tool may end up waiting indefinitely for the +synchronization process to complete. + +To address this issue, this feature enables users to establish a timeout for the sync operation. If the operation exceeds the specified time limit, +it will be terminated, preventing extended periods of inactivity or indefinite waiting in automated processes. + +### Goals + +The following goals are intended to be met by this enhancement: + +#### [G-1] Synchronization timeout + +The synchronization timeout feature should allow users to set a timeout for the sync operation. If the sync operation exceeds this timeout, it will be terminated. + +#### [G-2] Termination settings + +The termination settings would allow users to terminate the sync operation earlier when a known resource is stuck in a certain state for a specified amount of time. + +## Proposal + +The proposed additional synchronization settings are to be added to the `syncPolicy.terminate` field within the Application CRD. The following features are to be added: + +* `timeout` - The timeout for the sync operation. If the sync operation exceeds this timeout, it will be terminated. +* `resources` - A list of resources to monitor for termination. If any of the resources in the list are stuck in a + certain state for a specified amount of time, the sync operation will be terminated. + +Example: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: guestbook +spec: + ... # standard application spec + + syncPolicy: + terminate: + timeout: 10m # timeout for the sync operation + resources: + - kind: Deployment + name: guestbook-ui + timeout: 5m # timeout for the resource + health: Progressing # health status of the resource +``` + +### Use cases + +Add a list of detailed use cases this enhancement intends to take care of. + +#### Normal sync operation: +As a user, I would like to trigger a sync operation and expect it to complete within a certain time limit. + +#### CI triggered sync operation: +As a user, I would like to trigger a sync operation from a CI/CD pipeline and expect it to complete within a certain time limit. + +#### Preview Applications: +As a user, I would like to leverage ApplicationSet PR generator to generate preview applications and expect the auto sync operation fails automatically +if it exceeds a certain time limit. + +### Implementation Details/Notes/Constraints [optional] + +The application CRD status field already has all required information to implement sync timeout. + +* Global sync timeout: only the operation start time is required to implement this functoinality. It is provided be the `status.operationState.startedAt` field. +* Resources state based termination. This part is a bit more complex and requires information about resources affected/created during the sync operation. Most of +the required information is already available in the Application CRD status field. The `status.operationState.syncResult.resources` field contains a list of resources +affected/created during the sync operation. Each `resource` list item includes the resource name, kind, and the resource health status. In order to provide accurate +duration of the resource health status it is proposed to add `modifiedAt` field to the `resource` list item. This field will be updated every time the resource health/phase +changes. + +### Security Considerations + +Proposed changes don't expand the scope of the application CRD and don't introduce any new security concerns. + +### Risks and Mitigations + +The execution of a synchronization operation is carried out in phases, which involve a series of Kubernetes API calls and typically take up to a few seconds. +There is no easy way to terminate the operation during the phase. So the operation might take few seconds longer than the specified timeout. It does not seems +reasonable to implement a more complex logic to terminate the operation during the phase. So it is proposed to just document that the operation might be terminated +few seconds after the timeout is reached. + +### Upgrade / Downgrade Strategy + +The proposed changes don't require any special upgrade/downgrade strategy. The new settings are optional and can be used by users only if they need them. + +## Drawbacks + +Slight increase of the application syncrhonization logic complexity. + +## Alternatives + +Rely on the external tools to terminate the sync operation. For example, the CI/CD pipeline can terminate the sync operation if it exceeds a certain time limit. \ No newline at end of file diff --git a/docs/requirements.txt b/docs/requirements.txt index f83fae49f0e7b..d350ac4870ee2 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,9 @@ -mkdocs==1.2.3 +mkdocs==1.3.0 +# Strict mode has been disabled in latest versions of mkdocs-material. +# Thus pointing to the older version of mkdocs-material. mkdocs-material==7.1.8 markdown_include==0.6.0 -pygments==2.7.4 +pygments==2.15.0 jinja2==3.0.3 -markdown==3.3.7 \ No newline at end of file +markdown==3.3.7 +pymdown-extensions==10.2.1 \ No newline at end of file diff --git a/docs/snyk/index.md b/docs/snyk/index.md index 520d2b224c90d..5f26934a1b4b4 100644 --- a/docs/snyk/index.md +++ b/docs/snyk/index.md @@ -13,64 +13,50 @@ recent minor releases. | | Critical | High | Medium | Low | |---:|:--------:|:----:|:------:|:---:| -| [go.mod](master/argocd-test.html) | 0 | 0 | 0 | 0 | -| [ui/yarn.lock](master/argocd-test.html) | 0 | 0 | 1 | 0 | -| [dex:v2.35.3](master/ghcr.io_dexidp_dex_v2.35.3.html) | 0 | 0 | 0 | 0 | -| [haproxy:2.6.2-alpine](master/haproxy_2.6.2-alpine.html) | 0 | 0 | 0 | 0 | -| [argocd:latest](master/quay.io_argoproj_argocd_latest.html) | 0 | 0 | 2 | 14 | -| [redis:7.0.7-alpine](master/redis_7.0.7-alpine.html) | 0 | 0 | 0 | 0 | +| [go.mod](master/argocd-test.html) | 0 | 0 | 9 | 0 | +| [ui/yarn.lock](master/argocd-test.html) | 0 | 0 | 0 | 0 | +| [dex:v2.38.0](master/ghcr.io_dexidp_dex_v2.38.0.html) | 0 | 0 | 2 | 1 | +| [haproxy:2.6.14-alpine](master/haproxy_2.6.14-alpine.html) | 0 | 1 | 3 | 1 | +| [argocd:latest](master/quay.io_argoproj_argocd_latest.html) | 0 | 0 | 8 | 14 | +| [redis:7.0.14-alpine](master/redis_7.0.14-alpine.html) | 0 | 0 | 2 | 1 | | [install.yaml](master/argocd-iac-install.html) | - | - | - | - | | [namespace-install.yaml](master/argocd-iac-namespace-install.html) | - | - | - | - | -### v2.6.0-rc4 +### v2.9.9 | | Critical | High | Medium | Low | |---:|:--------:|:----:|:------:|:---:| -| [go.mod](v2.6.0-rc4/argocd-test.html) | 0 | 0 | 0 | 0 | -| [ui/yarn.lock](v2.6.0-rc4/argocd-test.html) | 0 | 0 | 1 | 0 | -| [dex:v2.35.3](v2.6.0-rc4/ghcr.io_dexidp_dex_v2.35.3.html) | 0 | 0 | 0 | 0 | -| [haproxy:2.6.2-alpine](v2.6.0-rc4/haproxy_2.6.2-alpine.html) | 0 | 0 | 0 | 0 | -| [argocd:v2.6.0-rc4](v2.6.0-rc4/quay.io_argoproj_argocd_v2.6.0-rc4.html) | 0 | 0 | 2 | 14 | -| [redis:7.0.7-alpine](v2.6.0-rc4/redis_7.0.7-alpine.html) | 0 | 0 | 0 | 0 | -| [install.yaml](v2.6.0-rc4/argocd-iac-install.html) | - | - | - | - | -| [namespace-install.yaml](v2.6.0-rc4/argocd-iac-namespace-install.html) | - | - | - | - | +| [go.mod](v2.9.9/argocd-test.html) | 0 | 1 | 11 | 0 | +| [ui/yarn.lock](v2.9.9/argocd-test.html) | 0 | 0 | 0 | 0 | +| [dex:v2.37.0](v2.9.9/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 1 | 6 | 1 | +| [haproxy:2.6.14-alpine](v2.9.9/haproxy_2.6.14-alpine.html) | 0 | 1 | 3 | 1 | +| [argocd:v2.9.9](v2.9.9/quay.io_argoproj_argocd_v2.9.9.html) | 0 | 0 | 9 | 14 | +| [redis:7.0.11-alpine](v2.9.9/redis_7.0.11-alpine.html) | 1 | 1 | 6 | 1 | +| [install.yaml](v2.9.9/argocd-iac-install.html) | - | - | - | - | +| [namespace-install.yaml](v2.9.9/argocd-iac-namespace-install.html) | - | - | - | - | -### v2.5.7 +### v2.8.13 | | Critical | High | Medium | Low | |---:|:--------:|:----:|:------:|:---:| -| [go.mod](v2.5.7/argocd-test.html) | 0 | 0 | 4 | 0 | -| [ui/yarn.lock](v2.5.7/argocd-test.html) | 0 | 0 | 4 | 0 | -| [dex:v2.35.3](v2.5.7/ghcr.io_dexidp_dex_v2.35.3.html) | 0 | 0 | 0 | 0 | -| [haproxy:2.6.2-alpine](v2.5.7/haproxy_2.6.2-alpine.html) | 0 | 0 | 0 | 0 | -| [argocd:v2.5.7](v2.5.7/quay.io_argoproj_argocd_v2.5.7.html) | 0 | 0 | 2 | 14 | -| [redis:7.0.7-alpine](v2.5.7/redis_7.0.7-alpine.html) | 0 | 0 | 0 | 0 | -| [install.yaml](v2.5.7/argocd-iac-install.html) | - | - | - | - | -| [namespace-install.yaml](v2.5.7/argocd-iac-namespace-install.html) | - | - | - | - | +| [go.mod](v2.8.13/argocd-test.html) | 0 | 1 | 11 | 0 | +| [ui/yarn.lock](v2.8.13/argocd-test.html) | 0 | 0 | 0 | 0 | +| [dex:v2.37.0](v2.8.13/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 1 | 6 | 1 | +| [haproxy:2.6.14-alpine](v2.8.13/haproxy_2.6.14-alpine.html) | 0 | 1 | 3 | 1 | +| [argocd:v2.8.13](v2.8.13/quay.io_argoproj_argocd_v2.8.13.html) | 0 | 0 | 9 | 14 | +| [redis:7.0.11-alpine](v2.8.13/redis_7.0.11-alpine.html) | 1 | 1 | 6 | 1 | +| [install.yaml](v2.8.13/argocd-iac-install.html) | - | - | - | - | +| [namespace-install.yaml](v2.8.13/argocd-iac-namespace-install.html) | - | - | - | - | -### v2.4.19 +### v2.7.17 | | Critical | High | Medium | Low | |---:|:--------:|:----:|:------:|:---:| -| [go.mod](v2.4.19/argocd-test.html) | 0 | 0 | 4 | 0 | -| [ui/yarn.lock](v2.4.19/argocd-test.html) | 0 | 0 | 4 | 0 | -| [dex:v2.35.3](v2.4.19/ghcr.io_dexidp_dex_v2.35.3.html) | 0 | 0 | 0 | 0 | -| [haproxy:2.0.29-alpine](v2.4.19/haproxy_2.0.29-alpine.html) | 0 | 0 | 0 | 0 | -| [argocd:v2.4.19](v2.4.19/quay.io_argoproj_argocd_v2.4.19.html) | 0 | 0 | 2 | 14 | -| [redis:7.0.7-alpine](v2.4.19/redis_7.0.7-alpine.html) | 0 | 0 | 0 | 0 | -| [install.yaml](v2.4.19/argocd-iac-install.html) | - | - | - | - | -| [namespace-install.yaml](v2.4.19/argocd-iac-namespace-install.html) | - | - | - | - | - -### v2.3.13 - -| | Critical | High | Medium | Low | -|---:|:--------:|:----:|:------:|:---:| -| [go.mod](v2.3.13/argocd-test.html) | 0 | 0 | 4 | 0 | -| [ui/yarn.lock](v2.3.13/argocd-test.html) | 0 | 2 | 6 | 0 | -| [dex:v2.35.3](v2.3.13/ghcr.io_dexidp_dex_v2.35.3.html) | 0 | 0 | 0 | 0 | -| [haproxy:2.0.29-alpine](v2.3.13/haproxy_2.0.29-alpine.html) | 0 | 0 | 0 | 0 | -| [argocd-applicationset:v0.4.1](v2.3.13/quay.io_argoproj_argocd-applicationset_v0.4.1.html) | 0 | 4 | 38 | 29 | -| [argocd:v2.3.13](v2.3.13/quay.io_argoproj_argocd_v2.3.13.html) | 0 | 0 | 2 | 14 | -| [redis:6.2.8-alpine](v2.3.13/redis_6.2.8-alpine.html) | 0 | 0 | 0 | 0 | -| [install.yaml](v2.3.13/argocd-iac-install.html) | - | - | - | - | -| [namespace-install.yaml](v2.3.13/argocd-iac-namespace-install.html) | - | - | - | - | +| [go.mod](v2.7.17/argocd-test.html) | 0 | 0 | 9 | 0 | +| [ui/yarn.lock](v2.7.17/argocd-test.html) | 0 | 1 | 0 | 0 | +| [dex:v2.37.0](v2.7.17/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 1 | 6 | 1 | +| [haproxy:2.6.14-alpine](v2.7.17/haproxy_2.6.14-alpine.html) | 0 | 1 | 3 | 1 | +| [argocd:v2.7.17](v2.7.17/quay.io_argoproj_argocd_v2.7.17.html) | 0 | 0 | 12 | 19 | +| [redis:7.0.14-alpine](v2.7.17/redis_7.0.14-alpine.html) | 0 | 0 | 2 | 1 | +| [install.yaml](v2.7.17/argocd-iac-install.html) | - | - | - | - | +| [namespace-install.yaml](v2.7.17/argocd-iac-namespace-install.html) | - | - | - | - | diff --git a/docs/snyk/master/argocd-iac-install.html b/docs/snyk/master/argocd-iac-install.html index 08b5e4e70a09f..c063a06f7dae8 100644 --- a/docs/snyk/master/argocd-iac-install.html +++ b/docs/snyk/master/argocd-iac-install.html @@ -456,7 +456,7 @@

Snyk test report

-

January 22nd 2023, 12:17:21 am

+

March 24th 2024, 12:17:17 am (UTC+00:00)

Scanned the following path: @@ -466,7 +466,7 @@

Snyk test report

-
32 total issues
+
39 total issues
@@ -482,8 +482,54 @@

Snyk test report

+
+

Role or ClusterRole with dangerous permissions

+
+ +
+ high severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-47 +
  • + +
  • Introduced through: + [DocId: 16] + + rules[5] + + resources + +
  • + +
  • + Line number: 21035 +
  • +
+ +
+ +

Impact

+

Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

+ +

Remediation

+

Consider removing these permissions

+ + +
+
+ + + +
-

Role with dangerous permissions

+

Role or ClusterRole with dangerous permissions

@@ -494,7 +540,7 @@

Role with dangerous permissions

  • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
  • Introduced through: @@ -507,29 +553,29 @@

    Role with dangerous permissions

  • - Line number: 15177 + Line number: 20744

Impact

-

Using this role grants dangerous permissions

+

Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

Remediation

-

Consider removing this permissions

+

Consider removing these permissions


-

Role with dangerous permissions

+

Role or ClusterRole with dangerous permissions

@@ -540,7 +586,7 @@

Role with dangerous permissions

  • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
  • Introduced through: @@ -553,29 +599,29 @@

    Role with dangerous permissions

  • - Line number: 15254 + Line number: 20829

Impact

-

Using this role grants dangerous permissions

+

Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

Remediation

-

Consider removing this permissions

+

Consider removing these permissions


-

Role with dangerous permissions

+

Role or ClusterRole with dangerous permissions

@@ -586,7 +632,7 @@

Role with dangerous permissions

  • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
  • Introduced through: @@ -599,29 +645,29 @@

    Role with dangerous permissions

  • - Line number: 15282 + Line number: 20857

Impact

-

Using this role grants dangerous permissions

+

Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

Remediation

-

Consider removing this permissions

+

Consider removing these permissions


-

Role with dangerous permissions

+

Role or ClusterRole with dangerous permissions

@@ -632,42 +678,42 @@

Role with dangerous permissions

  • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
  • Introduced through: [DocId: 13] - rules[3] + rules[1] resources
  • - Line number: 15326 + Line number: 20887

Impact

-

Using this role grants dangerous permissions

+

Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

Remediation

-

Consider removing this permissions

+

Consider removing these permissions


-

Role with dangerous permissions

+

Role or ClusterRole with dangerous permissions

@@ -678,42 +724,42 @@

Role with dangerous permissions

  • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
  • Introduced through: [DocId: 13] - rules[1] + rules[3] resources
  • - Line number: 15308 + Line number: 20905

Impact

-

Using this role grants dangerous permissions

+

Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

Remediation

-

Consider removing this permissions

+

Consider removing these permissions


-

Role with dangerous permissions

+

Role or ClusterRole with dangerous permissions

@@ -724,7 +770,7 @@

Role with dangerous permissions

  • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
  • Introduced through: @@ -737,24 +783,24 @@

    Role with dangerous permissions

  • - Line number: 15342 + Line number: 20921

Impact

-

Using this role grants dangerous permissions

+

Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

Remediation

-

Consider removing this permissions

+

Consider removing these permissions


@@ -770,11 +816,11 @@

Container could be running with outdated image

  • - Public ID: SNYK-CC-K8S-42 + Public ID: SNYK-CC-K8S-42
  • Introduced through: - [DocId: 46] + [DocId: 47] spec @@ -789,7 +835,7 @@

    Container could be running with outdated image

  • - Line number: 16346 + Line number: 22203
@@ -806,7 +852,7 @@

Remediation

@@ -822,11 +868,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 42] + [DocId: 43] input @@ -847,7 +893,7 @@

    Container has no CPU limit

  • - Line number: 15809 + Line number: 21512
@@ -864,7 +910,7 @@

Remediation

@@ -880,11 +926,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 43] + [DocId: 44] input @@ -905,7 +951,7 @@

    Container has no CPU limit

  • - Line number: 15982 + Line number: 21763
@@ -922,7 +968,7 @@

Remediation

@@ -938,11 +984,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 43] + [DocId: 44] input @@ -963,7 +1009,7 @@

    Container has no CPU limit

  • - Line number: 15948 + Line number: 21729
@@ -980,7 +1026,7 @@

Remediation

@@ -996,11 +1042,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 44] + [DocId: 45] input @@ -1021,7 +1067,7 @@

    Container has no CPU limit

  • - Line number: 16038 + Line number: 21823
@@ -1038,7 +1084,7 @@

Remediation

@@ -1054,11 +1100,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 45] + [DocId: 46] input @@ -1079,7 +1125,7 @@

    Container has no CPU limit

  • - Line number: 16112 + Line number: 21922
@@ -1096,7 +1142,7 @@

Remediation

@@ -1112,11 +1158,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 46] + [DocId: 47] input @@ -1137,7 +1183,7 @@

    Container has no CPU limit

  • - Line number: 16346 + Line number: 22203
@@ -1154,7 +1200,7 @@

Remediation

@@ -1170,11 +1216,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 46] + [DocId: 47] input @@ -1195,7 +1241,7 @@

    Container has no CPU limit

  • - Line number: 16168 + Line number: 21979
@@ -1212,7 +1258,7 @@

Remediation

@@ -1228,11 +1274,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 47] + [DocId: 48] input @@ -1253,7 +1299,7 @@

    Container has no CPU limit

  • - Line number: 16431 + Line number: 22288
@@ -1270,7 +1316,7 @@

Remediation

@@ -1286,11 +1332,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 48] + [DocId: 49] input @@ -1311,7 +1357,7 @@

    Container has no CPU limit

  • - Line number: 16735 + Line number: 22634
@@ -1328,7 +1374,7 @@

Remediation

@@ -1344,11 +1390,11 @@

Container is running with multiple open ports

  • - Public ID: SNYK-CC-K8S-36 + Public ID: SNYK-CC-K8S-36
  • Introduced through: - [DocId: 43] + [DocId: 44] spec @@ -1363,7 +1409,7 @@

    Container is running with multiple open ports

  • - Line number: 15962 + Line number: 21743
@@ -1380,12 +1426,12 @@

Remediation

-

Container is running with writable root filesystem

+

Container is running without liveness probe

@@ -1396,13 +1442,11 @@

Container is running with writable root filesystem

  • - Public ID: SNYK-CC-K8S-8 + Public ID: SNYK-CC-K8S-41
  • Introduced through: - [DocId: 45] - - input + [DocId: 43] spec @@ -1410,33 +1454,31 @@

    Container is running with writable root filesystem

    spec - containers[redis] - - securityContext + containers[argocd-applicationset-controller] - readOnlyRootFilesystem + livenessProbe
  • - Line number: 16122 + Line number: 21512

Impact

-

Compromised process could abuse writable root filesystem to elevate privileges

+

Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

Remediation

-

Set `securityContext.readOnlyRootFilesystem` to `true`

+

Add `livenessProbe` attribute


@@ -1452,11 +1494,11 @@

Container is running without liveness probe

  • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
  • Introduced through: - [DocId: 42] + [DocId: 44] spec @@ -1464,14 +1506,14 @@

    Container is running without liveness probe

    spec - containers[argocd-applicationset-controller] + containers[dex] livenessProbe
  • - Line number: 15809 + Line number: 21729
@@ -1488,7 +1530,7 @@

Remediation

@@ -1504,11 +1546,11 @@

Container is running without liveness probe

  • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
  • Introduced through: - [DocId: 43] + [DocId: 46] spec @@ -1516,14 +1558,14 @@

    Container is running without liveness probe

    spec - containers[dex] + containers[redis] livenessProbe
  • - Line number: 15948 + Line number: 21922
@@ -1540,12 +1582,12 @@

Remediation

-

Container is running without liveness probe

+

Container is running without memory limit

@@ -1556,48 +1598,54 @@

Container is running without liveness probe

  • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
  • Introduced through: [DocId: 43] + input + spec template spec - initContainers[copyutil] + containers[argocd-applicationset-controller] - livenessProbe + resources + + limits + + memory
  • - Line number: 15982 + Line number: 21512

Impact

-

Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

+

Containers without memory limits are more likely to be terminated when the node runs out of memory

Remediation

-

Add `livenessProbe` attribute

+

Set `resources.limits.memory` value


-

Container is running without liveness probe

+

Container is running without memory limit

@@ -1608,11 +1656,13 @@

Container is running without liveness probe

  • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 45] + [DocId: 44] + + input spec @@ -1620,36 +1670,40 @@

    Container is running without liveness probe

    spec - containers[redis] + containers[dex] - livenessProbe + resources + + limits + + memory
  • - Line number: 16112 + Line number: 21729

Impact

-

Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

+

Containers without memory limits are more likely to be terminated when the node runs out of memory

Remediation

-

Add `livenessProbe` attribute

+

Set `resources.limits.memory` value


-

Container is running without liveness probe

+

Container is running without memory limit

@@ -1660,11 +1714,13 @@

Container is running without liveness probe

  • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 46] + [DocId: 44] + + input spec @@ -1674,29 +1730,33 @@

    Container is running without liveness probe

    initContainers[copyutil] - livenessProbe + resources + + limits + + memory
  • - Line number: 16346 + Line number: 21763

Impact

-

Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

+

Containers without memory limits are more likely to be terminated when the node runs out of memory

Remediation

-

Add `livenessProbe` attribute

+

Set `resources.limits.memory` value


@@ -1712,11 +1772,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 42] + [DocId: 45] input @@ -1726,7 +1786,7 @@

    Container is running without memory limit

    spec - containers[argocd-applicationset-controller] + containers[argocd-notifications-controller] resources @@ -1737,7 +1797,7 @@

    Container is running without memory limit

  • - Line number: 15809 + Line number: 21823
@@ -1754,7 +1814,7 @@

Remediation

@@ -1770,11 +1830,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 43] + [DocId: 46] input @@ -1784,7 +1844,7 @@

    Container is running without memory limit

    spec - containers[dex] + containers[redis] resources @@ -1795,7 +1855,7 @@

    Container is running without memory limit

  • - Line number: 15948 + Line number: 21922
@@ -1812,7 +1872,7 @@

Remediation

@@ -1828,11 +1888,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 43] + [DocId: 47] input @@ -1853,7 +1913,7 @@

    Container is running without memory limit

  • - Line number: 15982 + Line number: 22203
@@ -1870,7 +1930,7 @@

Remediation

@@ -1886,11 +1946,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 44] + [DocId: 47] input @@ -1900,7 +1960,7 @@

    Container is running without memory limit

    spec - containers[argocd-notifications-controller] + containers[argocd-repo-server] resources @@ -1911,7 +1971,7 @@

    Container is running without memory limit

  • - Line number: 16038 + Line number: 21979
@@ -1928,7 +1988,7 @@

Remediation

@@ -1944,11 +2004,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 45] + [DocId: 48] input @@ -1958,7 +2018,7 @@

    Container is running without memory limit

    spec - containers[redis] + containers[argocd-server] resources @@ -1969,7 +2029,7 @@

    Container is running without memory limit

  • - Line number: 16112 + Line number: 22288
@@ -1986,7 +2046,7 @@

Remediation

@@ -2002,11 +2062,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 46] + [DocId: 49] input @@ -2016,7 +2076,7 @@

    Container is running without memory limit

    spec - initContainers[copyutil] + containers[argocd-application-controller] resources @@ -2027,7 +2087,7 @@

    Container is running without memory limit

  • - Line number: 16346 + Line number: 22634
@@ -2044,12 +2104,12 @@

Remediation

-

Container is running without memory limit

+

Container's or Pod's UID could clash with host's UID

@@ -2060,11 +2120,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
  • Introduced through: - [DocId: 46] + [DocId: 43] input @@ -2074,40 +2134,94 @@

    Container is running without memory limit

    spec - containers[argocd-repo-server] + containers[argocd-applicationset-controller] - resources + securityContext - limits + runAsUser + +
  • + +
  • + Line number: 21653 +
  • +
+ +
+ +

Impact

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

+ +

Remediation

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

+ + +
+
+ + + +
+
+

Container's or Pod's UID could clash with host's UID

+
+ +
+ low severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-11 +
  • + +
  • Introduced through: + [DocId: 44] - memory + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser
  • - Line number: 16168 + Line number: 21771

Impact

-

Containers without memory limits are more likely to be terminated when the node runs out of memory

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

Remediation

-

Set `resources.limits.memory` value

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


-

Container is running without memory limit

+

Container's or Pod's UID could clash with host's UID

@@ -2118,7 +2232,175 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11 +
  • + +
  • Introduced through: + [DocId: 44] + + input + + spec + + template + + spec + + containers[dex] + + securityContext + + runAsUser + +
  • + +
  • + Line number: 21746 +
  • +
+ +
+ +

Impact

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

+ +

Remediation

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

+ + +
+
+ + + +
+
+

Container's or Pod's UID could clash with host's UID

+
+ +
+ low severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-11 +
  • + +
  • Introduced through: + [DocId: 45] + + input + + spec + + template + + spec + + containers[argocd-notifications-controller] + + securityContext + + runAsUser + +
  • + +
  • + Line number: 21856 +
  • +
+ +
+ +

Impact

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

+ +

Remediation

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

+ + +
+
+ + + +
+
+

Container's or Pod's UID could clash with host's UID

+
+ +
+ low severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-11 +
  • + +
  • Introduced through: + [DocId: 46] + + input + + spec + + template + + spec + + containers[redis] + + securityContext + + runAsUser + +
  • + +
  • + Line number: 21932 +
  • +
+ +
+ +

Impact

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

+ +

Remediation

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

+ + +
+
+ + + +
+
+

Container's or Pod's UID could clash with host's UID

+
+ +
+ low severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-11
  • Introduced through: @@ -2132,40 +2414,94 @@

    Container is running without memory limit

    spec - containers[argocd-server] + initContainers[copyutil] - resources + securityContext - limits + runAsUser + +
  • + +
  • + Line number: 22210 +
  • +
+ +
+ +

Impact

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

+ +

Remediation

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

+ + +
+
+ + + +
+
+

Container's or Pod's UID could clash with host's UID

+
+ +
+ low severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-11 +
  • + +
  • Introduced through: + [DocId: 47] - memory + input + + spec + + template + + spec + + containers[argocd-repo-server] + + securityContext + + runAsUser
  • - Line number: 16431 + Line number: 22176

Impact

-

Containers without memory limits are more likely to be terminated when the node runs out of memory

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

Remediation

-

Set `resources.limits.memory` value

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


-

Container is running without memory limit

+

Container's or Pod's UID could clash with host's UID

@@ -2176,7 +2512,7 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
  • Introduced through: @@ -2190,35 +2526,89 @@

    Container is running without memory limit

    spec - containers[argocd-application-controller] + containers[argocd-server] - resources + securityContext - limits + runAsUser + +
  • + +
  • + Line number: 22544 +
  • +
+ +
+ +

Impact

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

+ +

Remediation

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

+ + +
+
+ + + +
+
+

Container's or Pod's UID could clash with host's UID

+
+ +
+ low severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-11 +
  • + +
  • Introduced through: + [DocId: 49] - memory + input + + spec + + template + + spec + + containers[argocd-application-controller] + + securityContext + + runAsUser
  • - Line number: 16735 + Line number: 22824

Impact

-

Containers without memory limits are more likely to be terminated when the node runs out of memory

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

Remediation

-

Set `resources.limits.memory` value

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


diff --git a/docs/snyk/master/argocd-iac-namespace-install.html b/docs/snyk/master/argocd-iac-namespace-install.html index 76128f58b2dd9..1795ba67af3c6 100644 --- a/docs/snyk/master/argocd-iac-namespace-install.html +++ b/docs/snyk/master/argocd-iac-namespace-install.html @@ -456,7 +456,7 @@

Snyk test report

-

January 22nd 2023, 12:17:30 am

+

March 24th 2024, 12:17:26 am (UTC+00:00)

Scanned the following path: @@ -466,7 +466,7 @@

Snyk test report

-
32 total issues
+
38 total issues
@@ -483,7 +483,7 @@

Snyk test report

-

Role with dangerous permissions

+

Role or ClusterRole with dangerous permissions

@@ -494,7 +494,7 @@

Role with dangerous permissions

  • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
  • Introduced through: @@ -514,22 +514,22 @@

    Role with dangerous permissions


    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


-

Role with dangerous permissions

+

Role or ClusterRole with dangerous permissions

@@ -540,7 +540,7 @@

Role with dangerous permissions

  • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
  • Introduced through: @@ -553,29 +553,29 @@

    Role with dangerous permissions

  • - Line number: 154 + Line number: 162

Impact

-

Using this role grants dangerous permissions

+

Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

Remediation

-

Consider removing this permissions

+

Consider removing these permissions


-

Role with dangerous permissions

+

Role or ClusterRole with dangerous permissions

@@ -586,7 +586,7 @@

Role with dangerous permissions

  • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
  • Introduced through: @@ -599,29 +599,29 @@

    Role with dangerous permissions

  • - Line number: 182 + Line number: 190

Impact

-

Using this role grants dangerous permissions

+

Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

Remediation

-

Consider removing this permissions

+

Consider removing these permissions


-

Role with dangerous permissions

+

Role or ClusterRole with dangerous permissions

@@ -632,42 +632,42 @@

Role with dangerous permissions

  • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
  • Introduced through: [DocId: 10] - rules[3] + rules[1] resources
  • - Line number: 226 + Line number: 220

Impact

-

Using this role grants dangerous permissions

+

Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

Remediation

-

Consider removing this permissions

+

Consider removing these permissions


-

Role with dangerous permissions

+

Role or ClusterRole with dangerous permissions

@@ -678,42 +678,42 @@

Role with dangerous permissions

  • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
  • Introduced through: [DocId: 10] - rules[1] + rules[3] resources
  • - Line number: 208 + Line number: 238

Impact

-

Using this role grants dangerous permissions

+

Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

Remediation

-

Consider removing this permissions

+

Consider removing these permissions


-

Role with dangerous permissions

+

Role or ClusterRole with dangerous permissions

@@ -724,7 +724,7 @@

Role with dangerous permissions

  • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
  • Introduced through: @@ -737,24 +737,24 @@

    Role with dangerous permissions

  • - Line number: 242 + Line number: 254

Impact

-

Using this role grants dangerous permissions

+

Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

Remediation

-

Consider removing this permissions

+

Consider removing these permissions


@@ -770,11 +770,11 @@

Container could be running with outdated image

  • - Public ID: SNYK-CC-K8S-42 + Public ID: SNYK-CC-K8S-42
  • Introduced through: - [DocId: 39] + [DocId: 38] spec @@ -789,7 +789,7 @@

    Container could be running with outdated image

  • - Line number: 1153 + Line number: 1324
@@ -806,7 +806,7 @@

Remediation

@@ -822,11 +822,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 35] + [DocId: 34] input @@ -847,7 +847,7 @@

    Container has no CPU limit

  • - Line number: 616 + Line number: 633
@@ -864,7 +864,7 @@

Remediation

@@ -880,11 +880,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 36] + [DocId: 35] input @@ -905,7 +905,7 @@

    Container has no CPU limit

  • - Line number: 789 + Line number: 884
@@ -922,7 +922,7 @@

Remediation

@@ -938,11 +938,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 36] + [DocId: 35] input @@ -963,7 +963,7 @@

    Container has no CPU limit

  • - Line number: 755 + Line number: 850
@@ -980,7 +980,7 @@

Remediation

@@ -996,11 +996,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 37] + [DocId: 36] input @@ -1021,7 +1021,7 @@

    Container has no CPU limit

  • - Line number: 845 + Line number: 944
@@ -1038,7 +1038,7 @@

Remediation

@@ -1054,11 +1054,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 38] + [DocId: 37] input @@ -1079,7 +1079,7 @@

    Container has no CPU limit

  • - Line number: 919 + Line number: 1043
@@ -1096,7 +1096,7 @@

Remediation

@@ -1112,11 +1112,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 39] + [DocId: 38] input @@ -1137,7 +1137,7 @@

    Container has no CPU limit

  • - Line number: 1153 + Line number: 1324
@@ -1154,7 +1154,7 @@

Remediation

@@ -1170,11 +1170,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 39] + [DocId: 38] input @@ -1195,7 +1195,7 @@

    Container has no CPU limit

  • - Line number: 975 + Line number: 1100
@@ -1212,7 +1212,7 @@

Remediation

@@ -1228,11 +1228,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 40] + [DocId: 39] input @@ -1253,7 +1253,7 @@

    Container has no CPU limit

  • - Line number: 1238 + Line number: 1409
@@ -1270,7 +1270,7 @@

Remediation

@@ -1286,11 +1286,11 @@

Container has no CPU limit

  • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
  • Introduced through: - [DocId: 41] + [DocId: 40] input @@ -1311,7 +1311,7 @@

    Container has no CPU limit

  • - Line number: 1542 + Line number: 1755
@@ -1328,7 +1328,7 @@

Remediation

@@ -1344,11 +1344,11 @@

Container is running with multiple open ports

  • - Public ID: SNYK-CC-K8S-36 + Public ID: SNYK-CC-K8S-36
  • Introduced through: - [DocId: 36] + [DocId: 35] spec @@ -1363,7 +1363,7 @@

    Container is running with multiple open ports

  • - Line number: 769 + Line number: 864
@@ -1380,12 +1380,12 @@

Remediation

-

Container is running with writable root filesystem

+

Container is running without liveness probe

@@ -1396,13 +1396,11 @@

Container is running with writable root filesystem

  • - Public ID: SNYK-CC-K8S-8 + Public ID: SNYK-CC-K8S-41
  • Introduced through: - [DocId: 38] - - input + [DocId: 34] spec @@ -1410,33 +1408,31 @@

    Container is running with writable root filesystem

    spec - containers[redis] - - securityContext + containers[argocd-applicationset-controller] - readOnlyRootFilesystem + livenessProbe
  • - Line number: 929 + Line number: 633

Impact

-

Compromised process could abuse writable root filesystem to elevate privileges

+

Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

Remediation

-

Set `securityContext.readOnlyRootFilesystem` to `true`

+

Add `livenessProbe` attribute


@@ -1452,7 +1448,7 @@

Container is running without liveness probe

  • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
  • Introduced through: @@ -1464,14 +1460,14 @@

    Container is running without liveness probe

    spec - containers[argocd-applicationset-controller] + containers[dex] livenessProbe
  • - Line number: 616 + Line number: 850
@@ -1488,7 +1484,7 @@

Remediation

@@ -1504,11 +1500,11 @@

Container is running without liveness probe

  • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
  • Introduced through: - [DocId: 36] + [DocId: 37] spec @@ -1516,14 +1512,14 @@

    Container is running without liveness probe

    spec - containers[dex] + containers[redis] livenessProbe
  • - Line number: 755 + Line number: 1043
@@ -1540,12 +1536,12 @@

Remediation

-

Container is running without liveness probe

+

Container is running without memory limit

@@ -1556,11 +1552,13 @@

Container is running without liveness probe

  • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 36] + [DocId: 34] + + input spec @@ -1568,36 +1566,40 @@

    Container is running without liveness probe

    spec - initContainers[copyutil] + containers[argocd-applicationset-controller] - livenessProbe + resources + + limits + + memory
  • - Line number: 789 + Line number: 633

Impact

-

Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

+

Containers without memory limits are more likely to be terminated when the node runs out of memory

Remediation

-

Add `livenessProbe` attribute

+

Set `resources.limits.memory` value


-

Container is running without liveness probe

+

Container is running without memory limit

@@ -1608,11 +1610,13 @@

Container is running without liveness probe

  • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 38] + [DocId: 35] + + input spec @@ -1620,36 +1624,40 @@

    Container is running without liveness probe

    spec - containers[redis] + containers[dex] - livenessProbe + resources + + limits + + memory
  • - Line number: 919 + Line number: 850

Impact

-

Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

+

Containers without memory limits are more likely to be terminated when the node runs out of memory

Remediation

-

Add `livenessProbe` attribute

+

Set `resources.limits.memory` value


-

Container is running without liveness probe

+

Container is running without memory limit

@@ -1660,11 +1668,13 @@

Container is running without liveness probe

  • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 39] + [DocId: 35] + + input spec @@ -1674,29 +1684,33 @@

    Container is running without liveness probe

    initContainers[copyutil] - livenessProbe + resources + + limits + + memory
  • - Line number: 1153 + Line number: 884

Impact

-

Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

+

Containers without memory limits are more likely to be terminated when the node runs out of memory

Remediation

-

Add `livenessProbe` attribute

+

Set `resources.limits.memory` value


@@ -1712,11 +1726,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 35] + [DocId: 36] input @@ -1726,7 +1740,7 @@

    Container is running without memory limit

    spec - containers[argocd-applicationset-controller] + containers[argocd-notifications-controller] resources @@ -1737,7 +1751,7 @@

    Container is running without memory limit

  • - Line number: 616 + Line number: 944
@@ -1754,7 +1768,7 @@

Remediation

@@ -1770,11 +1784,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 36] + [DocId: 37] input @@ -1784,7 +1798,7 @@

    Container is running without memory limit

    spec - containers[dex] + containers[redis] resources @@ -1795,7 +1809,7 @@

    Container is running without memory limit

  • - Line number: 755 + Line number: 1043
@@ -1812,7 +1826,7 @@

Remediation

@@ -1828,11 +1842,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 36] + [DocId: 38] input @@ -1853,7 +1867,7 @@

    Container is running without memory limit

  • - Line number: 789 + Line number: 1324
@@ -1870,7 +1884,7 @@

Remediation

@@ -1886,11 +1900,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 37] + [DocId: 38] input @@ -1900,7 +1914,7 @@

    Container is running without memory limit

    spec - containers[argocd-notifications-controller] + containers[argocd-repo-server] resources @@ -1911,7 +1925,7 @@

    Container is running without memory limit

  • - Line number: 845 + Line number: 1100
@@ -1928,7 +1942,7 @@

Remediation

@@ -1944,11 +1958,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 38] + [DocId: 39] input @@ -1958,7 +1972,7 @@

    Container is running without memory limit

    spec - containers[redis] + containers[argocd-server] resources @@ -1969,7 +1983,7 @@

    Container is running without memory limit

  • - Line number: 919 + Line number: 1409
@@ -1986,7 +2000,7 @@

Remediation

@@ -2002,11 +2016,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
  • Introduced through: - [DocId: 39] + [DocId: 40] input @@ -2016,7 +2030,7 @@

    Container is running without memory limit

    spec - initContainers[copyutil] + containers[argocd-application-controller] resources @@ -2027,7 +2041,7 @@

    Container is running without memory limit

  • - Line number: 1153 + Line number: 1755
@@ -2044,12 +2058,12 @@

Remediation

-

Container is running without memory limit

+

Container's or Pod's UID could clash with host's UID

@@ -2060,11 +2074,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
  • Introduced through: - [DocId: 39] + [DocId: 34] input @@ -2074,40 +2088,94 @@

    Container is running without memory limit

    spec - containers[argocd-repo-server] + containers[argocd-applicationset-controller] - resources + securityContext - limits + runAsUser + +
  • + +
  • + Line number: 774 +
  • +
+ +
+ +

Impact

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

+ +

Remediation

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

+ + +
+
+ + + +
+
+

Container's or Pod's UID could clash with host's UID

+
+ +
+ low severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-11 +
  • + +
  • Introduced through: + [DocId: 35] - memory + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser
  • - Line number: 975 + Line number: 892

Impact

-

Containers without memory limits are more likely to be terminated when the node runs out of memory

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

Remediation

-

Set `resources.limits.memory` value

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


-

Container is running without memory limit

+

Container's or Pod's UID could clash with host's UID

@@ -2118,11 +2186,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
  • Introduced through: - [DocId: 40] + [DocId: 35] input @@ -2132,40 +2200,94 @@

    Container is running without memory limit

    spec - containers[argocd-server] + containers[dex] - resources + securityContext - limits + runAsUser + +
  • + +
  • + Line number: 867 +
  • +
+ +
+ +

Impact

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

+ +

Remediation

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

+ + +
+
+ + + +
+
+

Container's or Pod's UID could clash with host's UID

+
+ +
+ low severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-11 +
  • + +
  • Introduced through: + [DocId: 36] - memory + input + + spec + + template + + spec + + containers[argocd-notifications-controller] + + securityContext + + runAsUser
  • - Line number: 1238 + Line number: 977

Impact

-

Containers without memory limits are more likely to be terminated when the node runs out of memory

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

Remediation

-

Set `resources.limits.memory` value

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


-

Container is running without memory limit

+

Container's or Pod's UID could clash with host's UID

@@ -2176,11 +2298,11 @@

Container is running without memory limit

  • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
  • Introduced through: - [DocId: 41] + [DocId: 37] input @@ -2190,35 +2312,257 @@

    Container is running without memory limit

    spec - containers[argocd-application-controller] + containers[redis] - resources + securityContext - limits + runAsUser + +
  • + +
  • + Line number: 1053 +
  • +
+ +
+ +

Impact

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

+ +

Remediation

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

+ + +
+
+ + + +
+
+

Container's or Pod's UID could clash with host's UID

+
+ +
+ low severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-11 +
  • + +
  • Introduced through: + [DocId: 38] - memory + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser
  • - Line number: 1542 + Line number: 1331

Impact

-

Containers without memory limits are more likely to be terminated when the node runs out of memory

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

Remediation

-

Set `resources.limits.memory` value

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

+ + +
+
+ + + +
+
+

Container's or Pod's UID could clash with host's UID

+
+ +
+ low severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-11 +
  • + +
  • Introduced through: + [DocId: 38] + + input + + spec + + template + + spec + + containers[argocd-repo-server] + + securityContext + + runAsUser + +
  • + +
  • + Line number: 1297 +
  • +
+ +
+ +

Impact

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

+ +

Remediation

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

+ + +
+
+ + + +
+
+

Container's or Pod's UID could clash with host's UID

+
+ +
+ low severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-11 +
  • + +
  • Introduced through: + [DocId: 39] + + input + + spec + + template + + spec + + containers[argocd-server] + + securityContext + + runAsUser + +
  • + +
  • + Line number: 1665 +
  • +
+ +
+ +

Impact

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

+ +

Remediation

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

+ + +
+
+ + + +
+
+

Container's or Pod's UID could clash with host's UID

+
+ +
+ low severity +
+ +
+ +
    +
  • + Public ID: SNYK-CC-K8S-11 +
  • + +
  • Introduced through: + [DocId: 40] + + input + + spec + + template + + spec + + containers[argocd-application-controller] + + securityContext + + runAsUser + +
  • + +
  • + Line number: 1945 +
  • +
+ +
+ +

Impact

+

UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

+ +

Remediation

+

Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


diff --git a/docs/snyk/master/argocd-test.html b/docs/snyk/master/argocd-test.html index a139b2f3831bd..b745cf7cbd119 100644 --- a/docs/snyk/master/argocd-test.html +++ b/docs/snyk/master/argocd-test.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,19 +456,20 @@

Snyk test report

-

January 22nd 2023, 12:15:25 am

+

March 24th 2024, 12:15:25 am (UTC+00:00)

Scanned the following paths:
    -
  • /argo-cd/argoproj/argo-cd/v2 (gomodules)
  • /argo-cd (yarn)
  • +
  • /argo-cd/argoproj/argo-cd/v2/go.mod (gomodules)
  • +
  • /argo-cd/ui/yarn.lock (yarn)
-
1 known vulnerabilities
-
1 vulnerable dependency paths
-
1728 dependencies
+
9 known vulnerabilities
+
144 vulnerable dependency paths
+
2037 dependencies
@@ -477,7 +478,7 @@

Snyk test report

-

Regular Expression Denial of Service (ReDoS)

+

LGPL-3.0 license

@@ -488,18 +489,3041 @@

Regular Expression Denial of Service (ReDoS)

  • - Package Manager: npm + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + gopkg.in/retry.v1 +
  • + +
  • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/Azure/kubelogin/pkg/token@0.0.20 and others +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/Azure/kubelogin/pkg/token@0.0.20 + + gopkg.in/retry.v1@1.0.3 + + + +
  • +
+ +
+ +
+ +

LGPL-3.0 license

+ +
+ + + +
+
+

Infinite loop

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
  • +
  • + Package Manager: golang
  • Vulnerable module: - cookiejar + google.golang.org/protobuf/internal/encoding/json +
  • + +
  • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 and others +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/pkg/grpc/http@#d56162821bd1 + + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.21.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/reflection@1.59.0 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health@1.59.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 + + + +
  • +
+ +
+ +
+ +

Overview

+

Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

+

Note:

+

This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

+

Remediation

+

Upgrade google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

+

References

+ + +
+ + + +
+
+

Stack-based Buffer Overflow

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
  • +
  • + Package Manager: golang +
  • +
  • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
  • + +
  • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 and others +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.21.0 + + go.opentelemetry.io/proto/otlp/collector/trace/v1@1.0.0 + + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.16.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/pkg/grpc/http@#d56162821bd1 + + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2/apierror@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.21.0 + + go.opentelemetry.io/proto/otlp/collector/trace/v1@1.0.0 + + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.16.0 + + google.golang.org/protobuf/types/known/structpb@1.31.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.21.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2/apierror@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2/apierror@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.21.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/reflection@1.59.0 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health@1.59.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2/apierror@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2/apierror@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/reflection@1.59.0 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health@1.59.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
+ +
+ +
+ +

Overview

+

Affected versions of this package are vulnerable to Stack-based Buffer Overflow when processing input that uses pathologically deep nesting.

+

Remediation

+

Upgrade google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

+

References

+ + +
+ + + +
+
+

Infinite loop

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
  • +
  • + Package Manager: golang +
  • +
  • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
  • + +
  • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 and others +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.21.0 + + go.opentelemetry.io/proto/otlp/collector/trace/v1@1.0.0 + + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.16.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/pkg/grpc/http@#d56162821bd1 + + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2/apierror@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.21.0 + + go.opentelemetry.io/proto/otlp/collector/trace/v1@1.0.0 + + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.16.0 + + google.golang.org/protobuf/types/known/structpb@1.31.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.21.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2/apierror@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2/apierror@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.21.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/reflection@1.59.0 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health@1.59.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2/apierror@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2/apierror@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/internal/gensupport@0.132.0 + + github.com/googleapis/gax-go/v2@2.12.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/reflection@1.59.0 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + google.golang.org/grpc/health@1.59.0 + + google.golang.org/grpc/health/grpc_health_v1@1.59.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + google.golang.org/api/chat/v1@0.132.0 + + google.golang.org/api/transport/http@0.132.0 + + google.golang.org/api/option@0.132.0 + + google.golang.org/grpc@1.59.0 + + google.golang.org/grpc/internal/transport@1.59.0 + + google.golang.org/grpc/internal/pretty@1.59.0 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + + +
  • +
+ +
+ +
+ +

Overview

+

Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

+

Note:

+

This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

+

Remediation

+

Upgrade google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

+

References

+ + +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/r3labs/diff +
  • + +
  • Introduced through: + + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/r3labs/diff@1.1.0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/r3labs/diff@1.1.0 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-version +
  • + +
  • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, code.gitea.io/sdk/gitea@0.15.1 and others +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + code.gitea.io/sdk/gitea@0.15.1 + + github.com/hashicorp/go-version@1.2.1 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-retryablehttp +
  • + +
  • Introduced through: + + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/hashicorp/go-retryablehttp@0.7.4 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/xanzy/go-gitlab@0.91.1 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-cleanhttp
  • Introduced through: - argo-cd-ui@1.0.0, superagent@7.1.6 and others + github.com/argoproj/argo-cd/v2@0.0.0, github.com/hashicorp/go-retryablehttp@0.7.4 and others
@@ -511,11 +3535,122 @@

Detailed paths

  • Introduced through: - argo-cd-ui@1.0.0 + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/xanzy/go-gitlab@0.91.1 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/xanzy/go-gitlab@0.91.1 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/api@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 + + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/controller@#2daee6022f41 - superagent@7.1.6 + github.com/argoproj/notifications-engine/pkg/subscriptions@#2daee6022f41 - cookiejar@2.1.3 + github.com/argoproj/notifications-engine/pkg/services@#2daee6022f41 + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -526,92 +3661,72 @@

    Detailed paths


    -

    Overview

    -

    Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) via the Cookie.parse function, which uses an insecure regular expression.

    -

    PoC

    -
    const { CookieJar } = require("cookiejar");
    -        
    -        const jar = new CookieJar();
    -        
    -        const start = performance.now();
    -        const attack = "a" + "t".repeat(50_000);
    -        jar.setCookie(attack);
    -        console.log(`CookieJar.setCookie(): ${performance.now() - start}`);
    -        
    -

    Details

    -

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.

    -

    The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.

    -

    Let’s take the following regular expression as an example:

    -
    regex = /A(B|C+)+D/
    -        
    -

    This regular expression accomplishes the following:

    -
      -
    • A The string must start with the letter 'A'
    • -
    • (B|C+)+ The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the + matches one or more times). The + at the end of this section states that we can look for one or more matches of this section.
    • -
    • D Finally, we ensure this section of the string ends with a 'D'
    • -
    -

    The expression would match inputs such as ABBD, ABCCCCD, ABCBCCCD and ACCCCCD

    -

    It most cases, it doesn't take very long for a regex engine to find a match:

    -
    $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD")'
    -        0.04s user 0.01s system 95% cpu 0.052 total
    -        
    -        $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX")'
    -        1.79s user 0.02s system 99% cpu 1.812 total
    -        
    -

    The entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.

    -

    Most Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as catastrophic backtracking.

    -

    Let's look at how our expression runs into this problem, using a shorter string: "ACCCX". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:

    -
      -
    1. CCC
    2. -
    3. CC+C
    4. -
    5. C+CC
    6. -
    7. C+C+C.
    8. -
    -

    The engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use RegEx 101 debugger to see the engine has to take a total of 38 steps before it can determine the string doesn't match.

    -

    From there, the number of steps the engine must use to validate a string just continues to grow.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    StringNumber of C'sNumber of steps
    ACCCX338
    ACCCCX471
    ACCCCCX5136
    ACCCCCCCCCCCCCCX1465,553
    -

    By the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.

    -

    Remediation

    -

    Upgrade cookiejar to version 2.1.4 or higher.

    -

    References

    - +

    MPL-2.0 license

    + +
    + + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/gosimple/slug +
  • + +
  • Introduced through: + + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/gosimple/slug@1.13.1 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/gosimple/slug@1.13.1 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license


diff --git a/docs/snyk/master/ghcr.io_dexidp_dex_v2.35.3.html b/docs/snyk/master/ghcr.io_dexidp_dex_v2.35.3.html deleted file mode 100644 index cc34f61f14aba..0000000000000 --- a/docs/snyk/master/ghcr.io_dexidp_dex_v2.35.3.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
-
-
-
- - - Snyk - Open Source Security - - - - - - - -
-

Snyk test report

- -

January 22nd 2023, 12:15:32 am

-
-
- Scanned the following path: -
    -
  • ghcr.io/dexidp/dex:v2.35.3/dexidp/dex (apk)
  • -
-
- -
-
0 known vulnerabilities
-
0 vulnerable dependency paths
-
14 dependencies
-
-
-
-
-
- - - - - - - -
Project docker-image|ghcr.io/dexidp/dex
Path ghcr.io/dexidp/dex:v2.35.3/dexidp/dex
Package Manager apk
-
-
- No known vulnerabilities detected. -
-
- - - diff --git a/docs/snyk/master/ghcr.io_dexidp_dex_v2.38.0.html b/docs/snyk/master/ghcr.io_dexidp_dex_v2.38.0.html new file mode 100644 index 0000000000000..7d85ddf3861f8 --- /dev/null +++ b/docs/snyk/master/ghcr.io_dexidp_dex_v2.38.0.html @@ -0,0 +1,2561 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
+
+
+
+ + + Snyk - Open Source Security + + + + + + + +
+

Snyk test report

+ +

March 24th 2024, 12:15:32 am (UTC+00:00)

+
+
+ Scanned the following paths: +
    +
  • ghcr.io/dexidp/dex:v2.38.0/dexidp/dex (apk)
  • +
  • ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3//usr/local/bin/gomplate (gomodules)
  • +
  • ghcr.io/dexidp/dex:v2.38.0/dexidp/dex//usr/local/bin/docker-entrypoint (gomodules)
  • +
  • ghcr.io/dexidp/dex:v2.38.0/dexidp/dex//usr/local/bin/dex (gomodules)
  • +
+
+ +
+
27 known vulnerabilities
+
62 vulnerable dependency paths
+
829 dependencies
+
+
+
+
+ +
+
+
+

Out-of-bounds Write

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Package Manager: alpine:3.19 +
  • +
  • + Vulnerable module: + + openssl/libcrypto3 +
  • + +
  • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + openssl/libcrypto3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libcrypto3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + busybox/ssl_client@1.36.1-r15 + + openssl/libcrypto3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libssl3@3.1.4-r2 + + openssl/libcrypto3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + openssl/libssl3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libssl3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + busybox/ssl_client@1.36.1-r15 + + openssl/libssl3@3.1.4-r2 + + + +
  • +
+ +
+ +
+ +

NVD Description

+

Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.19 relevant fixed versions and status.

+

Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

+

Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

+

The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

+

The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

+

The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

+

Remediation

+

Upgrade Alpine:3.19 openssl to version 3.1.4-r3 or higher.

+

References

+ + +
+ + + +
+
+

CVE-2024-0727

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Package Manager: alpine:3.19 +
  • +
  • + Vulnerable module: + + openssl/libcrypto3 +
  • + +
  • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + openssl/libcrypto3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libcrypto3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + busybox/ssl_client@1.36.1-r15 + + openssl/libcrypto3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libssl3@3.1.4-r2 + + openssl/libcrypto3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + openssl/libssl3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libssl3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + busybox/ssl_client@1.36.1-r15 + + openssl/libssl3@3.1.4-r2 + + + +
  • +
+ +
+ +
+ +

NVD Description

+

Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.19 relevant fixed versions and status.

+

Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

+

Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

+

A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

+

OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

+

We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

+

The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

+

Remediation

+

Upgrade Alpine:3.19 openssl to version 3.1.4-r5 or higher.

+

References

+ + +
+ + + +
+
+

Infinite loop

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Vulnerable module: + + google.golang.org/protobuf/internal/encoding/json +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/internal/encoding/json@v1.31.0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/protobuf/internal/encoding/json@v1.31.0 + + + +
  • +
  • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/protobuf/internal/encoding/json@v1.32.0 + + + +
  • +
+ +
+ +
+ +

Overview

+

Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

+

Note:

+

This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

+

Remediation

+

Upgrade google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

+

References

+ + +
+ + + +
+
+

Stack-based Buffer Overflow

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
  • +
+ +
+ +
+ +

Overview

+

Affected versions of this package are vulnerable to Stack-based Buffer Overflow when processing input that uses pathologically deep nesting.

+

Remediation

+

Upgrade google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

+

References

+ + +
+ + + +
+
+

Infinite loop

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
  • +
  • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/protobuf/encoding/protojson@v1.32.0 + + + +
  • +
+ +
+ +
+ +

Overview

+

Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

+

Note:

+

This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

+

Remediation

+

Upgrade google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

+

References

+ + +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/vault/sdk/helper/certutil +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/vault/sdk/helper/certutil@v0.5.0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/certutil@v0.5.0 + + + +
  • +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/compressutil@v0.5.0 + + + +
  • +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/jsonutil@v0.5.0 + + + +
  • +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/pluginutil@v0.5.0 + + + +
  • +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/strutil@v0.5.0 + + + +
  • +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/logical@v0.5.0 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/vault/api +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/vault/api@v1.6.0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/api@v1.6.0 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/serf/coordinate +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/serf/coordinate@v0.9.7 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/serf/coordinate@v0.9.7 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/dexidp/dex /usr/local/bin/dex +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/hcl/v2 +
  • + +
  • Introduced through: + + github.com/dexidp/dex@* and github.com/hashicorp/hcl/v2@v2.13.0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2@v2.13.0 + + + +
  • +
  • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/ext/customdecode@v2.13.0 + + + +
  • +
  • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/ext/tryfunc@v2.13.0 + + + +
  • +
  • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/gohcl@v2.13.0 + + + +
  • +
  • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/hclparse@v2.13.0 + + + +
  • +
  • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/hclsyntax@v2.13.0 + + + +
  • +
  • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/hclwrite@v2.13.0 + + + +
  • +
  • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/json@v2.13.0 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/hcl +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/hcl@v1.0.0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl@v1.0.0 + + + +
  • +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/hcl/token@v1.0.0 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/golang-lru/simplelru +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/golang-lru/simplelru@v0.5.4 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/golang-lru/simplelru@v0.5.4 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-version +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-version@v1.5.0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-version@v1.5.0 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-sockaddr +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-sockaddr@v1.0.2 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-sockaddr@v1.0.2 + + + +
  • +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-sockaddr/template@v1.0.2 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-secure-stdlib/strutil +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-secure-stdlib/strutil@v0.1.2 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-secure-stdlib/strutil@v0.1.2 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-secure-stdlib/parseutil +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-secure-stdlib/parseutil@v0.1.5 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-secure-stdlib/parseutil@v0.1.5 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-secure-stdlib/mlock +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-secure-stdlib/mlock@v0.1.2 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-secure-stdlib/mlock@v0.1.2 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-rootcerts +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-rootcerts@v1.0.2 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-rootcerts@v1.0.2 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-retryablehttp +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-retryablehttp@v0.7.1 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-retryablehttp@v0.7.1 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-plugin +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-plugin@v1.4.4 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-plugin@v1.4.4 + + + +
  • +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-plugin/internal/plugin@v1.4.4 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-immutable-radix +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-immutable-radix@v1.3.1 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-immutable-radix@v1.3.1 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/go-cleanhttp +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-cleanhttp@v0.5.2 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-cleanhttp@v0.5.2 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/errwrap +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/errwrap@v1.1.0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/errwrap@v1.1.0 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/hashicorp/consul/api +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/consul/api@v1.13.0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/consul/api@v1.13.0 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/gosimple/slug +
  • + +
  • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/gosimple/slug@v1.12.0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/gosimple/slug@v1.12.0 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

MPL-2.0 license

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/dexidp/dex /usr/local/bin/dex +
  • +
  • + Package Manager: golang +
  • +
  • + Module: + + github.com/go-sql-driver/mysql +
  • + +
  • Introduced through: + + github.com/dexidp/dex@* and github.com/go-sql-driver/mysql@v1.7.1 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/dexidp/dex@* + + github.com/go-sql-driver/mysql@v1.7.1 + + + +
  • +
+ +
+ +
+ +

MPL-2.0 license

+ +
+ + + +
+
+

Improper Handling of Highly Compressed Data (Data Amplification)

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: ghcr.io/dexidp/dex:v2.38.0/dexidp/dex /usr/local/bin/dex +
  • +
  • + Package Manager: golang +
  • +
  • + Vulnerable module: + + github.com/go-jose/go-jose/v3 +
  • + +
  • Introduced through: + + github.com/dexidp/dex@* and github.com/go-jose/go-jose/v3@v3.0.1 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + github.com/dexidp/dex@* + + github.com/go-jose/go-jose/v3@v3.0.1 + + + +
  • +
+ +
+ +
+ +

Overview

+

Affected versions of this package are vulnerable to Improper Handling of Highly Compressed Data (Data Amplification). An attacker could send a JWE containing compressed data that, when decompressed by Decrypt or DecryptMulti, would use large amounts of memory and CPU.

+

Remediation

+

Upgrade github.com/go-jose/go-jose/v3 to version 3.0.3 or higher.

+

References

+ + +
+ + + +
+
+

CVE-2023-6237

+
+ +
+ low severity +
+ +
+ +
    +
  • + Package Manager: alpine:3.19 +
  • +
  • + Vulnerable module: + + openssl/libcrypto3 +
  • + +
  • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.38.0 and openssl/libcrypto3@3.1.4-r2 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + openssl/libcrypto3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libcrypto3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + busybox/ssl_client@1.36.1-r15 + + openssl/libcrypto3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libssl3@3.1.4-r2 + + openssl/libcrypto3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + openssl/libssl3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libssl3@3.1.4-r2 + + + +
  • +
  • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.38.0 + + busybox/ssl_client@1.36.1-r15 + + openssl/libssl3@3.1.4-r2 + + + +
  • +
+ +
+ +
+ +

NVD Description

+

This vulnerability has not been analyzed by NVD yet.

+

Remediation

+

Upgrade Alpine:3.19 openssl to version 3.1.4-r4 or higher.

+ +
+ + + +
+
+
+
+ + + diff --git a/docs/snyk/master/haproxy_2.6.14-alpine.html b/docs/snyk/master/haproxy_2.6.14-alpine.html new file mode 100644 index 0000000000000..106ec7c2cc72f --- /dev/null +++ b/docs/snyk/master/haproxy_2.6.14-alpine.html @@ -0,0 +1,1376 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
+
+
+
+ + + Snyk - Open Source Security + + + + + + + +
+

Snyk test report

+ +

March 24th 2024, 12:15:37 am (UTC+00:00)

+
+
+ Scanned the following path: +
    +
  • haproxy:2.6.14-alpine (apk)
  • +
+
+ +
+
5 known vulnerabilities
+
45 vulnerable dependency paths
+
18 dependencies
+
+
+
+
+
+ + + + + + + +
Project docker-image|haproxy
Path haproxy:2.6.14-alpine
Package Manager apk
+
+
+
+
+

CVE-2023-5363

+
+ +
+ high severity +
+ +
+ +
    +
  • + Package Manager: alpine:3.18 +
  • +
  • + Vulnerable module: + + openssl/libcrypto3 +
  • + +
  • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
+ +
+ +
+ +

NVD Description

+

Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

+

Issue summary: A bug has been identified in the processing of key and + initialisation vector (IV) lengths. This can lead to potential truncation + or overruns during the initialisation of some symmetric ciphers.

+

Impact summary: A truncation in the IV can result in non-uniqueness, + which could result in loss of confidentiality for some cipher modes.

+

When calling EVP_EncryptInit_ex2(), EVP_DecryptInit_ex2() or + EVP_CipherInit_ex2() the provided OSSL_PARAM array is processed after + the key and IV have been established. Any alterations to the key length, + via the "keylen" parameter or the IV length, via the "ivlen" parameter, + within the OSSL_PARAM array will not take effect as intended, potentially + causing truncation or overreading of these values. The following ciphers + and cipher modes are impacted: RC2, RC4, RC5, CCM, GCM and OCB.

+

For the CCM, GCM and OCB cipher modes, truncation of the IV can result in + loss of confidentiality. For example, when following NIST's SP 800-38D + section 8.2.1 guidance for constructing a deterministic IV for AES in + GCM mode, truncation of the counter portion could lead to IV reuse.

+

Both truncations and overruns of the key and overruns of the IV will + produce incorrect results and could, in some cases, trigger a memory + exception. However, these issues are not currently assessed as security + critical.

+

Changing the key and/or IV lengths is not considered to be a common operation + and the vulnerable API was recently introduced. Furthermore it is likely that + application developers will have spotted this problem during testing since + decryption would fail unless both peers in the communication were similarly + vulnerable. For these reasons we expect the probability of an application being + vulnerable to this to be quite low. However if an application is vulnerable then + this issue is considered very serious. For these reasons we have assessed this + issue as Moderate severity overall.

+

The OpenSSL SSL/TLS implementation is not affected by this issue.

+

The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this because + the issue lies outside of the FIPS provider boundary.

+

OpenSSL 3.1 and 3.0 are vulnerable to this issue.

+

Remediation

+

Upgrade Alpine:3.18 openssl to version 3.1.4-r0 or higher.

+

References

+ + +
+ + + +
+
+

Improper Check for Unusual or Exceptional Conditions

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Package Manager: alpine:3.18 +
  • +
  • + Vulnerable module: + + openssl/libcrypto3 +
  • + +
  • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
+ +
+ +
+ +

NVD Description

+

Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

+

Issue summary: Generating excessively long X9.42 DH keys or checking + excessively long X9.42 DH keys or parameters may be very slow.

+

Impact summary: Applications that use the functions DH_generate_key() to + generate an X9.42 DH key may experience long delays. Likewise, applications + that use DH_check_pub_key(), DH_check_pub_key_ex() or EVP_PKEY_public_check() + to check an X9.42 DH key or X9.42 DH parameters may experience long delays. + Where the key or parameters that are being checked have been obtained from + an untrusted source this may lead to a Denial of Service.

+

While DH_check() performs all the necessary checks (as of CVE-2023-3817), + DH_check_pub_key() doesn't make any of these checks, and is therefore + vulnerable for excessively large P and Q parameters.

+

Likewise, while DH_generate_key() performs a check for an excessively large + P, it doesn't check for an excessively large Q.

+

An application that calls DH_generate_key() or DH_check_pub_key() and + supplies a key or parameters obtained from an untrusted source could be + vulnerable to a Denial of Service attack.

+

DH_generate_key() and DH_check_pub_key() are also called by a number of + other OpenSSL functions. An application calling any of those other + functions may similarly be affected. The other functions affected by this + are DH_check_pub_key_ex(), EVP_PKEY_public_check(), and EVP_PKEY_generate().

+

Also vulnerable are the OpenSSL pkey command line application when using the + "-pubcheck" option, as well as the OpenSSL genpkey command line application.

+

The OpenSSL SSL/TLS implementation is not affected by this issue.

+

The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

+

Remediation

+

Upgrade Alpine:3.18 openssl to version 3.1.4-r1 or higher.

+

References

+ + +
+ + + +
+
+

Out-of-bounds Write

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Package Manager: alpine:3.18 +
  • +
  • + Vulnerable module: + + openssl/libcrypto3 +
  • + +
  • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
+ +
+ +
+ +

NVD Description

+

Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

+

Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

+

Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

+

The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

+

The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

+

The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

+

Remediation

+

Upgrade Alpine:3.18 openssl to version 3.1.4-r3 or higher.

+

References

+ + +
+ + + +
+
+

CVE-2024-0727

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Package Manager: alpine:3.18 +
  • +
  • + Vulnerable module: + + openssl/libcrypto3 +
  • + +
  • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
+ +
+ +
+ +

NVD Description

+

Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

+

Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

+

Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

+

A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

+

OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

+

We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

+

The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

+

Remediation

+

Upgrade Alpine:3.18 openssl to version 3.1.4-r5 or higher.

+

References

+ + +
+ + + +
+
+

CVE-2023-6237

+
+ +
+ low severity +
+ +
+ +
    +
  • + Package Manager: alpine:3.18 +
  • +
  • + Vulnerable module: + + openssl/libcrypto3 +
  • + +
  • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
  • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
  • +
+ +
+ +
+ +

NVD Description

+

This vulnerability has not been analyzed by NVD yet.

+

Remediation

+

Upgrade Alpine:3.18 openssl to version 3.1.4-r4 or higher.

+ +
+ + + +
+
+
+
+ + + diff --git a/docs/snyk/master/haproxy_2.6.2-alpine.html b/docs/snyk/master/haproxy_2.6.2-alpine.html deleted file mode 100644 index c484716686348..0000000000000 --- a/docs/snyk/master/haproxy_2.6.2-alpine.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
-
-
-
- - - Snyk - Open Source Security - - - - - - - -
-

Snyk test report

- -

January 22nd 2023, 12:15:36 am

-
-
- Scanned the following path: -
    -
  • haproxy:2.6.2-alpine (apk)
  • -
-
- -
-
0 known vulnerabilities
-
0 vulnerable dependency paths
-
17 dependencies
-
-
-
-
-
- - - - - - - -
Project docker-image|haproxy
Path haproxy:2.6.2-alpine
Package Manager apk
-
-
- No known vulnerabilities detected. -
-
- - - diff --git a/docs/snyk/master/quay.io_argoproj_argocd_latest.html b/docs/snyk/master/quay.io_argoproj_argocd_latest.html index e2ee32c460857..045db290b0fbb 100644 --- a/docs/snyk/master/quay.io_argoproj_argocd_latest.html +++ b/docs/snyk/master/quay.io_argoproj_argocd_latest.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,37 +456,32 @@

Snyk test report

-

January 22nd 2023, 12:15:59 am

+

March 24th 2024, 12:15:54 am (UTC+00:00)

- Scanned the following path: + Scanned the following paths:
    -
  • quay.io/argoproj/argocd:latest/argoproj/argocd (deb)
  • +
  • quay.io/argoproj/argocd:latest/argoproj/argocd/Dockerfile (deb)
  • +
  • quay.io/argoproj/argocd:latest/argoproj/argo-cd/v2//usr/local/bin/argocd (gomodules)
  • +
  • quay.io/argoproj/argocd:latest//usr/local/bin/kustomize (gomodules)
  • +
  • quay.io/argoproj/argocd:latest/helm/v3//usr/local/bin/helm (gomodules)
  • +
  • quay.io/argoproj/argocd:latest/git-lfs/git-lfs//usr/bin/git-lfs (gomodules)
-
16 known vulnerabilities
-
102 vulnerable dependency paths
-
162 dependencies
+
32 known vulnerabilities
+
175 vulnerable dependency paths
+
2276 dependencies
-
- - - - - - - -
Project docker-image|quay.io/argoproj/argocd
Path quay.io/argoproj/argocd:latest/argoproj/argocd
Package Manager deb
Manifest Dockerfile
-
+
-

Off-by-one Error

+

CVE-2020-22916

@@ -496,18 +491,173 @@

Off-by-one Error


    +
  • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
  • Package Manager: ubuntu:22.04
  • Vulnerable module: - systemd/libsystemd0 + xz-utils/liblzma5 +
  • + +
  • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and xz-utils/liblzma5@5.2.5-2ubuntu1 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + xz-utils/liblzma5@5.2.5-2ubuntu1 + + + +
  • +
+ +
+ +
+ +

NVD Description

+

Note: Versions mentioned in the description apply only to the upstream xz-utils package and not the xz-utils package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

+

An issue discovered in XZ 5.2.5 allows attackers to cause a denial of service via decompression of a crafted file. NOTE: the vendor disputes the claims of "endless output" and "denial of service" because decompression of the 17,486 bytes always results in 114,881,179 bytes, which is often a reasonable size increase.

+

Remediation

+

There is no fixed version for Ubuntu:22.04 xz-utils.

+

References

+ + +
+ + + +
+
+

CVE-2023-51767

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
  • +
  • + Package Manager: ubuntu:22.04 +
  • +
  • + Vulnerable module: + + openssh/openssh-client +
  • + +
  • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and openssh/openssh-client@1:8.9p1-3ubuntu0.6 + +
  • +
+ +
+ + +

Detailed paths

+ +
    +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + + +
  • +
+ +
+ +
+ +

NVD Description

+

Note: Versions mentioned in the description apply only to the upstream openssh package and not the openssh package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

+

OpenSSH through 9.6, when common types of DRAM are used, might allow row hammer attacks (for authentication bypass) because the integer value of authenticated in mm_answer_authpassword does not resist flips of a single bit. NOTE: this is applicable to a certain threat model of attacker-victim co-location in which the attacker has user privileges.

+

Remediation

+

There is no fixed version for Ubuntu:22.04 openssh.

+

References

+ + +
+ + + +
+
+

Information Exposure

+
+ +
+ medium severity +
+ +
+ +
    +
  • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
  • +
  • + Package Manager: ubuntu:22.04 +
  • +
  • + Vulnerable module: + + libgcrypt20
  • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and systemd/libsystemd0@249.11-0ubuntu3.6 + docker-image|quay.io/argoproj/argocd@latest and libgcrypt20@1.9.4-3ubuntu3
@@ -522,7 +672,7 @@

Detailed paths

Introduced through: docker-image|quay.io/argoproj/argocd@latest - systemd/libsystemd0@249.11-0ubuntu3.6 + libgcrypt20@1.9.4-3ubuntu3 @@ -531,9 +681,9 @@

Detailed paths

Introduced through: docker-image|quay.io/argoproj/argocd@latest - apt@2.4.8 + gnupg2/dirmngr@2.2.27-3ubuntu2.1 - systemd/libsystemd0@249.11-0ubuntu3.6 + libgcrypt20@1.9.4-3ubuntu3 @@ -542,9 +692,9 @@

Detailed paths

Introduced through: docker-image|quay.io/argoproj/argocd@latest - procps/libprocps8@2:3.3.17-6ubuntu2 + gnupg2/gpg@2.2.27-3ubuntu2.1 - systemd/libsystemd0@249.11-0ubuntu3.6 + libgcrypt20@1.9.4-3ubuntu3 @@ -553,77 +703,1693 @@

Detailed paths

Introduced through: docker-image|quay.io/argoproj/argocd@latest - util-linux@2.37.2-4ubuntu3 + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + libgcrypt20@1.9.4-3ubuntu3 + + + + +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + apt@2.4.11 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/gnupg@2.2.27-3ubuntu2.1 - systemd/libsystemd0@249.11-0ubuntu3.6 + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + libgcrypt20@1.9.4-3ubuntu3 -
  • + + + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream libgcrypt20 package and not the libgcrypt20 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A timing-based side-channel flaw was found in libgcrypt's RSA implementation. This issue may allow a remote attacker to initiate a Bleichenbacher-style attack, which can lead to the decryption of RSA ciphertexts.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 libgcrypt20.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-26461

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libkrb5support0@1.19.2-2ubuntu0.3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    Kerberos 5 (aka krb5) 1.21.2 contains a memory leak vulnerability in /krb5/src/lib/gssapi/krb5/k5sealv3.c.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-26462

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libkrb5support0@1.19.2-2ubuntu0.3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    Kerberos 5 (aka krb5) 1.21.2 contains a memory leak vulnerability in /krb5/src/kdc/ndr.c.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-26458

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + krb5/libkrb5support0@1.19.2-2ubuntu0.3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    Kerberos 5 (aka krb5) 1.21.2 contains a memory leak in /krb5/src/lib/rpc/pmap_rmt.c.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    References

    + + +
    + + + +
    +
    +

    LGPL-3.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + gopkg.in/retry.v1 +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and gopkg.in/retry.v1@v1.0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + gopkg.in/retry.v1@v1.0.3 + + + +
    • +
    + +
    + +
    + +

    LGPL-3.0 license

    + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/internal/encoding/json +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/protobuf/internal/encoding/json@v1.31.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + google.golang.org/protobuf/internal/encoding/json@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Stack-based Buffer Overflow

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Stack-based Buffer Overflow when processing input that uses pathologically deep nesting.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Information Exposure

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gnutls28/libgnutls30 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + apt@2.4.11 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + openldap/libldap-2.5-0@2.5.17+dfsg-0ubuntu0.22.04.1 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + rtmpdump/librtmp1@2.4+20151223.gitfa8646d.1-2build4 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream gnutls28 package and not the gnutls28 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A flaw was found in GnuTLS. The Minerva attack is a cryptographic vulnerability that exploits deterministic behavior in systems like GnuTLS, leading to side-channel leaks. In specific scenarios, such as when using the GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE flag, it can result in a noticeable step in nonce size from 513 to 512 bits, exposing a potential timing side-channel.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 gnutls28.

    +

    References

    + + +
    + + + +
    +
    +

    Uncaught Exception

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gnutls28/libgnutls30 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + apt@2.4.11 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + openldap/libldap-2.5-0@2.5.17+dfsg-0ubuntu0.22.04.1 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + rtmpdump/librtmp1@2.4+20151223.gitfa8646d.1-2build4 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream gnutls28 package and not the gnutls28 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A flaw has been discovered in GnuTLS where an application crash can be induced when attempting to verify a specially crafted .pem bundle using the "certtool --verify-chain" command.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 gnutls28.

    +

    References

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/r3labs/diff +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/r3labs/diff@v1.1.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/r3labs/diff@v1.1.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-version +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-version@v1.2.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/hashicorp/go-version@v1.2.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-retryablehttp +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-retryablehttp@v0.7.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/hashicorp/go-retryablehttp@v0.7.4 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/helm/v3 /usr/local/bin/helm +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-multierror +
    • + +
    • Introduced through: + + helm.sh/helm/v3@* and github.com/hashicorp/go-multierror@v1.1.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + helm.sh/helm/v3@* + + github.com/hashicorp/go-multierror@v1.1.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-cleanhttp +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-cleanhttp@v0.5.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - util-linux/bsdutils@1:2.37.2-4ubuntu3 + github.com/argoproj/argo-cd/v2@* - systemd/libsystemd0@249.11-0ubuntu3.6 + github.com/hashicorp/go-cleanhttp@v0.5.2
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - apt@2.4.8 - - apt/libapt-pkg6.0@2.4.8 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - +
    - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - systemd/libudev1@249.11-0ubuntu3.6 - - +
  • - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - libfido2/libfido2-1@1.10.0-1 - - systemd/libudev1@249.11-0ubuntu3.6 - - +
    + +

    MPL-2.0 license

    -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - util-linux@2.37.2-4ubuntu3 - - systemd/libudev1@249.11-0ubuntu3.6 - - +
    -
  • + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/gosimple/slug +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/gosimple/slug@v1.13.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest - - apt@2.4.8 + github.com/argoproj/argo-cd/v2@* - apt/libapt-pkg6.0@2.4.8 - - systemd/libudev1@249.11-0ubuntu3.6 + github.com/gosimple/slug@v1.13.1 @@ -634,51 +2400,41 @@

      Detailed paths


      -

      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream systemd package.

      -

      An off-by-one Error issue was discovered in Systemd in format_timespan() function of time-util.c. An attacker could supply specific values for time and accuracy that leads to buffer overrun in format_timespan(), leading to a Denial of Service.

      -

      Remediation

      -

      There is no fixed version for Ubuntu:22.04 systemd.

      -

      References

      - +

      MPL-2.0 license


    -
    -

    Integer Overflow or Wraparound

    +
    +

    CVE-2023-7008

    -
    - medium severity +
    + low severity

      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - krb5/libk5crypto3 + systemd/libsystemd0
    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and krb5/libk5crypto3@1.19.2-2 + docker-image|quay.io/argoproj/argocd@latest and systemd/libsystemd0@249.11-0ubuntu3.12
    @@ -693,7 +2449,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - krb5/libk5crypto3@1.19.2-2 + systemd/libsystemd0@249.11-0ubuntu3.12 @@ -702,19 +2458,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 + apt@2.4.11 - krb5/libk5crypto3@1.19.2-2 + systemd/libsystemd0@249.11-0ubuntu3.12 @@ -723,21 +2469,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 + procps/libprocps8@2:3.3.17-6ubuntu2.1 - krb5/libkrb5-3@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 + systemd/libsystemd0@249.11-0ubuntu3.12 @@ -746,7 +2480,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - krb5/libkrb5-3@1.19.2-2 + util-linux@2.37.2-4ubuntu3 + + systemd/libsystemd0@249.11-0ubuntu3.12 @@ -755,19 +2491,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 + util-linux/bsdutils@1:2.37.2-4ubuntu3 - krb5/libkrb5-3@1.19.2-2 + systemd/libsystemd0@249.11-0ubuntu3.12 @@ -776,18 +2502,11 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - krb5/libgssapi-krb5-2@1.19.2-2 - - - - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd@latest + apt@2.4.11 - openssh/openssh-client@1:8.9p1-3ubuntu0.1 + apt/libapt-pkg6.0@2.4.11 - krb5/libgssapi-krb5-2@1.19.2-2 + systemd/libsystemd0@249.11-0ubuntu3.12 @@ -796,11 +2515,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - krb5/libgssapi-krb5-2@1.19.2-2 + systemd/libudev1@249.11-0ubuntu3.12 @@ -809,13 +2524,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 + libfido2/libfido2-1@1.10.0-1 - krb5/libgssapi-krb5-2@1.19.2-2 + systemd/libudev1@249.11-0ubuntu3.12 @@ -824,17 +2535,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + util-linux@2.37.2-4ubuntu3 - krb5/libgssapi-krb5-2@1.19.2-2 + systemd/libudev1@249.11-0ubuntu3.12 @@ -843,9 +2546,11 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - meta-common-packages@meta + apt@2.4.11 - krb5/libkrb5support0@1.19.2-2 + apt/libapt-pkg6.0@2.4.11 + + systemd/libudev1@249.11-0ubuntu3.12 @@ -857,31 +2562,31 @@

    Detailed paths


    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream krb5 package.

    -

    PAC parsing in MIT Kerberos 5 (aka krb5) before 1.19.4 and 1.20.x before 1.20.1 has integer overflows that may lead to remote code execution (in KDC, kadmind, or a GSS or Kerberos application server) on 32-bit platforms (which have a resultant heap-based buffer overflow), and cause a denial of service on other platforms. This occurs in krb5_pac_parse in lib/krb5/krb/pac.c. Heimdal before 7.7.1 has "a similar bug."

    +

    Note: Versions mentioned in the description apply only to the upstream systemd package and not the systemd package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A vulnerability was found in systemd-resolved. This issue may allow systemd-resolved to accept records of DNSSEC-signed domains even when they have no signature, allowing man-in-the-middles (or the upstream DNS resolver) to manipulate records.

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    There is no fixed version for Ubuntu:22.04 systemd.

    References


  • -

    CVE-2022-46908

    +

    Arbitrary Code Injection

    @@ -891,19 +2596,22 @@

    CVE-2022-46908


      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - sqlite3/libsqlite3-0 + shadow/passwd
    • Introduced through: + docker-image|quay.io/argoproj/argocd@latest and shadow/passwd@1:4.8.1-2ubuntu2.2 - docker-image|quay.io/argoproj/argocd@latest, gnupg2/gpg@2.2.27-3ubuntu2.1 and others
    @@ -917,9 +2625,38 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - gnupg2/gpg@2.2.27-3ubuntu2.1 + shadow/passwd@1:4.8.1-2ubuntu2.2 + + + + +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest - sqlite3/libsqlite3-0@3.37.2-2ubuntu0.1 + shadow/login@1:4.8.1-2ubuntu2.2 @@ -931,22 +2668,24 @@

    Detailed paths


    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream sqlite3 package.

    -

    SQLite through 3.40.0, when relying on --safe for execution of an untrusted CLI script, does not properly implement the azProhibitedFunctions protection mechanism, and instead allows UDF functions such as WRITEFILE.

    +

    Note: Versions mentioned in the description apply only to the upstream shadow package and not the shadow package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    In Shadow 4.13, it is possible to inject control characters into fields provided to the SUID program chfn (change finger). Although it is not possible to exploit this directly (e.g., adding a new user fails because \n is in the block list), it is possible to misrepresent the /etc/passwd file when viewed. Use of \r manipulations and Unicode characters to work around blocking of the : character make it possible to give the impression that a new user has been added. In other words, an adversary may be able to convince a system administrator to take the system offline (an indirect, social-engineered denial of service) by demonstrating that "cat /etc/passwd" shows a rogue user account.

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 sqlite3.

    +

    There is no fixed version for Ubuntu:22.04 shadow.

    References


  • @@ -961,6 +2700,9 @@

    Uncontrolled Recursion


      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • Package Manager: ubuntu:22.04
    • @@ -1010,7 +2752,8 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream pcre3 package.

      +

      Note: Versions mentioned in the description apply only to the upstream pcre3 package and not the pcre3 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      In PCRE 8.41, the OP_KETRMAX feature in the match function in pcre_exec.c allows stack exhaustion (uncontrolled recursion) when processing a crafted regular expression.

      Remediation

      There is no fixed version for Ubuntu:22.04 pcre3.

      @@ -1022,6 +2765,9 @@

      References

    • MLIST
    • OSS security Advisory
    • Security Focus
    • +
    • cve@mitre.org
    • +
    • cve@mitre.org
    • +
    • cve@mitre.org

    @@ -1042,6 +2788,9 @@

    Release of Invalid Pointer or Reference


      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • Package Manager: ubuntu:22.04
    • @@ -1080,14 +2829,15 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream patch package.

      +

      Note: Versions mentioned in the description apply only to the upstream patch package and not the patch package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      An Invalid Pointer vulnerability exists in GNU patch 2.7 via the another_hunk function, which causes a Denial of Service.

      Remediation

      There is no fixed version for Ubuntu:22.04 patch.

      References


      @@ -1108,6 +2858,9 @@

      Double Free


        +
      • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
      • Package Manager: ubuntu:22.04
      • @@ -1146,7 +2899,8 @@

        Detailed paths


        NVD Description

        -

        Note: Versions mentioned in the description apply to the upstream patch package.

        +

        Note: Versions mentioned in the description apply only to the upstream patch package and not the patch package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

        A double free exists in the another_hunk function in pch.c in GNU patch through 2.7.6.

        Remediation

        There is no fixed version for Ubuntu:22.04 patch.

        @@ -1169,7 +2923,7 @@

        References

    -

    Improper Locking

    +

    CVE-2023-50495

    @@ -1179,18 +2933,21 @@

    Improper Locking


      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - openssl/libssl3 + ncurses/libtinfo6
    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and openssl/libssl3@3.0.2-0ubuntu1.7 + docker-image|quay.io/argoproj/argocd@latest and ncurses/libtinfo6@6.3-2ubuntu0.1
    @@ -1205,7 +2962,84 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - openssl/libssl3@3.0.2-0ubuntu1.7 + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + + +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + bash@5.1-6ubuntu1.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + less@590-1ubuntu0.22.04.2 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + libedit/libedit2@3.1-20210910-1build1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + ncurses/libncurses6@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + ncurses/ncurses-bin@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1214,9 +3048,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.1 + util-linux@2.37.2-4ubuntu3 - openssl/libssl3@3.0.2-0ubuntu1.7 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1225,9 +3059,13 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - libfido2/libfido2-1@1.10.0-1 + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + readline/libreadline8@8.1.2-1 - openssl/libssl3@3.0.2-0ubuntu1.7 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1236,9 +3074,13 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - openssh/openssh-client@1:8.9p1-3ubuntu0.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + pinentry/pinentry-curses@1.1.1-1build2 - openssl/libssl3@3.0.2-0ubuntu1.7 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1247,11 +3089,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - ca-certificates@20211016ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.7 - - openssl/libssl3@3.0.2-0ubuntu1.7 + ncurses/libncursesw6@6.3-2ubuntu0.1 @@ -1260,13 +3098,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 + procps@2:3.3.17-6ubuntu2.1 - libssh/libssh-4@0.9.6-2build1 - - openssl/libssl3@3.0.2-0ubuntu1.7 + ncurses/libncursesw6@6.3-2ubuntu0.1 @@ -1275,21 +3109,13 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - krb5/libgssapi-krb5-2@1.19.2-2 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - krb5/libkrb5-3@1.19.2-2 + pinentry/pinentry-curses@1.1.1-1build2 - openssl/libssl3@3.0.2-0ubuntu1.7 + ncurses/libncursesw6@6.3-2ubuntu0.1 @@ -1298,7 +3124,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - openssl@3.0.2-0ubuntu1.7 + ncurses/libncurses6@6.3-2ubuntu0.1 @@ -1307,152 +3133,27 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - ca-certificates@20211016ubuntu0.22.04.1 + procps@2:3.3.17-6ubuntu2.1 - openssl@3.0.2-0ubuntu1.7 + ncurses/libncurses6@6.3-2ubuntu0.1
  • - - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssl package.

    -

    If an X.509 certificate contains a malformed policy constraint and policy processing is enabled, then a write lock will be taken twice recursively. On some operating systems (most widely: Windows) this results in a denial of service when the affected process hangs. Policy processing being enabled on a publicly facing server is not considered to be a common setup. Policy processing is enabled by passing the -policy&#39; argument to the command line utilities or by calling either X509_VERIFY_PARAM_add0_policy()' or `X509_VERIFY_PARAM_set1_policies()' functions.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssl.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2021-41617

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssh/openssh-client -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@latest and openssh/openssh-client@1:8.9p1-3ubuntu0.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: docker-image|quay.io/argoproj/argocd@latest - openssh/openssh-client@1:8.9p1-3ubuntu0.1 + ncurses/ncurses-base@6.3-2ubuntu0.1
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssh package.

    -

    sshd in OpenSSH 6.2 through 8.x before 8.8, when certain non-default configurations are used, allows privilege escalation because supplemental groups are not initialized as expected. Helper programs for AuthorizedKeysCommand and AuthorizedPrincipalsCommand may run with privileges associated with group memberships of the sshd process, if the configuration specifies running the command as a different user.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssh.

    -

    References

    - - -
    - - - -
    -
    -

    Information Exposure

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssh/openssh-client -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@latest and openssh/openssh-client@1:8.9p1-3ubuntu0.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
    -

    Out-of-bounds Read

    +

    CVE-2023-45918

    @@ -1499,6 +3197,9 @@

    Out-of-bounds Read


      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • Package Manager: ubuntu:22.04
    • @@ -1510,7 +3211,7 @@

      Out-of-bounds Read

    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and ncurses/libtinfo6@6.3-2 + docker-image|quay.io/argoproj/argocd@latest and ncurses/libtinfo6@6.3-2ubuntu0.1
    @@ -1525,7 +3226,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - ncurses/libtinfo6@6.3-2 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1534,9 +3235,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - bash@5.1-6ubuntu1 + bash@5.1-6ubuntu1.1 - ncurses/libtinfo6@6.3-2 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1545,9 +3246,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - ncurses/libncursesw6@6.3-2 + ncurses/libncursesw6@6.3-2ubuntu0.1 - ncurses/libtinfo6@6.3-2 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1556,9 +3257,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - less@590-1build1 + less@590-1ubuntu0.22.04.2 - ncurses/libtinfo6@6.3-2 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1569,7 +3270,7 @@

    Detailed paths

    libedit/libedit2@3.1-20210910-1build1 - ncurses/libtinfo6@6.3-2 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1578,9 +3279,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - ncurses/libncurses6@6.3-2 + ncurses/libncurses6@6.3-2ubuntu0.1 - ncurses/libtinfo6@6.3-2 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1589,9 +3290,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - ncurses/ncurses-bin@6.3-2 + ncurses/ncurses-bin@6.3-2ubuntu0.1 - ncurses/libtinfo6@6.3-2 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1600,9 +3301,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - procps@2:3.3.17-6ubuntu2 + procps@2:3.3.17-6ubuntu2.1 - ncurses/libtinfo6@6.3-2 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1613,7 +3314,7 @@

    Detailed paths

    util-linux@2.37.2-4ubuntu3 - ncurses/libtinfo6@6.3-2 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1628,7 +3329,7 @@

    Detailed paths

    readline/libreadline8@8.1.2-1 - ncurses/libtinfo6@6.3-2 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1643,7 +3344,7 @@

    Detailed paths

    pinentry/pinentry-curses@1.1.1-1build2 - ncurses/libtinfo6@6.3-2 + ncurses/libtinfo6@6.3-2ubuntu0.1 @@ -1652,7 +3353,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - ncurses/libncursesw6@6.3-2 + ncurses/libncursesw6@6.3-2ubuntu0.1 @@ -1661,9 +3362,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - procps@2:3.3.17-6ubuntu2 + procps@2:3.3.17-6ubuntu2.1 - ncurses/libncursesw6@6.3-2 + ncurses/libncursesw6@6.3-2ubuntu0.1 @@ -1678,7 +3379,7 @@

    Detailed paths

    pinentry/pinentry-curses@1.1.1-1build2 - ncurses/libncursesw6@6.3-2 + ncurses/libncursesw6@6.3-2ubuntu0.1 @@ -1687,7 +3388,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - ncurses/libncurses6@6.3-2 + ncurses/libncurses6@6.3-2ubuntu0.1 @@ -1696,9 +3397,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - procps@2:3.3.17-6ubuntu2 + procps@2:3.3.17-6ubuntu2.1 - ncurses/libncurses6@6.3-2 + ncurses/libncurses6@6.3-2ubuntu0.1 @@ -1707,7 +3408,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - ncurses/ncurses-base@6.3-2 + ncurses/ncurses-base@6.3-2ubuntu0.1 @@ -1716,7 +3417,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - ncurses/ncurses-bin@6.3-2 + ncurses/ncurses-bin@6.3-2ubuntu0.1 @@ -1728,24 +3429,99 @@

    Detailed paths


    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream ncurses package.

    -

    ncurses 6.3 before patch 20220416 has an out-of-bounds read and segmentation violation in convert_strings in tinfo/read_entry.c in the terminfo library.

    +

    Note: Versions mentioned in the description apply only to the upstream ncurses package and not the ncurses package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    ncurses 6.4-20230610 has a NULL pointer dereference in tgetstr in tinfo/lib_termcap.c.

    Remediation

    There is no fixed version for Ubuntu:22.04 ncurses.

    References

    + +
    + + + +
    +
    +

    Resource Exhaustion

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + libzstd/libzstd1 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@latest and libzstd/libzstd1@1.4.8+dfsg-3build1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + libzstd/libzstd1@1.4.8+dfsg-3build1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream libzstd package and not the libzstd package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A vulnerability was found in zstd v1.4.10, where an attacker can supply empty string as an argument to the command line tool to cause buffer overrun.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 libzstd.

    +

    References

    +
    @@ -1760,6 +3536,9 @@

    Integer Overflow or Wraparound


      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • Package Manager: ubuntu:22.04
    • @@ -1771,7 +3550,7 @@

      Integer Overflow or Wraparound

    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and krb5/libk5crypto3@1.19.2-2 + docker-image|quay.io/argoproj/argocd@latest and krb5/libk5crypto3@1.19.2-2ubuntu0.3
    @@ -1786,7 +3565,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - krb5/libk5crypto3@1.19.2-2 + krb5/libk5crypto3@1.19.2-2ubuntu0.3 @@ -1797,17 +3576,17 @@

    Detailed paths

    adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-2ubuntu2.1 + shadow/passwd@1:4.8.1-2ubuntu2.2 - pam/libpam-modules@1.4.0-11ubuntu2 + pam/libpam-modules@1.4.0-11ubuntu2.4 libnsl/libnsl2@1.3.0-2build2 libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libgssapi-krb5-2@1.19.2-2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - krb5/libk5crypto3@1.19.2-2 + krb5/libk5crypto3@1.19.2-2ubuntu0.3 @@ -1818,19 +3597,19 @@

    Detailed paths

    adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-2ubuntu2.1 + shadow/passwd@1:4.8.1-2ubuntu2.2 - pam/libpam-modules@1.4.0-11ubuntu2 + pam/libpam-modules@1.4.0-11ubuntu2.4 libnsl/libnsl2@1.3.0-2build2 libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libgssapi-krb5-2@1.19.2-2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - krb5/libkrb5-3@1.19.2-2 + krb5/libkrb5-3@1.19.2-2ubuntu0.3 - krb5/libk5crypto3@1.19.2-2 + krb5/libk5crypto3@1.19.2-2ubuntu0.3 @@ -1839,7 +3618,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - krb5/libkrb5-3@1.19.2-2 + krb5/libkrb5-3@1.19.2-2ubuntu0.3 @@ -1850,17 +3629,17 @@

    Detailed paths

    adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-2ubuntu2.1 + shadow/passwd@1:4.8.1-2ubuntu2.2 - pam/libpam-modules@1.4.0-11ubuntu2 + pam/libpam-modules@1.4.0-11ubuntu2.4 libnsl/libnsl2@1.3.0-2build2 libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libgssapi-krb5-2@1.19.2-2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - krb5/libkrb5-3@1.19.2-2 + krb5/libkrb5-3@1.19.2-2ubuntu0.3 @@ -1869,7 +3648,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - krb5/libgssapi-krb5-2@1.19.2-2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 @@ -1878,9 +3657,9 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - openssh/openssh-client@1:8.9p1-3ubuntu0.1 + openssh/openssh-client@1:8.9p1-3ubuntu0.6 - krb5/libgssapi-krb5-2@1.19.2-2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 @@ -1889,11 +3668,11 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.34.1-1ubuntu1.6 + git@1:2.34.1-1ubuntu1.10 - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - krb5/libgssapi-krb5-2@1.19.2-2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 @@ -1902,13 +3681,13 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.34.1-1ubuntu1.6 + git@1:2.34.1-1ubuntu1.10 - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - libssh/libssh-4@0.9.6-2build1 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - krb5/libgssapi-krb5-2@1.19.2-2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 @@ -1919,15 +3698,15 @@

    Detailed paths

    adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-2ubuntu2.1 + shadow/passwd@1:4.8.1-2ubuntu2.2 - pam/libpam-modules@1.4.0-11ubuntu2 + pam/libpam-modules@1.4.0-11ubuntu2.4 libnsl/libnsl2@1.3.0-2build2 libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libgssapi-krb5-2@1.19.2-2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 @@ -1936,9 +3715,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - meta-common-packages@meta - - krb5/libkrb5support0@1.19.2-2 + krb5/libkrb5support0@1.19.2-2ubuntu0.3 @@ -1950,17 +3727,19 @@

    Detailed paths


    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream krb5 package.

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    An issue was discovered in MIT Kerberos 5 (aka krb5) through 1.16. There is a variable "dbentry->n_key_data" in kadmin/dbutil/dump.c that can store 16-bit data but unknowingly the developer has assigned a "u4" variable to it, which is for 32-bit data. An attacker can use this vulnerability to affect other artifacts of the database as we know that a Kerberos database dump file contains trusted data.

    Remediation

    There is no fixed version for Ubuntu:22.04 krb5.

    References


    @@ -1971,7 +3750,7 @@

    References

    -

    CVE-2022-3219

    +

    Out-of-bounds Write

    @@ -1981,6 +3760,9 @@

    CVE-2022-3219


      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • Package Manager: ubuntu:22.04
    • @@ -2016,7 +3798,7 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - apt@2.4.8 + apt@2.4.11 gnupg2/gpgv@2.2.27-3ubuntu2.1 @@ -2323,12 +4105,20 @@

      Detailed paths


      NVD Description

      -

      This vulnerability has not been analyzed by NVD yet.

      +

      Note: Versions mentioned in the description apply only to the upstream gnupg2 package and not the gnupg2 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      GnuPG can be made to spin on a relatively small input by (for example) crafting a public key with thousands of signatures attached, compressed down to just a few KB.

      Remediation

      There is no fixed version for Ubuntu:22.04 gnupg2.

      References


      @@ -2349,6 +4139,9 @@

      Allocation of Resources Without Limits or Throttling

        +
      • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
      • Package Manager: ubuntu:22.04
      • @@ -2360,7 +4153,7 @@

        Allocation of Resources Without Limits or Throttling

        Introduced through: - docker-image|quay.io/argoproj/argocd@latest and glibc/libc-bin@2.35-0ubuntu3.1 + docker-image|quay.io/argoproj/argocd@latest and glibc/libc-bin@2.35-0ubuntu3.6
      @@ -2375,7 +4168,7 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - glibc/libc-bin@2.35-0ubuntu3.1 + glibc/libc-bin@2.35-0ubuntu3.6 @@ -2384,9 +4177,7 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - meta-common-packages@meta - - glibc/libc6@2.35-0ubuntu3.1 + glibc/libc6@2.35-0ubuntu3.6 @@ -2398,16 +4189,17 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream glibc package.

      +

      Note: Versions mentioned in the description apply only to the upstream glibc package and not the glibc package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      sha256crypt and sha512crypt through 0.6 allow attackers to cause a denial of service (CPU consumption) because the algorithm's runtime is proportional to the square of the length of the password.

      Remediation

      There is no fixed version for Ubuntu:22.04 glibc.

      References


      @@ -2428,6 +4220,9 @@

      Improper Input Validation


        +
      • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
      • Package Manager: ubuntu:22.04
      • @@ -2440,7 +4235,7 @@

        Improper Input Validation

      • Introduced through: - docker-image|quay.io/argoproj/argocd@latest, git@1:2.34.1-1ubuntu1.6 and others + docker-image|quay.io/argoproj/argocd@latest, git@1:2.34.1-1ubuntu1.10 and others
      @@ -2454,9 +4249,9 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.34.1-1ubuntu1.6 + git@1:2.34.1-1ubuntu1.10 - git/git-man@1:2.34.1-1ubuntu1.6 + git/git-man@1:2.34.1-1ubuntu1.10 @@ -2465,7 +4260,7 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git@1:2.34.1-1ubuntu1.6 + git@1:2.34.1-1ubuntu1.10 @@ -2474,9 +4269,9 @@

      Detailed paths

      Introduced through: docker-image|quay.io/argoproj/argocd@latest - git-lfs@3.0.2-1 + git-lfs@3.0.2-1ubuntu0.2 - git@1:2.34.1-1ubuntu1.6 + git@1:2.34.1-1ubuntu1.10 @@ -2488,7 +4283,8 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream git package.

      +

      Note: Versions mentioned in the description apply only to the upstream git package and not the git package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      GIT version 2.15.1 and earlier contains a Input Validation Error vulnerability in Client that can result in problems including messing up terminal configuration to RCE. This attack appear to be exploitable via The user must interact with a malicious git server, (or have their traffic modified in a MITM attack).

      Remediation

      There is no fixed version for Ubuntu:22.04 git.

      @@ -2507,7 +4303,7 @@

      References

    -

    Improper Input Validation

    +

    Uncontrolled Recursion

    @@ -2517,18 +4313,21 @@

    Improper Input Validation


      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - coreutils + gcc-12/libstdc++6
    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and coreutils@8.32-4.1ubuntu1 + docker-image|quay.io/argoproj/argocd@latest and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04
    @@ -2543,7 +4342,49 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - coreutils@8.32-4.1ubuntu1 + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + + +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + apt@2.4.11 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gcc-12/gcc-12-base@12.3.0-1ubuntu1~22.04 + + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@latest + + gcc-12/libgcc-s1@12.3.0-1ubuntu1~22.04 @@ -2555,28 +4396,29 @@

    Detailed paths


    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream coreutils package.

    -

    chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.

    +

    Note: Versions mentioned in the description apply only to the upstream gcc-12 package and not the gcc-12 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    libiberty/rust-demangle.c in GNU GCC 11.2 allows stack consumption in demangle_const, as demonstrated by nm-new.

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 coreutils.

    +

    There is no fixed version for Ubuntu:22.04 gcc-12.

    References


  • -

    Out-of-bounds Write

    +

    Improper Input Validation

    @@ -2586,18 +4428,21 @@

    Out-of-bounds Write


      +
    • + Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd Dockerfile +
    • Package Manager: ubuntu:22.04
    • Vulnerable module: - bash + coreutils
    • Introduced through: - docker-image|quay.io/argoproj/argocd@latest and bash@5.1-6ubuntu1 + docker-image|quay.io/argoproj/argocd@latest and coreutils@8.32-4.1ubuntu1.1
    @@ -2612,7 +4457,7 @@

    Detailed paths

    Introduced through: docker-image|quay.io/argoproj/argocd@latest - bash@5.1-6ubuntu1 + coreutils@8.32-4.1ubuntu1.1 @@ -2624,20 +4469,25 @@

    Detailed paths


    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream bash package.

    -

    A flaw was found in the bash package, where a heap-buffer overflow can occur in valid parameter_transform. This issue may lead to memory problems.

    +

    Note: Versions mentioned in the description apply only to the upstream coreutils package and not the coreutils package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 bash.

    +

    There is no fixed version for Ubuntu:22.04 coreutils.

    References


    diff --git a/docs/snyk/master/redis_7.0.14-alpine.html b/docs/snyk/master/redis_7.0.14-alpine.html new file mode 100644 index 0000000000000..f47d4fe717527 --- /dev/null +++ b/docs/snyk/master/redis_7.0.14-alpine.html @@ -0,0 +1,993 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    March 24th 2024, 12:15:59 am (UTC+00:00)

    +
    +
    + Scanned the following paths: +
      +
    • redis:7.0.14-alpine (apk)
    • +
    • redis:7.0.14-alpine/tianon/gosu//usr/local/bin/gosu (gomodules)
    • +
    +
    + +
    +
    3 known vulnerabilities
    +
    27 vulnerable dependency paths
    +
    19 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.19 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.14-alpine and openssl/libcrypto3@3.1.4-r2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + busybox/ssl_client@1.36.1-r15 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libssl3@3.1.4-r2 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + busybox/ssl_client@1.36.1-r15 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.19 relevant fixed versions and status.

    +

    Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

    +

    Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

    +

    The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

    +

    The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

    +

    The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

    +

    Remediation

    +

    Upgrade Alpine:3.19 openssl to version 3.1.4-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-0727

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.19 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.14-alpine and openssl/libcrypto3@3.1.4-r2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + busybox/ssl_client@1.36.1-r15 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libssl3@3.1.4-r2 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + busybox/ssl_client@1.36.1-r15 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.19 relevant fixed versions and status.

    +

    Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

    +

    Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

    +

    A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

    +

    OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

    +

    We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

    +

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.19 openssl to version 3.1.4-r5 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-6237

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.19 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.14-alpine and openssl/libcrypto3@3.1.4-r2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + busybox/ssl_client@1.36.1-r15 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libssl3@3.1.4-r2 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + busybox/ssl_client@1.36.1-r15 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    This vulnerability has not been analyzed by NVD yet.

    +

    Remediation

    +

    Upgrade Alpine:3.19 openssl to version 3.1.4-r4 or higher.

    + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/master/redis_7.0.7-alpine.html b/docs/snyk/master/redis_7.0.7-alpine.html deleted file mode 100644 index 0cbbce3dd9b09..0000000000000 --- a/docs/snyk/master/redis_7.0.7-alpine.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:16:03 am

    -
    -
    - Scanned the following path: -
      -
    • redis:7.0.7-alpine (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    18 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|redis
    Path redis:7.0.7-alpine
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.3.13/argocd-iac-install.html b/docs/snyk/v2.3.13/argocd-iac-install.html deleted file mode 100644 index 91bfb11773e3b..0000000000000 --- a/docs/snyk/v2.3.13/argocd-iac-install.html +++ /dev/null @@ -1,3251 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:24:51 am

    -
    -
    - Scanned the following path: -
      -
    • /argo-cd/manifests/install.yaml (Kubernetes)
    • -
    -
    - -
    -
    50 total issues
    -
    -
    -
    -
    - -
    - - - - - - -
    Project manifests/install.yaml
    Path /argo-cd/manifests/install.yaml
    Project Type Kubernetes
    -
    -
    -
    -

    Container does not drop all default capabilities

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-6 -
    • - -
    • Introduced through: - [DocId: 41] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - securityContext - - capabilities - - drop - -
    • - -
    • - Line number: 9800 -
    • -
    - -
    - -

    Impact

    -

    Containers are running with potentially unnecessary privileges

    - -

    Remediation

    -

    Add `ALL` to `securityContext.capabilities.drop` list, and add only required capabilities in `securityContext.capabilities.add`

    - - -
    -
    - - - -
    -
    -

    Container does not drop all default capabilities

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-6 -
    • - -
    • Introduced through: - [DocId: 42] - - input - - spec - - template - - spec - - containers[dex] - - securityContext - - capabilities - - drop - -
    • - -
    • - Line number: 9874 -
    • -
    - -
    - -

    Impact

    -

    Containers are running with potentially unnecessary privileges

    - -

    Remediation

    -

    Add `ALL` to `securityContext.capabilities.drop` list, and add only required capabilities in `securityContext.capabilities.add`

    - - -
    -
    - - - -
    -
    -

    Container does not drop all default capabilities

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-6 -
    • - -
    • Introduced through: - [DocId: 42] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - capabilities - - drop - -
    • - -
    • - Line number: 9884 -
    • -
    - -
    - -

    Impact

    -

    Containers are running with potentially unnecessary privileges

    - -

    Remediation

    -

    Add `ALL` to `securityContext.capabilities.drop` list, and add only required capabilities in `securityContext.capabilities.add`

    - - -
    -
    - - - -
    -
    -

    Container does not drop all default capabilities

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-6 -
    • - -
    • Introduced through: - [DocId: 43] - - input - - spec - - template - - spec - - containers[argocd-notifications-controller] - - securityContext - - capabilities - - drop - -
    • - -
    • - Line number: 9920 -
    • -
    - -
    - -

    Impact

    -

    Containers are running with potentially unnecessary privileges

    - -

    Remediation

    -

    Add `ALL` to `securityContext.capabilities.drop` list, and add only required capabilities in `securityContext.capabilities.add`

    - - -
    -
    - - - -
    -
    -

    Container does not drop all default capabilities

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-6 -
    • - -
    • Introduced through: - [DocId: 44] - - input - - spec - - template - - spec - - containers[redis] - - securityContext - - capabilities - - drop - -
    • - -
    • - Line number: 9986 -
    • -
    - -
    - -

    Impact

    -

    Containers are running with potentially unnecessary privileges

    - -

    Remediation

    -

    Add `ALL` to `securityContext.capabilities.drop` list, and add only required capabilities in `securityContext.capabilities.add`

    - - -
    -
    - - - -
    -
    -

    Container does not drop all default capabilities

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-6 -
    • - -
    • Introduced through: - [DocId: 45] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - capabilities - - drop - -
    • - -
    • - Line number: 10169 -
    • -
    - -
    - -

    Impact

    -

    Containers are running with potentially unnecessary privileges

    - -

    Remediation

    -

    Add `ALL` to `securityContext.capabilities.drop` list, and add only required capabilities in `securityContext.capabilities.add`

    - - -
    -
    - - - -
    -
    -

    Container is running without privilege escalation control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-9 -
    • - -
    • Introduced through: - [DocId: 41] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - securityContext - - allowPrivilegeEscalation - -
    • - -
    • - Line number: 9800 -
    • -
    - -
    - -

    Impact

    -

    Processes could elevate current privileges via known vectors, for example SUID binaries

    - -

    Remediation

    -

    Set `securityContext.allowPrivilegeEscalation` to `false`

    - - -
    -
    - - - -
    -
    -

    Container is running without privilege escalation control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-9 -
    • - -
    • Introduced through: - [DocId: 42] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - allowPrivilegeEscalation - -
    • - -
    • - Line number: 9884 -
    • -
    - -
    - -

    Impact

    -

    Processes could elevate current privileges via known vectors, for example SUID binaries

    - -

    Remediation

    -

    Set `securityContext.allowPrivilegeEscalation` to `false`

    - - -
    -
    - - - -
    -
    -

    Container is running without privilege escalation control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-9 -
    • - -
    • Introduced through: - [DocId: 43] - - input - - spec - - template - - spec - - containers[argocd-notifications-controller] - - securityContext - - allowPrivilegeEscalation - -
    • - -
    • - Line number: 9920 -
    • -
    - -
    - -

    Impact

    -

    Processes could elevate current privileges via known vectors, for example SUID binaries

    - -

    Remediation

    -

    Set `securityContext.allowPrivilegeEscalation` to `false`

    - - -
    -
    - - - -
    -
    -

    Container is running without privilege escalation control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-9 -
    • - -
    • Introduced through: - [DocId: 44] - - input - - spec - - template - - spec - - containers[redis] - - securityContext - - allowPrivilegeEscalation - -
    • - -
    • - Line number: 9986 -
    • -
    - -
    - -

    Impact

    -

    Processes could elevate current privileges via known vectors, for example SUID binaries

    - -

    Remediation

    -

    Set `securityContext.allowPrivilegeEscalation` to `false`

    - - -
    -
    - - - -
    -
    -

    Container is running without privilege escalation control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-9 -
    • - -
    • Introduced through: - [DocId: 45] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - allowPrivilegeEscalation - -
    • - -
    • - Line number: 10169 -
    • -
    - -
    - -

    Impact

    -

    Processes could elevate current privileges via known vectors, for example SUID binaries

    - -

    Remediation

    -

    Set `securityContext.allowPrivilegeEscalation` to `false`

    - - -
    -
    - - - -
    -
    -

    Container is running without root user control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-10 -
    • - -
    • Introduced through: - [DocId: 41] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - securityContext - - runAsNonRoot - -
    • - -
    • - Line number: 9800 -
    • -
    - -
    - -

    Impact

    -

    Container could be running with full administrative privileges

    - -

    Remediation

    -

    Set `securityContext.runAsNonRoot` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running without root user control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-10 -
    • - -
    • Introduced through: - [DocId: 42] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - runAsNonRoot - -
    • - -
    • - Line number: 9884 -
    • -
    - -
    - -

    Impact

    -

    Container could be running with full administrative privileges

    - -

    Remediation

    -

    Set `securityContext.runAsNonRoot` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running without root user control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-10 -
    • - -
    • Introduced through: - [DocId: 45] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - runAsNonRoot - -
    • - -
    • - Line number: 10169 -
    • -
    - -
    - -

    Impact

    -

    Container could be running with full administrative privileges

    - -

    Remediation

    -

    Set `securityContext.runAsNonRoot` to `true`

    - - -
    -
    - - - -
    -
    -

    Role with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 9] - - rules[0] - - resources - -
    • - -
    • - Line number: 9184 -
    • -
    - -
    - -

    Impact

    -

    Using this role grants dangerous permissions

    - -

    Remediation

    -

    Consider removing this permissions

    - - -
    -
    - - - -
    -
    -

    Role with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 10] - - rules[3] - - resources - -
    • - -
    • - Line number: 9256 -
    • -
    - -
    - -

    Impact

    -

    Using this role grants dangerous permissions

    - -

    Remediation

    -

    Consider removing this permissions

    - - -
    -
    - - - -
    -
    -

    Role with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 11] - - rules[0] - - resources - -
    • - -
    • - Line number: 9284 -
    • -
    - -
    - -

    Impact

    -

    Using this role grants dangerous permissions

    - -

    Remediation

    -

    Consider removing this permissions

    - - -
    -
    - - - -
    -
    -

    Role with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 12] - - rules[3] - - resources - -
    • - -
    • - Line number: 9328 -
    • -
    - -
    - -

    Impact

    -

    Using this role grants dangerous permissions

    - -

    Remediation

    -

    Consider removing this permissions

    - - -
    -
    - - - -
    -
    -

    Role with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 12] - - rules[1] - - resources - -
    • - -
    • - Line number: 9310 -
    • -
    - -
    - -

    Impact

    -

    Using this role grants dangerous permissions

    - -

    Remediation

    -

    Consider removing this permissions

    - - -
    -
    - - - -
    -
    -

    Role with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 13] - - rules[0] - - resources - -
    • - -
    • - Line number: 9344 -
    • -
    - -
    - -

    Impact

    -

    Using this role grants dangerous permissions

    - -

    Remediation

    -

    Consider removing this permissions

    - - -
    -
    - - - -
    -
    -

    Container could be running with outdated image

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-42 -
    • - -
    • Introduced through: - [DocId: 45] - - spec - - template - - spec - - initContainers[copyutil] - - imagePullPolicy - -
    • - -
    • - Line number: 10169 -
    • -
    - -
    - -

    Impact

    -

    The container may run with outdated or unauthorized image

    - -

    Remediation

    -

    Set `imagePullPolicy` attribute to `Always`

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 41] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 9800 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 42] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 9884 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 42] - - input - - spec - - template - - spec - - containers[dex] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 9864 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 43] - - input - - spec - - template - - spec - - containers[argocd-notifications-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 9920 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 44] - - input - - spec - - template - - spec - - containers[redis] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 9986 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 45] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 10169 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 45] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 10035 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 46] - - input - - spec - - template - - spec - - containers[argocd-server] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 10244 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 47] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 10494 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container is running with multiple open ports

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-36 -
    • - -
    • Introduced through: - [DocId: 42] - - spec - - template - - spec - - containers[dex] - - ports - -
    • - -
    • - Line number: 9871 -
    • -
    - -
    - -

    Impact

    -

    Increases the attack surface of the application and the container.

    - -

    Remediation

    -

    Reduce `ports` count to 2

    - - -
    -
    - - - -
    -
    -

    Container is running with writable root filesystem

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-8 -
    • - -
    • Introduced through: - [DocId: 41] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - securityContext - - readOnlyRootFilesystem - -
    • - -
    • - Line number: 9800 -
    • -
    - -
    - -

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    - -

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running with writable root filesystem

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-8 -
    • - -
    • Introduced through: - [DocId: 42] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - readOnlyRootFilesystem - -
    • - -
    • - Line number: 9884 -
    • -
    - -
    - -

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    - -

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running with writable root filesystem

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-8 -
    • - -
    • Introduced through: - [DocId: 43] - - input - - spec - - template - - spec - - containers[argocd-notifications-controller] - - securityContext - - readOnlyRootFilesystem - -
    • - -
    • - Line number: 9920 -
    • -
    - -
    - -

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    - -

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running with writable root filesystem

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-8 -
    • - -
    • Introduced through: - [DocId: 44] - - input - - spec - - template - - spec - - containers[redis] - - securityContext - - readOnlyRootFilesystem - -
    • - -
    • - Line number: 9986 -
    • -
    - -
    - -

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    - -

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running with writable root filesystem

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-8 -
    • - -
    • Introduced through: - [DocId: 45] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - readOnlyRootFilesystem - -
    • - -
    • - Line number: 10169 -
    • -
    - -
    - -

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    - -

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-41 -
    • - -
    • Introduced through: - [DocId: 41] - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - livenessProbe - -
    • - -
    • - Line number: 9800 -
    • -
    - -
    - -

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    - -

    Remediation

    -

    Add `livenessProbe` attribute

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-41 -
    • - -
    • Introduced through: - [DocId: 42] - - spec - - template - - spec - - containers[dex] - - livenessProbe - -
    • - -
    • - Line number: 9864 -
    • -
    - -
    - -

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    - -

    Remediation

    -

    Add `livenessProbe` attribute

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-41 -
    • - -
    • Introduced through: - [DocId: 42] - - spec - - template - - spec - - initContainers[copyutil] - - livenessProbe - -
    • - -
    • - Line number: 9884 -
    • -
    - -
    - -

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    - -

    Remediation

    -

    Add `livenessProbe` attribute

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-41 -
    • - -
    • Introduced through: - [DocId: 44] - - spec - - template - - spec - - containers[redis] - - livenessProbe - -
    • - -
    • - Line number: 9986 -
    • -
    - -
    - -

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    - -

    Remediation

    -

    Add `livenessProbe` attribute

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-41 -
    • - -
    • Introduced through: - [DocId: 45] - - spec - - template - - spec - - initContainers[copyutil] - - livenessProbe - -
    • - -
    • - Line number: 10169 -
    • -
    - -
    - -

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    - -

    Remediation

    -

    Add `livenessProbe` attribute

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 41] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 9800 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 42] - - input - - spec - - template - - spec - - containers[dex] - - resources - - limits - - memory - -
    • - -
    • - Line number: 9864 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 42] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - memory - -
    • - -
    • - Line number: 9884 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 43] - - input - - spec - - template - - spec - - containers[argocd-notifications-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 9920 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 44] - - input - - spec - - template - - spec - - containers[redis] - - resources - - limits - - memory - -
    • - -
    • - Line number: 9986 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 45] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - memory - -
    • - -
    • - Line number: 10169 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 45] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - resources - - limits - - memory - -
    • - -
    • - Line number: 10035 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 46] - - input - - spec - - template - - spec - - containers[argocd-server] - - resources - - limits - - memory - -
    • - -
    • - Line number: 10244 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 47] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 10494 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -
    - -
    - - - diff --git a/docs/snyk/v2.3.13/argocd-iac-namespace-install.html b/docs/snyk/v2.3.13/argocd-iac-namespace-install.html deleted file mode 100644 index b67dcb0a6e068..0000000000000 --- a/docs/snyk/v2.3.13/argocd-iac-namespace-install.html +++ /dev/null @@ -1,3251 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:25:20 am

    -
    -
    - Scanned the following path: -
      -
    • /argo-cd/manifests/namespace-install.yaml (Kubernetes)
    • -
    -
    - -
    -
    50 total issues
    -
    -
    -
    -
    - -
    - - - - - - -
    Project manifests/namespace-install.yaml
    Path /argo-cd/manifests/namespace-install.yaml
    Project Type Kubernetes
    -
    -
    -
    -

    Container does not drop all default capabilities

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-6 -
    • - -
    • Introduced through: - [DocId: 35] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - securityContext - - capabilities - - drop - -
    • - -
    • - Line number: 7096 -
    • -
    - -
    - -

    Impact

    -

    Containers are running with potentially unnecessary privileges

    - -

    Remediation

    -

    Add `ALL` to `securityContext.capabilities.drop` list, and add only required capabilities in `securityContext.capabilities.add`

    - - -
    -
    - - - -
    -
    -

    Container does not drop all default capabilities

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-6 -
    • - -
    • Introduced through: - [DocId: 36] - - input - - spec - - template - - spec - - containers[dex] - - securityContext - - capabilities - - drop - -
    • - -
    • - Line number: 7170 -
    • -
    - -
    - -

    Impact

    -

    Containers are running with potentially unnecessary privileges

    - -

    Remediation

    -

    Add `ALL` to `securityContext.capabilities.drop` list, and add only required capabilities in `securityContext.capabilities.add`

    - - -
    -
    - - - -
    -
    -

    Container does not drop all default capabilities

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-6 -
    • - -
    • Introduced through: - [DocId: 36] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - capabilities - - drop - -
    • - -
    • - Line number: 7180 -
    • -
    - -
    - -

    Impact

    -

    Containers are running with potentially unnecessary privileges

    - -

    Remediation

    -

    Add `ALL` to `securityContext.capabilities.drop` list, and add only required capabilities in `securityContext.capabilities.add`

    - - -
    -
    - - - -
    -
    -

    Container does not drop all default capabilities

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-6 -
    • - -
    • Introduced through: - [DocId: 37] - - input - - spec - - template - - spec - - containers[argocd-notifications-controller] - - securityContext - - capabilities - - drop - -
    • - -
    • - Line number: 7216 -
    • -
    - -
    - -

    Impact

    -

    Containers are running with potentially unnecessary privileges

    - -

    Remediation

    -

    Add `ALL` to `securityContext.capabilities.drop` list, and add only required capabilities in `securityContext.capabilities.add`

    - - -
    -
    - - - -
    -
    -

    Container does not drop all default capabilities

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-6 -
    • - -
    • Introduced through: - [DocId: 38] - - input - - spec - - template - - spec - - containers[redis] - - securityContext - - capabilities - - drop - -
    • - -
    • - Line number: 7282 -
    • -
    - -
    - -

    Impact

    -

    Containers are running with potentially unnecessary privileges

    - -

    Remediation

    -

    Add `ALL` to `securityContext.capabilities.drop` list, and add only required capabilities in `securityContext.capabilities.add`

    - - -
    -
    - - - -
    -
    -

    Container does not drop all default capabilities

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-6 -
    • - -
    • Introduced through: - [DocId: 39] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - capabilities - - drop - -
    • - -
    • - Line number: 7465 -
    • -
    - -
    - -

    Impact

    -

    Containers are running with potentially unnecessary privileges

    - -

    Remediation

    -

    Add `ALL` to `securityContext.capabilities.drop` list, and add only required capabilities in `securityContext.capabilities.add`

    - - -
    -
    - - - -
    -
    -

    Container is running without privilege escalation control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-9 -
    • - -
    • Introduced through: - [DocId: 35] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - securityContext - - allowPrivilegeEscalation - -
    • - -
    • - Line number: 7096 -
    • -
    - -
    - -

    Impact

    -

    Processes could elevate current privileges via known vectors, for example SUID binaries

    - -

    Remediation

    -

    Set `securityContext.allowPrivilegeEscalation` to `false`

    - - -
    -
    - - - -
    -
    -

    Container is running without privilege escalation control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-9 -
    • - -
    • Introduced through: - [DocId: 36] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - allowPrivilegeEscalation - -
    • - -
    • - Line number: 7180 -
    • -
    - -
    - -

    Impact

    -

    Processes could elevate current privileges via known vectors, for example SUID binaries

    - -

    Remediation

    -

    Set `securityContext.allowPrivilegeEscalation` to `false`

    - - -
    -
    - - - -
    -
    -

    Container is running without privilege escalation control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-9 -
    • - -
    • Introduced through: - [DocId: 37] - - input - - spec - - template - - spec - - containers[argocd-notifications-controller] - - securityContext - - allowPrivilegeEscalation - -
    • - -
    • - Line number: 7216 -
    • -
    - -
    - -

    Impact

    -

    Processes could elevate current privileges via known vectors, for example SUID binaries

    - -

    Remediation

    -

    Set `securityContext.allowPrivilegeEscalation` to `false`

    - - -
    -
    - - - -
    -
    -

    Container is running without privilege escalation control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-9 -
    • - -
    • Introduced through: - [DocId: 38] - - input - - spec - - template - - spec - - containers[redis] - - securityContext - - allowPrivilegeEscalation - -
    • - -
    • - Line number: 7282 -
    • -
    - -
    - -

    Impact

    -

    Processes could elevate current privileges via known vectors, for example SUID binaries

    - -

    Remediation

    -

    Set `securityContext.allowPrivilegeEscalation` to `false`

    - - -
    -
    - - - -
    -
    -

    Container is running without privilege escalation control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-9 -
    • - -
    • Introduced through: - [DocId: 39] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - allowPrivilegeEscalation - -
    • - -
    • - Line number: 7465 -
    • -
    - -
    - -

    Impact

    -

    Processes could elevate current privileges via known vectors, for example SUID binaries

    - -

    Remediation

    -

    Set `securityContext.allowPrivilegeEscalation` to `false`

    - - -
    -
    - - - -
    -
    -

    Container is running without root user control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-10 -
    • - -
    • Introduced through: - [DocId: 35] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - securityContext - - runAsNonRoot - -
    • - -
    • - Line number: 7096 -
    • -
    - -
    - -

    Impact

    -

    Container could be running with full administrative privileges

    - -

    Remediation

    -

    Set `securityContext.runAsNonRoot` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running without root user control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-10 -
    • - -
    • Introduced through: - [DocId: 36] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - runAsNonRoot - -
    • - -
    • - Line number: 7180 -
    • -
    - -
    - -

    Impact

    -

    Container could be running with full administrative privileges

    - -

    Remediation

    -

    Set `securityContext.runAsNonRoot` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running without root user control

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-10 -
    • - -
    • Introduced through: - [DocId: 39] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - runAsNonRoot - -
    • - -
    • - Line number: 7465 -
    • -
    - -
    - -

    Impact

    -

    Container could be running with full administrative privileges

    - -

    Remediation

    -

    Set `securityContext.runAsNonRoot` to `true`

    - - -
    -
    - - - -
    -
    -

    Role with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 7] - - rules[0] - - resources - -
    • - -
    • - Line number: 6565 -
    • -
    - -
    - -

    Impact

    -

    Using this role grants dangerous permissions

    - -

    Remediation

    -

    Consider removing this permissions

    - - -
    -
    - - - -
    -
    -

    Role with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 8] - - rules[3] - - resources - -
    • - -
    • - Line number: 6637 -
    • -
    - -
    - -

    Impact

    -

    Using this role grants dangerous permissions

    - -

    Remediation

    -

    Consider removing this permissions

    - - -
    -
    - - - -
    -
    -

    Role with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 9] - - rules[0] - - resources - -
    • - -
    • - Line number: 6665 -
    • -
    - -
    - -

    Impact

    -

    Using this role grants dangerous permissions

    - -

    Remediation

    -

    Consider removing this permissions

    - - -
    -
    - - - -
    -
    -

    Role with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 10] - - rules[3] - - resources - -
    • - -
    • - Line number: 6709 -
    • -
    - -
    - -

    Impact

    -

    Using this role grants dangerous permissions

    - -

    Remediation

    -

    Consider removing this permissions

    - - -
    -
    - - - -
    -
    -

    Role with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 10] - - rules[1] - - resources - -
    • - -
    • - Line number: 6691 -
    • -
    - -
    - -

    Impact

    -

    Using this role grants dangerous permissions

    - -

    Remediation

    -

    Consider removing this permissions

    - - -
    -
    - - - -
    -
    -

    Role with dangerous permissions

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-47 -
    • - -
    • Introduced through: - [DocId: 11] - - rules[0] - - resources - -
    • - -
    • - Line number: 6725 -
    • -
    - -
    - -

    Impact

    -

    Using this role grants dangerous permissions

    - -

    Remediation

    -

    Consider removing this permissions

    - - -
    -
    - - - -
    -
    -

    Container could be running with outdated image

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-42 -
    • - -
    • Introduced through: - [DocId: 39] - - spec - - template - - spec - - initContainers[copyutil] - - imagePullPolicy - -
    • - -
    • - Line number: 7465 -
    • -
    - -
    - -

    Impact

    -

    The container may run with outdated or unauthorized image

    - -

    Remediation

    -

    Set `imagePullPolicy` attribute to `Always`

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 35] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 7096 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 36] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 7180 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 36] - - input - - spec - - template - - spec - - containers[dex] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 7160 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 37] - - input - - spec - - template - - spec - - containers[argocd-notifications-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 7216 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 38] - - input - - spec - - template - - spec - - containers[redis] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 7282 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 39] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 7465 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 39] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 7331 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 40] - - input - - spec - - template - - spec - - containers[argocd-server] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 7540 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container has no CPU limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-5 -
    • - -
    • Introduced through: - [DocId: 41] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - resources - - limits - - cpu - -
    • - -
    • - Line number: 7790 -
    • -
    - -
    - -

    Impact

    -

    CPU limits can prevent containers from consuming valuable compute time for no benefit (e.g. inefficient code) that might lead to unnecessary costs. It is advisable to also configure CPU requests to ensure application stability.

    - -

    Remediation

    -

    Add `resources.limits.cpu` field with required CPU limit value

    - - -
    -
    - - - -
    -
    -

    Container is running with multiple open ports

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-36 -
    • - -
    • Introduced through: - [DocId: 36] - - spec - - template - - spec - - containers[dex] - - ports - -
    • - -
    • - Line number: 7167 -
    • -
    - -
    - -

    Impact

    -

    Increases the attack surface of the application and the container.

    - -

    Remediation

    -

    Reduce `ports` count to 2

    - - -
    -
    - - - -
    -
    -

    Container is running with writable root filesystem

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-8 -
    • - -
    • Introduced through: - [DocId: 35] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - securityContext - - readOnlyRootFilesystem - -
    • - -
    • - Line number: 7096 -
    • -
    - -
    - -

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    - -

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running with writable root filesystem

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-8 -
    • - -
    • Introduced through: - [DocId: 36] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - readOnlyRootFilesystem - -
    • - -
    • - Line number: 7180 -
    • -
    - -
    - -

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    - -

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running with writable root filesystem

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-8 -
    • - -
    • Introduced through: - [DocId: 37] - - input - - spec - - template - - spec - - containers[argocd-notifications-controller] - - securityContext - - readOnlyRootFilesystem - -
    • - -
    • - Line number: 7216 -
    • -
    - -
    - -

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    - -

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running with writable root filesystem

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-8 -
    • - -
    • Introduced through: - [DocId: 38] - - input - - spec - - template - - spec - - containers[redis] - - securityContext - - readOnlyRootFilesystem - -
    • - -
    • - Line number: 7282 -
    • -
    - -
    - -

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    - -

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running with writable root filesystem

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-8 -
    • - -
    • Introduced through: - [DocId: 39] - - input - - spec - - template - - spec - - initContainers[copyutil] - - securityContext - - readOnlyRootFilesystem - -
    • - -
    • - Line number: 7465 -
    • -
    - -
    - -

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    - -

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-41 -
    • - -
    • Introduced through: - [DocId: 35] - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - livenessProbe - -
    • - -
    • - Line number: 7096 -
    • -
    - -
    - -

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    - -

    Remediation

    -

    Add `livenessProbe` attribute

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-41 -
    • - -
    • Introduced through: - [DocId: 36] - - spec - - template - - spec - - containers[dex] - - livenessProbe - -
    • - -
    • - Line number: 7160 -
    • -
    - -
    - -

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    - -

    Remediation

    -

    Add `livenessProbe` attribute

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-41 -
    • - -
    • Introduced through: - [DocId: 36] - - spec - - template - - spec - - initContainers[copyutil] - - livenessProbe - -
    • - -
    • - Line number: 7180 -
    • -
    - -
    - -

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    - -

    Remediation

    -

    Add `livenessProbe` attribute

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-41 -
    • - -
    • Introduced through: - [DocId: 38] - - spec - - template - - spec - - containers[redis] - - livenessProbe - -
    • - -
    • - Line number: 7282 -
    • -
    - -
    - -

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    - -

    Remediation

    -

    Add `livenessProbe` attribute

    - - -
    -
    - - - -
    -
    -

    Container is running without liveness probe

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-41 -
    • - -
    • Introduced through: - [DocId: 39] - - spec - - template - - spec - - initContainers[copyutil] - - livenessProbe - -
    • - -
    • - Line number: 7465 -
    • -
    - -
    - -

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    - -

    Remediation

    -

    Add `livenessProbe` attribute

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 35] - - input - - spec - - template - - spec - - containers[argocd-applicationset-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 7096 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 36] - - input - - spec - - template - - spec - - containers[dex] - - resources - - limits - - memory - -
    • - -
    • - Line number: 7160 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 36] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - memory - -
    • - -
    • - Line number: 7180 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 37] - - input - - spec - - template - - spec - - containers[argocd-notifications-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 7216 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 38] - - input - - spec - - template - - spec - - containers[redis] - - resources - - limits - - memory - -
    • - -
    • - Line number: 7282 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 39] - - input - - spec - - template - - spec - - initContainers[copyutil] - - resources - - limits - - memory - -
    • - -
    • - Line number: 7465 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 39] - - input - - spec - - template - - spec - - containers[argocd-repo-server] - - resources - - limits - - memory - -
    • - -
    • - Line number: 7331 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 40] - - input - - spec - - template - - spec - - containers[argocd-server] - - resources - - limits - - memory - -
    • - -
    • - Line number: 7540 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -

    Container is running without memory limit

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Public ID: SNYK-CC-K8S-4 -
    • - -
    • Introduced through: - [DocId: 41] - - input - - spec - - template - - spec - - containers[argocd-application-controller] - - resources - - limits - - memory - -
    • - -
    • - Line number: 7790 -
    • -
    - -
    - -

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    - -

    Remediation

    -

    Set `resources.limits.memory` value

    - - -
    -
    - - - -
    -
    -
    - -
    - - - diff --git a/docs/snyk/v2.3.13/ghcr.io_dexidp_dex_v2.35.3.html b/docs/snyk/v2.3.13/ghcr.io_dexidp_dex_v2.35.3.html deleted file mode 100644 index 2793c5a1fffc5..0000000000000 --- a/docs/snyk/v2.3.13/ghcr.io_dexidp_dex_v2.35.3.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:23:18 am

    -
    -
    - Scanned the following path: -
      -
    • ghcr.io/dexidp/dex:v2.35.3/dexidp/dex (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    14 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|ghcr.io/dexidp/dex
    Path ghcr.io/dexidp/dex:v2.35.3/dexidp/dex
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.3.13/haproxy_2.0.29-alpine.html b/docs/snyk/v2.3.13/haproxy_2.0.29-alpine.html deleted file mode 100644 index dd8eef8394b4b..0000000000000 --- a/docs/snyk/v2.3.13/haproxy_2.0.29-alpine.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:23:21 am

    -
    -
    - Scanned the following path: -
      -
    • haproxy:2.0.29-alpine (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    17 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|haproxy
    Path haproxy:2.0.29-alpine
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.3.13/quay.io_argoproj_argocd_v2.3.13.html b/docs/snyk/v2.3.13/quay.io_argoproj_argocd_v2.3.13.html deleted file mode 100644 index 528814aa093d0..0000000000000 --- a/docs/snyk/v2.3.13/quay.io_argoproj_argocd_v2.3.13.html +++ /dev/null @@ -1,2649 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:23:59 am

    -
    -
    - Scanned the following path: -
      -
    • quay.io/argoproj/argocd:v2.3.13/argoproj/argocd (deb)
    • -
    -
    - -
    -
    16 known vulnerabilities
    -
    102 vulnerable dependency paths
    -
    162 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|quay.io/argoproj/argocd
    Path quay.io/argoproj/argocd:v2.3.13/argoproj/argocd
    Package Manager deb
    Manifest Dockerfile
    -
    -
    -
    -
    -

    Off-by-one Error

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - systemd/libsystemd0 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and systemd/libsystemd0@249.11-0ubuntu3.6 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - apt@2.4.8 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - procps/libprocps8@2:3.3.17-6ubuntu2 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - util-linux@2.37.2-4ubuntu3 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - util-linux/bsdutils@1:2.37.2-4ubuntu3 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - apt@2.4.8 - - apt/libapt-pkg6.0@2.4.8 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - libfido2/libfido2-1@1.10.0-1 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - util-linux@2.37.2-4ubuntu3 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - apt@2.4.8 - - apt/libapt-pkg6.0@2.4.8 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream systemd package.

    -

    An off-by-one Error issue was discovered in Systemd in format_timespan() function of time-util.c. An attacker could supply specific values for time and accuracy that leads to buffer overrun in format_timespan(), leading to a Denial of Service.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 systemd.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and krb5/libk5crypto3@1.19.2-2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - meta-common-packages@meta - - krb5/libkrb5support0@1.19.2-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream krb5 package.

    -

    PAC parsing in MIT Kerberos 5 (aka krb5) before 1.19.4 and 1.20.x before 1.20.1 has integer overflows that may lead to remote code execution (in KDC, kadmind, or a GSS or Kerberos application server) on 32-bit platforms (which have a resultant heap-based buffer overflow), and cause a denial of service on other platforms. This occurs in krb5_pac_parse in lib/krb5/krb/pac.c. Heimdal before 7.7.1 has "a similar bug."

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2022-46908

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - sqlite3/libsqlite3-0 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.3.13, gnupg2/gpg@2.2.27-3ubuntu2.1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - sqlite3/libsqlite3-0@3.37.2-2ubuntu0.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream sqlite3 package.

    -

    SQLite through 3.40.0, when relying on --safe for execution of an untrusted CLI script, does not properly implement the azProhibitedFunctions protection mechanism, and instead allows UDF functions such as WRITEFILE.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 sqlite3.

    -

    References

    - - -
    - - - -
    -
    -

    Uncontrolled Recursion

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - pcre3/libpcre3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - grep@3.7-1build1 - - pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream pcre3 package.

    -

    In PCRE 8.41, the OP_KETRMAX feature in the match function in pcre_exec.c allows stack exhaustion (uncontrolled recursion) when processing a crafted regular expression.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 pcre3.

    -

    References

    - - -
    - - - -
    -
    -

    Release of Invalid Pointer or Reference

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - patch -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and patch@2.7.6-7build2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - patch@2.7.6-7build2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream patch package.

    -

    An Invalid Pointer vulnerability exists in GNU patch 2.7 via the another_hunk function, which causes a Denial of Service.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 patch.

    -

    References

    - - -
    - - - -
    -
    -

    Double Free

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - patch -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and patch@2.7.6-7build2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - patch@2.7.6-7build2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream patch package.

    -

    A double free exists in the another_hunk function in pch.c in GNU patch through 2.7.6.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 patch.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Locking

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssl/libssl3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and openssl/libssl3@3.0.2-0ubuntu1.7 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - libfido2/libfido2-1@1.10.0-1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - ca-certificates@20211016ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.7 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - openssl@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - ca-certificates@20211016ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.7 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssl package.

    -

    If an X.509 certificate contains a malformed policy constraint and policy processing is enabled, then a write lock will be taken twice recursively. On some operating systems (most widely: Windows) this results in a denial of service when the affected process hangs. Policy processing being enabled on a publicly facing server is not considered to be a common setup. Policy processing is enabled by passing the -policy&#39; argument to the command line utilities or by calling either X509_VERIFY_PARAM_add0_policy()' or `X509_VERIFY_PARAM_set1_policies()' functions.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssl.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2021-41617

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssh/openssh-client -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and openssh/openssh-client@1:8.9p1-3ubuntu0.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssh package.

    -

    sshd in OpenSSH 6.2 through 8.x before 8.8, when certain non-default configurations are used, allows privilege escalation because supplemental groups are not initialized as expected. Helper programs for AuthorizedKeysCommand and AuthorizedPrincipalsCommand may run with privileges associated with group memberships of the sshd process, if the configuration specifies running the command as a different user.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssh.

    -

    References

    - - -
    - - - -
    -
    -

    Information Exposure

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssh/openssh-client -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and openssh/openssh-client@1:8.9p1-3ubuntu0.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssh package.

    -

    The client side in OpenSSH 5.7 through 8.4 has an Observable Discrepancy leading to an information leak in the algorithm negotiation. This allows man-in-the-middle attackers to target initial connection attempts (where no host key for the server has been cached by the client). NOTE: some reports state that 8.5 and 8.6 are also affected.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssh.

    -

    References

    - - -
    - - - -
    -
    -

    Out-of-bounds Read

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - ncurses/libtinfo6 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and ncurses/libtinfo6@6.3-2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - bash@5.1-6ubuntu1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - ncurses/libncursesw6@6.3-2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - less@590-1build1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - libedit/libedit2@3.1-20210910-1build1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - ncurses/libncurses6@6.3-2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - ncurses/ncurses-bin@6.3-2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - procps@2:3.3.17-6ubuntu2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - util-linux@2.37.2-4ubuntu3 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - readline/libreadline8@8.1.2-1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - pinentry/pinentry-curses@1.1.1-1build2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - ncurses/libncursesw6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - procps@2:3.3.17-6ubuntu2 - - ncurses/libncursesw6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - pinentry/pinentry-curses@1.1.1-1build2 - - ncurses/libncursesw6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - ncurses/libncurses6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - procps@2:3.3.17-6ubuntu2 - - ncurses/libncurses6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - ncurses/ncurses-base@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - ncurses/ncurses-bin@6.3-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream ncurses package.

    -

    ncurses 6.3 before patch 20220416 has an out-of-bounds read and segmentation violation in convert_strings in tinfo/read_entry.c in the terminfo library.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 ncurses.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and krb5/libk5crypto3@1.19.2-2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - meta-common-packages@meta - - krb5/libkrb5support0@1.19.2-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream krb5 package.

    -

    An issue was discovered in MIT Kerberos 5 (aka krb5) through 1.16. There is a variable "dbentry->n_key_data" in kadmin/dbutil/dump.c that can store 16-bit data but unknowingly the developer has assigned a "u4" variable to it, which is for 32-bit data. An attacker can use this vulnerability to affect other artifacts of the database as we know that a Kerberos database dump file contains trusted data.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2022-3219

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - gnupg2/gpgv -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and gnupg2/gpgv@2.2.27-3ubuntu2.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gpgv@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - apt@2.4.8 - - gnupg2/gpgv@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgv@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    This vulnerability has not been analyzed by NVD yet.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 gnupg2.

    -

    References

    - - -
    - - - -
    -
    -

    Allocation of Resources Without Limits or Throttling

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - glibc/libc-bin -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and glibc/libc-bin@2.35-0ubuntu3.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - glibc/libc-bin@2.35-0ubuntu3.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - meta-common-packages@meta - - glibc/libc6@2.35-0ubuntu3.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream glibc package.

    -

    sha256crypt and sha512crypt through 0.6 allow attackers to cause a denial of service (CPU consumption) because the algorithm's runtime is proportional to the square of the length of the password.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 glibc.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - git/git-man -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.3.13, git@1:2.34.1-1ubuntu1.6 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - git@1:2.34.1-1ubuntu1.6 - - git/git-man@1:2.34.1-1ubuntu1.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - git@1:2.34.1-1ubuntu1.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - git-lfs@3.0.2-1 - - git@1:2.34.1-1ubuntu1.6 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream git package.

    -

    GIT version 2.15.1 and earlier contains a Input Validation Error vulnerability in Client that can result in problems including messing up terminal configuration to RCE. This attack appear to be exploitable via The user must interact with a malicious git server, (or have their traffic modified in a MITM attack).

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 git.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - coreutils -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and coreutils@8.32-4.1ubuntu1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - coreutils@8.32-4.1ubuntu1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream coreutils package.

    -

    chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 coreutils.

    -

    References

    - - -
    - - - -
    -
    -

    Out-of-bounds Write

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - bash -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.3.13 and bash@5.1-6ubuntu1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.3.13 - - bash@5.1-6ubuntu1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream bash package.

    -

    A flaw was found in the bash package, where a heap-buffer overflow can occur in valid parameter_transform. This issue may lead to memory problems.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 bash.

    -

    References

    - - -
    - - - -
    -
    -
    -
    - - - diff --git a/docs/snyk/v2.3.13/redis_6.2.8-alpine.html b/docs/snyk/v2.3.13/redis_6.2.8-alpine.html deleted file mode 100644 index 43779992e4301..0000000000000 --- a/docs/snyk/v2.3.13/redis_6.2.8-alpine.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:24:04 am

    -
    -
    - Scanned the following path: -
      -
    • redis:6.2.8-alpine (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    18 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|redis
    Path redis:6.2.8-alpine
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.4.19/ghcr.io_dexidp_dex_v2.35.3.html b/docs/snyk/v2.4.19/ghcr.io_dexidp_dex_v2.35.3.html deleted file mode 100644 index a7de0984fc218..0000000000000 --- a/docs/snyk/v2.4.19/ghcr.io_dexidp_dex_v2.35.3.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:21:42 am

    -
    -
    - Scanned the following path: -
      -
    • ghcr.io/dexidp/dex:v2.35.3/dexidp/dex (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    14 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|ghcr.io/dexidp/dex
    Path ghcr.io/dexidp/dex:v2.35.3/dexidp/dex
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.4.19/haproxy_2.0.29-alpine.html b/docs/snyk/v2.4.19/haproxy_2.0.29-alpine.html deleted file mode 100644 index 82f50b8f63a41..0000000000000 --- a/docs/snyk/v2.4.19/haproxy_2.0.29-alpine.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:21:47 am

    -
    -
    - Scanned the following path: -
      -
    • haproxy:2.0.29-alpine (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    17 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|haproxy
    Path haproxy:2.0.29-alpine
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.4.19/quay.io_argoproj_argocd_v2.4.19.html b/docs/snyk/v2.4.19/quay.io_argoproj_argocd_v2.4.19.html deleted file mode 100644 index 5ea597bac98f3..0000000000000 --- a/docs/snyk/v2.4.19/quay.io_argoproj_argocd_v2.4.19.html +++ /dev/null @@ -1,2649 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:22:04 am

    -
    -
    - Scanned the following path: -
      -
    • quay.io/argoproj/argocd:v2.4.19/argoproj/argocd (deb)
    • -
    -
    - -
    -
    16 known vulnerabilities
    -
    102 vulnerable dependency paths
    -
    162 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|quay.io/argoproj/argocd
    Path quay.io/argoproj/argocd:v2.4.19/argoproj/argocd
    Package Manager deb
    Manifest Dockerfile
    -
    -
    -
    -
    -

    Off-by-one Error

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - systemd/libsystemd0 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and systemd/libsystemd0@249.11-0ubuntu3.6 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - apt@2.4.8 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - procps/libprocps8@2:3.3.17-6ubuntu2 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - util-linux@2.37.2-4ubuntu3 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - util-linux/bsdutils@1:2.37.2-4ubuntu3 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - apt@2.4.8 - - apt/libapt-pkg6.0@2.4.8 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - libfido2/libfido2-1@1.10.0-1 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - util-linux@2.37.2-4ubuntu3 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - apt@2.4.8 - - apt/libapt-pkg6.0@2.4.8 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream systemd package.

    -

    An off-by-one Error issue was discovered in Systemd in format_timespan() function of time-util.c. An attacker could supply specific values for time and accuracy that leads to buffer overrun in format_timespan(), leading to a Denial of Service.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 systemd.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and krb5/libk5crypto3@1.19.2-2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - meta-common-packages@meta - - krb5/libkrb5support0@1.19.2-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream krb5 package.

    -

    PAC parsing in MIT Kerberos 5 (aka krb5) before 1.19.4 and 1.20.x before 1.20.1 has integer overflows that may lead to remote code execution (in KDC, kadmind, or a GSS or Kerberos application server) on 32-bit platforms (which have a resultant heap-based buffer overflow), and cause a denial of service on other platforms. This occurs in krb5_pac_parse in lib/krb5/krb/pac.c. Heimdal before 7.7.1 has "a similar bug."

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2022-46908

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - sqlite3/libsqlite3-0 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.4.19, gnupg2/gpg@2.2.27-3ubuntu2.1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - sqlite3/libsqlite3-0@3.37.2-2ubuntu0.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream sqlite3 package.

    -

    SQLite through 3.40.0, when relying on --safe for execution of an untrusted CLI script, does not properly implement the azProhibitedFunctions protection mechanism, and instead allows UDF functions such as WRITEFILE.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 sqlite3.

    -

    References

    - - -
    - - - -
    -
    -

    Uncontrolled Recursion

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - pcre3/libpcre3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - grep@3.7-1build1 - - pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream pcre3 package.

    -

    In PCRE 8.41, the OP_KETRMAX feature in the match function in pcre_exec.c allows stack exhaustion (uncontrolled recursion) when processing a crafted regular expression.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 pcre3.

    -

    References

    - - -
    - - - -
    -
    -

    Release of Invalid Pointer or Reference

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - patch -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and patch@2.7.6-7build2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - patch@2.7.6-7build2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream patch package.

    -

    An Invalid Pointer vulnerability exists in GNU patch 2.7 via the another_hunk function, which causes a Denial of Service.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 patch.

    -

    References

    - - -
    - - - -
    -
    -

    Double Free

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - patch -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and patch@2.7.6-7build2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - patch@2.7.6-7build2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream patch package.

    -

    A double free exists in the another_hunk function in pch.c in GNU patch through 2.7.6.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 patch.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Locking

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssl/libssl3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and openssl/libssl3@3.0.2-0ubuntu1.7 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - libfido2/libfido2-1@1.10.0-1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - ca-certificates@20211016ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.7 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - openssl@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - ca-certificates@20211016ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.7 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssl package.

    -

    If an X.509 certificate contains a malformed policy constraint and policy processing is enabled, then a write lock will be taken twice recursively. On some operating systems (most widely: Windows) this results in a denial of service when the affected process hangs. Policy processing being enabled on a publicly facing server is not considered to be a common setup. Policy processing is enabled by passing the -policy&#39; argument to the command line utilities or by calling either X509_VERIFY_PARAM_add0_policy()' or `X509_VERIFY_PARAM_set1_policies()' functions.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssl.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2021-41617

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssh/openssh-client -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and openssh/openssh-client@1:8.9p1-3ubuntu0.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssh package.

    -

    sshd in OpenSSH 6.2 through 8.x before 8.8, when certain non-default configurations are used, allows privilege escalation because supplemental groups are not initialized as expected. Helper programs for AuthorizedKeysCommand and AuthorizedPrincipalsCommand may run with privileges associated with group memberships of the sshd process, if the configuration specifies running the command as a different user.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssh.

    -

    References

    - - -
    - - - -
    -
    -

    Information Exposure

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssh/openssh-client -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and openssh/openssh-client@1:8.9p1-3ubuntu0.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssh package.

    -

    The client side in OpenSSH 5.7 through 8.4 has an Observable Discrepancy leading to an information leak in the algorithm negotiation. This allows man-in-the-middle attackers to target initial connection attempts (where no host key for the server has been cached by the client). NOTE: some reports state that 8.5 and 8.6 are also affected.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssh.

    -

    References

    - - -
    - - - -
    -
    -

    Out-of-bounds Read

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - ncurses/libtinfo6 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and ncurses/libtinfo6@6.3-2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - bash@5.1-6ubuntu1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - ncurses/libncursesw6@6.3-2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - less@590-1build1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - libedit/libedit2@3.1-20210910-1build1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - ncurses/libncurses6@6.3-2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - ncurses/ncurses-bin@6.3-2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - procps@2:3.3.17-6ubuntu2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - util-linux@2.37.2-4ubuntu3 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - readline/libreadline8@8.1.2-1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - pinentry/pinentry-curses@1.1.1-1build2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - ncurses/libncursesw6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - procps@2:3.3.17-6ubuntu2 - - ncurses/libncursesw6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - pinentry/pinentry-curses@1.1.1-1build2 - - ncurses/libncursesw6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - ncurses/libncurses6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - procps@2:3.3.17-6ubuntu2 - - ncurses/libncurses6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - ncurses/ncurses-base@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - ncurses/ncurses-bin@6.3-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream ncurses package.

    -

    ncurses 6.3 before patch 20220416 has an out-of-bounds read and segmentation violation in convert_strings in tinfo/read_entry.c in the terminfo library.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 ncurses.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and krb5/libk5crypto3@1.19.2-2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - meta-common-packages@meta - - krb5/libkrb5support0@1.19.2-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream krb5 package.

    -

    An issue was discovered in MIT Kerberos 5 (aka krb5) through 1.16. There is a variable "dbentry->n_key_data" in kadmin/dbutil/dump.c that can store 16-bit data but unknowingly the developer has assigned a "u4" variable to it, which is for 32-bit data. An attacker can use this vulnerability to affect other artifacts of the database as we know that a Kerberos database dump file contains trusted data.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2022-3219

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - gnupg2/gpgv -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and gnupg2/gpgv@2.2.27-3ubuntu2.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gpgv@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - apt@2.4.8 - - gnupg2/gpgv@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgv@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    This vulnerability has not been analyzed by NVD yet.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 gnupg2.

    -

    References

    - - -
    - - - -
    -
    -

    Allocation of Resources Without Limits or Throttling

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - glibc/libc-bin -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and glibc/libc-bin@2.35-0ubuntu3.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - glibc/libc-bin@2.35-0ubuntu3.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - meta-common-packages@meta - - glibc/libc6@2.35-0ubuntu3.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream glibc package.

    -

    sha256crypt and sha512crypt through 0.6 allow attackers to cause a denial of service (CPU consumption) because the algorithm's runtime is proportional to the square of the length of the password.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 glibc.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - git/git-man -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.4.19, git@1:2.34.1-1ubuntu1.6 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - git@1:2.34.1-1ubuntu1.6 - - git/git-man@1:2.34.1-1ubuntu1.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - git@1:2.34.1-1ubuntu1.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - git-lfs@3.0.2-1 - - git@1:2.34.1-1ubuntu1.6 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream git package.

    -

    GIT version 2.15.1 and earlier contains a Input Validation Error vulnerability in Client that can result in problems including messing up terminal configuration to RCE. This attack appear to be exploitable via The user must interact with a malicious git server, (or have their traffic modified in a MITM attack).

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 git.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - coreutils -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and coreutils@8.32-4.1ubuntu1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - coreutils@8.32-4.1ubuntu1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream coreutils package.

    -

    chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 coreutils.

    -

    References

    - - -
    - - - -
    -
    -

    Out-of-bounds Write

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - bash -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.4.19 and bash@5.1-6ubuntu1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.4.19 - - bash@5.1-6ubuntu1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream bash package.

    -

    A flaw was found in the bash package, where a heap-buffer overflow can occur in valid parameter_transform. This issue may lead to memory problems.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 bash.

    -

    References

    - - -
    - - - -
    -
    -
    -
    - - - diff --git a/docs/snyk/v2.4.19/redis_7.0.7-alpine.html b/docs/snyk/v2.4.19/redis_7.0.7-alpine.html deleted file mode 100644 index 8d5a799fcb574..0000000000000 --- a/docs/snyk/v2.4.19/redis_7.0.7-alpine.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:22:07 am

    -
    -
    - Scanned the following path: -
      -
    • redis:7.0.7-alpine (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    18 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|redis
    Path redis:7.0.7-alpine
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.5.7/ghcr.io_dexidp_dex_v2.35.3.html b/docs/snyk/v2.5.7/ghcr.io_dexidp_dex_v2.35.3.html deleted file mode 100644 index 1462625622f11..0000000000000 --- a/docs/snyk/v2.5.7/ghcr.io_dexidp_dex_v2.35.3.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:20:03 am

    -
    -
    - Scanned the following path: -
      -
    • ghcr.io/dexidp/dex:v2.35.3/dexidp/dex (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    14 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|ghcr.io/dexidp/dex
    Path ghcr.io/dexidp/dex:v2.35.3/dexidp/dex
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.5.7/haproxy_2.6.2-alpine.html b/docs/snyk/v2.5.7/haproxy_2.6.2-alpine.html deleted file mode 100644 index d05e999b1ab85..0000000000000 --- a/docs/snyk/v2.5.7/haproxy_2.6.2-alpine.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:20:05 am

    -
    -
    - Scanned the following path: -
      -
    • haproxy:2.6.2-alpine (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    17 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|haproxy
    Path haproxy:2.6.2-alpine
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.5.7/quay.io_argoproj_argocd_v2.5.7.html b/docs/snyk/v2.5.7/quay.io_argoproj_argocd_v2.5.7.html deleted file mode 100644 index 8633b3405fe50..0000000000000 --- a/docs/snyk/v2.5.7/quay.io_argoproj_argocd_v2.5.7.html +++ /dev/null @@ -1,2649 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:20:22 am

    -
    -
    - Scanned the following path: -
      -
    • quay.io/argoproj/argocd:v2.5.7/argoproj/argocd (deb)
    • -
    -
    - -
    -
    16 known vulnerabilities
    -
    102 vulnerable dependency paths
    -
    162 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|quay.io/argoproj/argocd
    Path quay.io/argoproj/argocd:v2.5.7/argoproj/argocd
    Package Manager deb
    Manifest Dockerfile
    -
    -
    -
    -
    -

    Off-by-one Error

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - systemd/libsystemd0 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and systemd/libsystemd0@249.11-0ubuntu3.6 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - apt@2.4.8 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - procps/libprocps8@2:3.3.17-6ubuntu2 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - util-linux@2.37.2-4ubuntu3 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - util-linux/bsdutils@1:2.37.2-4ubuntu3 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - apt@2.4.8 - - apt/libapt-pkg6.0@2.4.8 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - libfido2/libfido2-1@1.10.0-1 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - util-linux@2.37.2-4ubuntu3 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - apt@2.4.8 - - apt/libapt-pkg6.0@2.4.8 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream systemd package.

    -

    An off-by-one Error issue was discovered in Systemd in format_timespan() function of time-util.c. An attacker could supply specific values for time and accuracy that leads to buffer overrun in format_timespan(), leading to a Denial of Service.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 systemd.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and krb5/libk5crypto3@1.19.2-2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - meta-common-packages@meta - - krb5/libkrb5support0@1.19.2-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream krb5 package.

    -

    PAC parsing in MIT Kerberos 5 (aka krb5) before 1.19.4 and 1.20.x before 1.20.1 has integer overflows that may lead to remote code execution (in KDC, kadmind, or a GSS or Kerberos application server) on 32-bit platforms (which have a resultant heap-based buffer overflow), and cause a denial of service on other platforms. This occurs in krb5_pac_parse in lib/krb5/krb/pac.c. Heimdal before 7.7.1 has "a similar bug."

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2022-46908

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - sqlite3/libsqlite3-0 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.5.7, gnupg2/gpg@2.2.27-3ubuntu2.1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - sqlite3/libsqlite3-0@3.37.2-2ubuntu0.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream sqlite3 package.

    -

    SQLite through 3.40.0, when relying on --safe for execution of an untrusted CLI script, does not properly implement the azProhibitedFunctions protection mechanism, and instead allows UDF functions such as WRITEFILE.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 sqlite3.

    -

    References

    - - -
    - - - -
    -
    -

    Uncontrolled Recursion

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - pcre3/libpcre3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - grep@3.7-1build1 - - pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream pcre3 package.

    -

    In PCRE 8.41, the OP_KETRMAX feature in the match function in pcre_exec.c allows stack exhaustion (uncontrolled recursion) when processing a crafted regular expression.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 pcre3.

    -

    References

    - - -
    - - - -
    -
    -

    Release of Invalid Pointer or Reference

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - patch -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and patch@2.7.6-7build2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - patch@2.7.6-7build2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream patch package.

    -

    An Invalid Pointer vulnerability exists in GNU patch 2.7 via the another_hunk function, which causes a Denial of Service.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 patch.

    -

    References

    - - -
    - - - -
    -
    -

    Double Free

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - patch -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and patch@2.7.6-7build2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - patch@2.7.6-7build2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream patch package.

    -

    A double free exists in the another_hunk function in pch.c in GNU patch through 2.7.6.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 patch.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Locking

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssl/libssl3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and openssl/libssl3@3.0.2-0ubuntu1.7 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - libfido2/libfido2-1@1.10.0-1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - ca-certificates@20211016ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.7 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - openssl@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - ca-certificates@20211016ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.7 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssl package.

    -

    If an X.509 certificate contains a malformed policy constraint and policy processing is enabled, then a write lock will be taken twice recursively. On some operating systems (most widely: Windows) this results in a denial of service when the affected process hangs. Policy processing being enabled on a publicly facing server is not considered to be a common setup. Policy processing is enabled by passing the -policy&#39; argument to the command line utilities or by calling either X509_VERIFY_PARAM_add0_policy()' or `X509_VERIFY_PARAM_set1_policies()' functions.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssl.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2021-41617

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssh/openssh-client -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and openssh/openssh-client@1:8.9p1-3ubuntu0.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssh package.

    -

    sshd in OpenSSH 6.2 through 8.x before 8.8, when certain non-default configurations are used, allows privilege escalation because supplemental groups are not initialized as expected. Helper programs for AuthorizedKeysCommand and AuthorizedPrincipalsCommand may run with privileges associated with group memberships of the sshd process, if the configuration specifies running the command as a different user.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssh.

    -

    References

    - - -
    - - - -
    -
    -

    Information Exposure

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssh/openssh-client -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and openssh/openssh-client@1:8.9p1-3ubuntu0.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssh package.

    -

    The client side in OpenSSH 5.7 through 8.4 has an Observable Discrepancy leading to an information leak in the algorithm negotiation. This allows man-in-the-middle attackers to target initial connection attempts (where no host key for the server has been cached by the client). NOTE: some reports state that 8.5 and 8.6 are also affected.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssh.

    -

    References

    - - -
    - - - -
    -
    -

    Out-of-bounds Read

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - ncurses/libtinfo6 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and ncurses/libtinfo6@6.3-2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - bash@5.1-6ubuntu1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - ncurses/libncursesw6@6.3-2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - less@590-1build1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - libedit/libedit2@3.1-20210910-1build1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - ncurses/libncurses6@6.3-2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - ncurses/ncurses-bin@6.3-2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - procps@2:3.3.17-6ubuntu2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - util-linux@2.37.2-4ubuntu3 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - readline/libreadline8@8.1.2-1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - pinentry/pinentry-curses@1.1.1-1build2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - ncurses/libncursesw6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - procps@2:3.3.17-6ubuntu2 - - ncurses/libncursesw6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - pinentry/pinentry-curses@1.1.1-1build2 - - ncurses/libncursesw6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - ncurses/libncurses6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - procps@2:3.3.17-6ubuntu2 - - ncurses/libncurses6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - ncurses/ncurses-base@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - ncurses/ncurses-bin@6.3-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream ncurses package.

    -

    ncurses 6.3 before patch 20220416 has an out-of-bounds read and segmentation violation in convert_strings in tinfo/read_entry.c in the terminfo library.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 ncurses.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and krb5/libk5crypto3@1.19.2-2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - meta-common-packages@meta - - krb5/libkrb5support0@1.19.2-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream krb5 package.

    -

    An issue was discovered in MIT Kerberos 5 (aka krb5) through 1.16. There is a variable "dbentry->n_key_data" in kadmin/dbutil/dump.c that can store 16-bit data but unknowingly the developer has assigned a "u4" variable to it, which is for 32-bit data. An attacker can use this vulnerability to affect other artifacts of the database as we know that a Kerberos database dump file contains trusted data.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2022-3219

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - gnupg2/gpgv -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and gnupg2/gpgv@2.2.27-3ubuntu2.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gpgv@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - apt@2.4.8 - - gnupg2/gpgv@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgv@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    This vulnerability has not been analyzed by NVD yet.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 gnupg2.

    -

    References

    - - -
    - - - -
    -
    -

    Allocation of Resources Without Limits or Throttling

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - glibc/libc-bin -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and glibc/libc-bin@2.35-0ubuntu3.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - glibc/libc-bin@2.35-0ubuntu3.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - meta-common-packages@meta - - glibc/libc6@2.35-0ubuntu3.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream glibc package.

    -

    sha256crypt and sha512crypt through 0.6 allow attackers to cause a denial of service (CPU consumption) because the algorithm's runtime is proportional to the square of the length of the password.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 glibc.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - git/git-man -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.5.7, git@1:2.34.1-1ubuntu1.6 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - git@1:2.34.1-1ubuntu1.6 - - git/git-man@1:2.34.1-1ubuntu1.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - git@1:2.34.1-1ubuntu1.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - git-lfs@3.0.2-1 - - git@1:2.34.1-1ubuntu1.6 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream git package.

    -

    GIT version 2.15.1 and earlier contains a Input Validation Error vulnerability in Client that can result in problems including messing up terminal configuration to RCE. This attack appear to be exploitable via The user must interact with a malicious git server, (or have their traffic modified in a MITM attack).

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 git.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - coreutils -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and coreutils@8.32-4.1ubuntu1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - coreutils@8.32-4.1ubuntu1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream coreutils package.

    -

    chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 coreutils.

    -

    References

    - - -
    - - - -
    -
    -

    Out-of-bounds Write

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - bash -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.5.7 and bash@5.1-6ubuntu1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.5.7 - - bash@5.1-6ubuntu1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream bash package.

    -

    A flaw was found in the bash package, where a heap-buffer overflow can occur in valid parameter_transform. This issue may lead to memory problems.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 bash.

    -

    References

    - - -
    - - - -
    -
    -
    -
    - - - diff --git a/docs/snyk/v2.5.7/redis_7.0.7-alpine.html b/docs/snyk/v2.5.7/redis_7.0.7-alpine.html deleted file mode 100644 index c875196a099f9..0000000000000 --- a/docs/snyk/v2.5.7/redis_7.0.7-alpine.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:20:24 am

    -
    -
    - Scanned the following path: -
      -
    • redis:7.0.7-alpine (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    18 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|redis
    Path redis:7.0.7-alpine
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.6.0-rc4/argocd-test.html b/docs/snyk/v2.6.0-rc4/argocd-test.html deleted file mode 100644 index 805621ccfd351..0000000000000 --- a/docs/snyk/v2.6.0-rc4/argocd-test.html +++ /dev/null @@ -1,623 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:17:46 am

    -
    -
    - Scanned the following paths: -
      -
    • /argo-cd/argoproj/argo-cd/v2 (gomodules)
    • /argo-cd (yarn)
    • -
    -
    - -
    -
    1 known vulnerabilities
    -
    1 vulnerable dependency paths
    -
    1730 dependencies
    -
    -
    -
    -
    - -
    -
    -
    -

    Regular Expression Denial of Service (ReDoS)

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - cookiejar -
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, superagent@7.1.6 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - superagent@7.1.6 - - cookiejar@2.1.3 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) via the Cookie.parse function, which uses an insecure regular expression.

    -

    PoC

    -
    const { CookieJar } = require("cookiejar");
    -        
    -        const jar = new CookieJar();
    -        
    -        const start = performance.now();
    -        const attack = "a" + "t".repeat(50_000);
    -        jar.setCookie(attack);
    -        console.log(`CookieJar.setCookie(): ${performance.now() - start}`);
    -        
    -

    Details

    -

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.

    -

    The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.

    -

    Let’s take the following regular expression as an example:

    -
    regex = /A(B|C+)+D/
    -        
    -

    This regular expression accomplishes the following:

    -
      -
    • A The string must start with the letter 'A'
    • -
    • (B|C+)+ The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the + matches one or more times). The + at the end of this section states that we can look for one or more matches of this section.
    • -
    • D Finally, we ensure this section of the string ends with a 'D'
    • -
    -

    The expression would match inputs such as ABBD, ABCCCCD, ABCBCCCD and ACCCCCD

    -

    It most cases, it doesn't take very long for a regex engine to find a match:

    -
    $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD")'
    -        0.04s user 0.01s system 95% cpu 0.052 total
    -        
    -        $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX")'
    -        1.79s user 0.02s system 99% cpu 1.812 total
    -        
    -

    The entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.

    -

    Most Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as catastrophic backtracking.

    -

    Let's look at how our expression runs into this problem, using a shorter string: "ACCCX". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:

    -
      -
    1. CCC
    2. -
    3. CC+C
    4. -
    5. C+CC
    6. -
    7. C+C+C.
    8. -
    -

    The engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use RegEx 101 debugger to see the engine has to take a total of 38 steps before it can determine the string doesn't match.

    -

    From there, the number of steps the engine must use to validate a string just continues to grow.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    StringNumber of C'sNumber of steps
    ACCCX338
    ACCCCX471
    ACCCCCX5136
    ACCCCCCCCCCCCCCX1465,553
    -

    By the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.

    -

    Remediation

    -

    Upgrade cookiejar to version 2.1.4 or higher.

    -

    References

    - - -
    - - - -
    -
    -
    -
    - - - diff --git a/docs/snyk/v2.6.0-rc4/ghcr.io_dexidp_dex_v2.35.3.html b/docs/snyk/v2.6.0-rc4/ghcr.io_dexidp_dex_v2.35.3.html deleted file mode 100644 index b67180db9cea3..0000000000000 --- a/docs/snyk/v2.6.0-rc4/ghcr.io_dexidp_dex_v2.35.3.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:17:50 am

    -
    -
    - Scanned the following path: -
      -
    • ghcr.io/dexidp/dex:v2.35.3/dexidp/dex (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    14 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|ghcr.io/dexidp/dex
    Path ghcr.io/dexidp/dex:v2.35.3/dexidp/dex
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.6.0-rc4/haproxy_2.6.2-alpine.html b/docs/snyk/v2.6.0-rc4/haproxy_2.6.2-alpine.html deleted file mode 100644 index 93198b20ea6fe..0000000000000 --- a/docs/snyk/v2.6.0-rc4/haproxy_2.6.2-alpine.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:17:52 am

    -
    -
    - Scanned the following path: -
      -
    • haproxy:2.6.2-alpine (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    17 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|haproxy
    Path haproxy:2.6.2-alpine
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.6.0-rc4/quay.io_argoproj_argocd_v2.6.0-rc4.html b/docs/snyk/v2.6.0-rc4/quay.io_argoproj_argocd_v2.6.0-rc4.html deleted file mode 100644 index 691d2db5efd00..0000000000000 --- a/docs/snyk/v2.6.0-rc4/quay.io_argoproj_argocd_v2.6.0-rc4.html +++ /dev/null @@ -1,2649 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:18:09 am

    -
    -
    - Scanned the following path: -
      -
    • quay.io/argoproj/argocd:v2.6.0-rc4/argoproj/argocd (deb)
    • -
    -
    - -
    -
    16 known vulnerabilities
    -
    102 vulnerable dependency paths
    -
    162 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|quay.io/argoproj/argocd
    Path quay.io/argoproj/argocd:v2.6.0-rc4/argoproj/argocd
    Package Manager deb
    Manifest Dockerfile
    -
    -
    -
    -
    -

    Off-by-one Error

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - systemd/libsystemd0 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and systemd/libsystemd0@249.11-0ubuntu3.6 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - apt@2.4.8 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - procps/libprocps8@2:3.3.17-6ubuntu2 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - util-linux@2.37.2-4ubuntu3 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - util-linux/bsdutils@1:2.37.2-4ubuntu3 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - apt@2.4.8 - - apt/libapt-pkg6.0@2.4.8 - - systemd/libsystemd0@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - libfido2/libfido2-1@1.10.0-1 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - util-linux@2.37.2-4ubuntu3 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - apt@2.4.8 - - apt/libapt-pkg6.0@2.4.8 - - systemd/libudev1@249.11-0ubuntu3.6 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream systemd package.

    -

    An off-by-one Error issue was discovered in Systemd in format_timespan() function of time-util.c. An attacker could supply specific values for time and accuracy that leads to buffer overrun in format_timespan(), leading to a Denial of Service.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 systemd.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and krb5/libk5crypto3@1.19.2-2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - meta-common-packages@meta - - krb5/libkrb5support0@1.19.2-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream krb5 package.

    -

    PAC parsing in MIT Kerberos 5 (aka krb5) before 1.19.4 and 1.20.x before 1.20.1 has integer overflows that may lead to remote code execution (in KDC, kadmind, or a GSS or Kerberos application server) on 32-bit platforms (which have a resultant heap-based buffer overflow), and cause a denial of service on other platforms. This occurs in krb5_pac_parse in lib/krb5/krb/pac.c. Heimdal before 7.7.1 has "a similar bug."

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2022-46908

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - sqlite3/libsqlite3-0 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4, gnupg2/gpg@2.2.27-3ubuntu2.1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - sqlite3/libsqlite3-0@3.37.2-2ubuntu0.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream sqlite3 package.

    -

    SQLite through 3.40.0, when relying on --safe for execution of an untrusted CLI script, does not properly implement the azProhibitedFunctions protection mechanism, and instead allows UDF functions such as WRITEFILE.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 sqlite3.

    -

    References

    - - -
    - - - -
    -
    -

    Uncontrolled Recursion

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - pcre3/libpcre3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - grep@3.7-1build1 - - pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream pcre3 package.

    -

    In PCRE 8.41, the OP_KETRMAX feature in the match function in pcre_exec.c allows stack exhaustion (uncontrolled recursion) when processing a crafted regular expression.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 pcre3.

    -

    References

    - - -
    - - - -
    -
    -

    Release of Invalid Pointer or Reference

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - patch -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and patch@2.7.6-7build2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - patch@2.7.6-7build2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream patch package.

    -

    An Invalid Pointer vulnerability exists in GNU patch 2.7 via the another_hunk function, which causes a Denial of Service.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 patch.

    -

    References

    - - -
    - - - -
    -
    -

    Double Free

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - patch -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and patch@2.7.6-7build2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - patch@2.7.6-7build2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream patch package.

    -

    A double free exists in the another_hunk function in pch.c in GNU patch through 2.7.6.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 patch.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Locking

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssl/libssl3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and openssl/libssl3@3.0.2-0ubuntu1.7 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - libfido2/libfido2-1@1.10.0-1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - ca-certificates@20211016ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.7 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - openssl/libssl3@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - openssl@3.0.2-0ubuntu1.7 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - ca-certificates@20211016ubuntu0.22.04.1 - - openssl@3.0.2-0ubuntu1.7 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssl package.

    -

    If an X.509 certificate contains a malformed policy constraint and policy processing is enabled, then a write lock will be taken twice recursively. On some operating systems (most widely: Windows) this results in a denial of service when the affected process hangs. Policy processing being enabled on a publicly facing server is not considered to be a common setup. Policy processing is enabled by passing the -policy&#39; argument to the command line utilities or by calling either X509_VERIFY_PARAM_add0_policy()' or `X509_VERIFY_PARAM_set1_policies()' functions.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssl.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2021-41617

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssh/openssh-client -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and openssh/openssh-client@1:8.9p1-3ubuntu0.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssh package.

    -

    sshd in OpenSSH 6.2 through 8.x before 8.8, when certain non-default configurations are used, allows privilege escalation because supplemental groups are not initialized as expected. Helper programs for AuthorizedKeysCommand and AuthorizedPrincipalsCommand may run with privileges associated with group memberships of the sshd process, if the configuration specifies running the command as a different user.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssh.

    -

    References

    - - -
    - - - -
    -
    -

    Information Exposure

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - openssh/openssh-client -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and openssh/openssh-client@1:8.9p1-3ubuntu0.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream openssh package.

    -

    The client side in OpenSSH 5.7 through 8.4 has an Observable Discrepancy leading to an information leak in the algorithm negotiation. This allows man-in-the-middle attackers to target initial connection attempts (where no host key for the server has been cached by the client). NOTE: some reports state that 8.5 and 8.6 are also affected.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 openssh.

    -

    References

    - - -
    - - - -
    -
    -

    Out-of-bounds Read

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - ncurses/libtinfo6 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and ncurses/libtinfo6@6.3-2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - bash@5.1-6ubuntu1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - ncurses/libncursesw6@6.3-2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - less@590-1build1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - libedit/libedit2@3.1-20210910-1build1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - ncurses/libncurses6@6.3-2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - ncurses/ncurses-bin@6.3-2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - procps@2:3.3.17-6ubuntu2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - util-linux@2.37.2-4ubuntu3 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - readline/libreadline8@8.1.2-1 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - pinentry/pinentry-curses@1.1.1-1build2 - - ncurses/libtinfo6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - ncurses/libncursesw6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - procps@2:3.3.17-6ubuntu2 - - ncurses/libncursesw6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - pinentry/pinentry-curses@1.1.1-1build2 - - ncurses/libncursesw6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - ncurses/libncurses6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - procps@2:3.3.17-6ubuntu2 - - ncurses/libncurses6@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - ncurses/ncurses-base@6.3-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - ncurses/ncurses-bin@6.3-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream ncurses package.

    -

    ncurses 6.3 before patch 20220416 has an out-of-bounds read and segmentation violation in convert_strings in tinfo/read_entry.c in the terminfo library.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 ncurses.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - krb5/libk5crypto3 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and krb5/libk5crypto3@1.19.2-2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - krb5/libk5crypto3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - krb5/libkrb5-3@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - openssh/openssh-client@1:8.9p1-3ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - git@1:2.34.1-1ubuntu1.6 - - curl/libcurl3-gnutls@7.81.0-1ubuntu1.7 - - libssh/libssh-4@0.9.6-2build1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-2ubuntu2.1 - - pam/libpam-modules@1.4.0-11ubuntu2 - - libnsl/libnsl2@1.3.0-2build2 - - libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - - krb5/libgssapi-krb5-2@1.19.2-2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - meta-common-packages@meta - - krb5/libkrb5support0@1.19.2-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream krb5 package.

    -

    An issue was discovered in MIT Kerberos 5 (aka krb5) through 1.16. There is a variable "dbentry->n_key_data" in kadmin/dbutil/dump.c that can store 16-bit data but unknowingly the developer has assigned a "u4" variable to it, which is for 32-bit data. An attacker can use this vulnerability to affect other artifacts of the database as we know that a Kerberos database dump file contains trusted data.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 krb5.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2022-3219

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - gnupg2/gpgv -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and gnupg2/gpgv@2.2.27-3ubuntu2.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gpgv@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - apt@2.4.8 - - gnupg2/gpgv@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgv@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 - - gnupg2/gpgconf@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - gnupg2/dirmngr@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - gnupg2/gpg@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - gnupg2/gpgsm@2.2.27-3ubuntu2.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - gnupg2/gnupg@2.2.27-3ubuntu2.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    This vulnerability has not been analyzed by NVD yet.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 gnupg2.

    -

    References

    - - -
    - - - -
    -
    -

    Allocation of Resources Without Limits or Throttling

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - glibc/libc-bin -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and glibc/libc-bin@2.35-0ubuntu3.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - glibc/libc-bin@2.35-0ubuntu3.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - meta-common-packages@meta - - glibc/libc6@2.35-0ubuntu3.1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream glibc package.

    -

    sha256crypt and sha512crypt through 0.6 allow attackers to cause a denial of service (CPU consumption) because the algorithm's runtime is proportional to the square of the length of the password.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 glibc.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - git/git-man -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4, git@1:2.34.1-1ubuntu1.6 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - git@1:2.34.1-1ubuntu1.6 - - git/git-man@1:2.34.1-1ubuntu1.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - git@1:2.34.1-1ubuntu1.6 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - git-lfs@3.0.2-1 - - git@1:2.34.1-1ubuntu1.6 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream git package.

    -

    GIT version 2.15.1 and earlier contains a Input Validation Error vulnerability in Client that can result in problems including messing up terminal configuration to RCE. This attack appear to be exploitable via The user must interact with a malicious git server, (or have their traffic modified in a MITM attack).

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 git.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - coreutils -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and coreutils@8.32-4.1ubuntu1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - coreutils@8.32-4.1ubuntu1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream coreutils package.

    -

    chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 coreutils.

    -

    References

    - - -
    - - - -
    -
    -

    Out-of-bounds Write

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:22.04 -
    • -
    • - Vulnerable module: - - bash -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 and bash@5.1-6ubuntu1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd@v2.6.0-rc4 - - bash@5.1-6ubuntu1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream bash package.

    -

    A flaw was found in the bash package, where a heap-buffer overflow can occur in valid parameter_transform. This issue may lead to memory problems.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:22.04 bash.

    -

    References

    - - -
    - - - -
    -
    -
    -
    - - - diff --git a/docs/snyk/v2.6.0-rc4/redis_7.0.7-alpine.html b/docs/snyk/v2.6.0-rc4/redis_7.0.7-alpine.html deleted file mode 100644 index e25016be291ae..0000000000000 --- a/docs/snyk/v2.6.0-rc4/redis_7.0.7-alpine.html +++ /dev/null @@ -1,492 +0,0 @@ - - - - - - - - - Snyk test report - - - - - - - - - -
    -
    -
    -
    - - - Snyk - Open Source Security - - - - - - - -
    -

    Snyk test report

    - -

    January 22nd 2023, 12:18:12 am

    -
    -
    - Scanned the following path: -
      -
    • redis:7.0.7-alpine (apk)
    • -
    -
    - -
    -
    0 known vulnerabilities
    -
    0 vulnerable dependency paths
    -
    18 dependencies
    -
    -
    -
    -
    -
    - - - - - - - -
    Project docker-image|redis
    Path redis:7.0.7-alpine
    Package Manager apk
    -
    -
    - No known vulnerabilities detected. -
    -
    - - - diff --git a/docs/snyk/v2.5.7/argocd-iac-install.html b/docs/snyk/v2.7.17/argocd-iac-install.html similarity index 72% rename from docs/snyk/v2.5.7/argocd-iac-install.html rename to docs/snyk/v2.7.17/argocd-iac-install.html index b404470870cf4..cfced2ce2b173 100644 --- a/docs/snyk/v2.5.7/argocd-iac-install.html +++ b/docs/snyk/v2.7.17/argocd-iac-install.html @@ -456,7 +456,7 @@

    Snyk test report

    -

    January 22nd 2023, 12:21:13 am

    +

    March 24th 2024, 12:23:21 am (UTC+00:00)

    Scanned the following path: @@ -466,7 +466,7 @@

    Snyk test report

    -
    32 total issues
    +
    39 total issues
    @@ -483,7 +483,7 @@

    Snyk test report

    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -494,7 +494,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -507,29 +507,29 @@

      Role with dangerous permissions

    • - Line number: 9318 + Line number: 16324

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -540,7 +540,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -553,29 +553,29 @@

      Role with dangerous permissions

    • - Line number: 9395 + Line number: 16401

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -586,7 +586,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -599,29 +599,29 @@

      Role with dangerous permissions

    • - Line number: 9423 + Line number: 16429

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -632,42 +632,42 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: [DocId: 13] - rules[3] + rules[1] resources
    • - Line number: 9467 + Line number: 16459

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -678,42 +678,42 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: [DocId: 13] - rules[1] + rules[3] resources
    • - Line number: 9449 + Line number: 16477

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -724,7 +724,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -737,24 +737,24 @@

      Role with dangerous permissions

    • - Line number: 9483 + Line number: 16493

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    @@ -770,7 +770,7 @@

    Container could be running with outdated image

    • - Public ID: SNYK-CC-K8S-42 + Public ID: SNYK-CC-K8S-42
    • Introduced through: @@ -789,7 +789,7 @@

      Container could be running with outdated image

    • - Line number: 10420 + Line number: 17537
    @@ -806,7 +806,7 @@

    Remediation

    @@ -822,7 +822,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -847,7 +847,7 @@

      Container has no CPU limit

    • - Line number: 9950 + Line number: 16980
    @@ -864,7 +864,7 @@

    Remediation

    @@ -880,7 +880,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -905,7 +905,7 @@

      Container has no CPU limit

    • - Line number: 10063 + Line number: 17152
    @@ -922,7 +922,7 @@

    Remediation

    @@ -938,7 +938,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -963,7 +963,7 @@

      Container has no CPU limit

    • - Line number: 10029 + Line number: 17118
    @@ -980,7 +980,7 @@

    Remediation

    @@ -996,7 +996,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -1021,7 +1021,7 @@

      Container has no CPU limit

    • - Line number: 10119 + Line number: 17212
    @@ -1038,7 +1038,7 @@

    Remediation

    @@ -1054,7 +1054,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -1079,7 +1079,7 @@

      Container has no CPU limit

    • - Line number: 10193 + Line number: 17293
    @@ -1096,7 +1096,7 @@

    Remediation

    @@ -1112,7 +1112,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -1137,7 +1137,7 @@

      Container has no CPU limit

    • - Line number: 10420 + Line number: 17537
    @@ -1154,7 +1154,7 @@

    Remediation

    @@ -1170,7 +1170,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -1195,7 +1195,7 @@

      Container has no CPU limit

    • - Line number: 10249 + Line number: 17349
    @@ -1212,7 +1212,7 @@

    Remediation

    @@ -1228,7 +1228,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -1253,7 +1253,7 @@

      Container has no CPU limit

    • - Line number: 10505 + Line number: 17622
    @@ -1270,7 +1270,7 @@

    Remediation

    @@ -1286,7 +1286,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -1311,7 +1311,7 @@

      Container has no CPU limit

    • - Line number: 10802 + Line number: 17932
    @@ -1328,7 +1328,7 @@

    Remediation

    @@ -1344,7 +1344,7 @@

    Container is running with multiple open ports

    • - Public ID: SNYK-CC-K8S-36 + Public ID: SNYK-CC-K8S-36
    • Introduced through: @@ -1363,7 +1363,7 @@

      Container is running with multiple open ports

    • - Line number: 10043 + Line number: 17132
    @@ -1380,7 +1380,7 @@

    Remediation

    @@ -1396,14 +1396,12 @@

    Container is running with writable root filesystem

    • - Public ID: SNYK-CC-K8S-8 + Public ID: SNYK-CC-K8S-8
    • Introduced through: [DocId: 45] - input - spec template @@ -1419,7 +1417,7 @@

      Container is running with writable root filesystem

    • - Line number: 10203 + Line number: 17303
    @@ -1429,14 +1427,14 @@

    Impact

    Compromised process could abuse writable root filesystem to elevate privileges

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    +

    Set `spec.{containers, initContainers}.securityContext.readOnlyRootFilesystem` to `true`


    @@ -1452,7 +1450,7 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: @@ -1471,7 +1469,7 @@

      Container is running without liveness probe

    • - Line number: 9950 + Line number: 16980
    @@ -1488,7 +1486,7 @@

    Remediation

    @@ -1504,7 +1502,7 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: @@ -1523,7 +1521,7 @@

      Container is running without liveness probe

    • - Line number: 10029 + Line number: 17118
    @@ -1540,7 +1538,7 @@

    Remediation

    @@ -1556,11 +1554,11 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 43] + [DocId: 45] spec @@ -1568,14 +1566,14 @@

      Container is running without liveness probe

      spec - initContainers[copyutil] + containers[redis] livenessProbe
    • - Line number: 10063 + Line number: 17293
    @@ -1592,12 +1590,12 @@

    Remediation

    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1608,11 +1606,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 45] + [DocId: 42] + + input spec @@ -1620,36 +1620,40 @@

      Container is running without liveness probe

      spec - containers[redis] + containers[argocd-applicationset-controller] - livenessProbe + resources + + limits + + memory
    • - Line number: 10193 + Line number: 16980

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1660,11 +1664,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 46] + [DocId: 43] + + input spec @@ -1672,31 +1678,35 @@

      Container is running without liveness probe

      spec - initContainers[copyutil] + containers[dex] - livenessProbe + resources + + limits + + memory
    • - Line number: 10420 + Line number: 17118

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    @@ -1712,11 +1722,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 42] + [DocId: 43] input @@ -1726,7 +1736,7 @@

      Container is running without memory limit

      spec - containers[argocd-applicationset-controller] + initContainers[copyutil] resources @@ -1737,7 +1747,7 @@

      Container is running without memory limit

    • - Line number: 9950 + Line number: 17152
    @@ -1754,7 +1764,7 @@

    Remediation

    @@ -1770,11 +1780,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 43] + [DocId: 44] input @@ -1784,7 +1794,7 @@

      Container is running without memory limit

      spec - containers[dex] + containers[argocd-notifications-controller] resources @@ -1795,7 +1805,7 @@

      Container is running without memory limit

    • - Line number: 10029 + Line number: 17212
    @@ -1812,7 +1822,7 @@

    Remediation

    @@ -1828,11 +1838,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 43] + [DocId: 45] input @@ -1842,7 +1852,7 @@

      Container is running without memory limit

      spec - initContainers[copyutil] + containers[redis] resources @@ -1853,7 +1863,7 @@

      Container is running without memory limit

    • - Line number: 10063 + Line number: 17293
    @@ -1870,7 +1880,7 @@

    Remediation

    @@ -1886,11 +1896,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 44] + [DocId: 46] input @@ -1900,7 +1910,7 @@

      Container is running without memory limit

      spec - containers[argocd-notifications-controller] + initContainers[copyutil] resources @@ -1911,7 +1921,7 @@

      Container is running without memory limit

    • - Line number: 10119 + Line number: 17537
    @@ -1928,7 +1938,7 @@

    Remediation

    @@ -1944,11 +1954,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 45] + [DocId: 46] input @@ -1958,7 +1968,7 @@

      Container is running without memory limit

      spec - containers[redis] + containers[argocd-repo-server] resources @@ -1969,7 +1979,7 @@

      Container is running without memory limit

    • - Line number: 10193 + Line number: 17349
    @@ -1986,7 +1996,7 @@

    Remediation

    @@ -2002,11 +2012,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 46] + [DocId: 47] input @@ -2016,7 +2026,7 @@

      Container is running without memory limit

      spec - initContainers[copyutil] + containers[argocd-server] resources @@ -2027,7 +2037,7 @@

      Container is running without memory limit

    • - Line number: 10420 + Line number: 17622
    @@ -2044,7 +2054,7 @@

    Remediation

    @@ -2060,11 +2070,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 46] + [DocId: 48] input @@ -2074,7 +2084,7 @@

      Container is running without memory limit

      spec - containers[argocd-repo-server] + containers[argocd-application-controller] resources @@ -2085,7 +2095,7 @@

      Container is running without memory limit

    • - Line number: 10249 + Line number: 17932
    @@ -2102,12 +2112,12 @@

    Remediation

    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2118,7 +2128,399 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 42] + + input + + spec + + template + + spec + + containers[argocd-applicationset-controller] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 17055 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 43] + + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 17160 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 43] + + input + + spec + + template + + spec + + containers[dex] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 17135 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 44] + + input + + spec + + template + + spec + + containers[argocd-notifications-controller] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 17227 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 45] + + input + + spec + + template + + spec + + containers[redis] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 17303 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 46] + + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 17544 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 46] + + input + + spec + + template + + spec + + containers[argocd-repo-server] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 17510 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11
    • Introduced through: @@ -2134,38 +2536,36 @@

      Container is running without memory limit

      containers[argocd-server] - resources - - limits + securityContext - memory + runAsUser
    • - Line number: 10505 + Line number: 17842

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2176,7 +2576,7 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: @@ -2192,33 +2592,31 @@

      Container is running without memory limit

      containers[argocd-application-controller] - resources - - limits + securityContext - memory + runAsUser
    • - Line number: 10802 + Line number: 18074

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    diff --git a/docs/snyk/v2.5.7/argocd-iac-namespace-install.html b/docs/snyk/v2.7.17/argocd-iac-namespace-install.html similarity index 72% rename from docs/snyk/v2.5.7/argocd-iac-namespace-install.html rename to docs/snyk/v2.7.17/argocd-iac-namespace-install.html index 472530a63d5da..f9744975422e6 100644 --- a/docs/snyk/v2.5.7/argocd-iac-namespace-install.html +++ b/docs/snyk/v2.7.17/argocd-iac-namespace-install.html @@ -456,7 +456,7 @@

    Snyk test report

    -

    January 22nd 2023, 12:21:22 am

    +

    March 24th 2024, 12:23:30 am (UTC+00:00)

    Scanned the following path: @@ -466,7 +466,7 @@

    Snyk test report

    -
    32 total issues
    +
    39 total issues
    @@ -483,7 +483,7 @@

    Snyk test report

    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -494,7 +494,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -514,22 +514,22 @@

      Role with dangerous permissions


      Impact

      -

      Using this role grants dangerous permissions

      +

      Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

      Remediation

      -

      Consider removing this permissions

      +

      Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -540,7 +540,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -560,22 +560,22 @@

      Role with dangerous permissions


      Impact

      -

      Using this role grants dangerous permissions

      +

      Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

      Remediation

      -

      Consider removing this permissions

      +

      Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -586,7 +586,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -606,22 +606,22 @@

      Role with dangerous permissions


      Impact

      -

      Using this role grants dangerous permissions

      +

      Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

      Remediation

      -

      Consider removing this permissions

      +

      Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -632,42 +632,42 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: [DocId: 10] - rules[3] + rules[1] resources
    • - Line number: 226 + Line number: 212

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -678,42 +678,42 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: [DocId: 10] - rules[1] + rules[3] resources
    • - Line number: 208 + Line number: 230

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -724,7 +724,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -737,24 +737,24 @@

      Role with dangerous permissions

    • - Line number: 242 + Line number: 246

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    @@ -770,7 +770,7 @@

    Container could be running with outdated image

    • - Public ID: SNYK-CC-K8S-42 + Public ID: SNYK-CC-K8S-42
    • Introduced through: @@ -789,7 +789,7 @@

      Container could be running with outdated image

    • - Line number: 1086 + Line number: 1197
    @@ -806,7 +806,7 @@

    Remediation

    @@ -822,7 +822,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -847,7 +847,7 @@

      Container has no CPU limit

    • - Line number: 616 + Line number: 640
    @@ -864,7 +864,7 @@

    Remediation

    @@ -880,7 +880,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -905,7 +905,7 @@

      Container has no CPU limit

    • - Line number: 729 + Line number: 812
    @@ -922,7 +922,7 @@

    Remediation

    @@ -938,7 +938,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -963,7 +963,7 @@

      Container has no CPU limit

    • - Line number: 695 + Line number: 778
    @@ -980,7 +980,7 @@

    Remediation

    @@ -996,7 +996,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -1021,7 +1021,7 @@

      Container has no CPU limit

    • - Line number: 785 + Line number: 872
    @@ -1038,7 +1038,7 @@

    Remediation

    @@ -1054,7 +1054,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -1079,7 +1079,7 @@

      Container has no CPU limit

    • - Line number: 859 + Line number: 953
    @@ -1096,7 +1096,7 @@

    Remediation

    @@ -1112,7 +1112,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -1137,7 +1137,7 @@

      Container has no CPU limit

    • - Line number: 1086 + Line number: 1197
    @@ -1154,7 +1154,7 @@

    Remediation

    @@ -1170,7 +1170,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -1195,7 +1195,7 @@

      Container has no CPU limit

    • - Line number: 915 + Line number: 1009
    @@ -1212,7 +1212,7 @@

    Remediation

    @@ -1228,7 +1228,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -1253,7 +1253,7 @@

      Container has no CPU limit

    • - Line number: 1171 + Line number: 1282
    @@ -1270,7 +1270,7 @@

    Remediation

    @@ -1286,7 +1286,7 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: @@ -1311,7 +1311,7 @@

      Container has no CPU limit

    • - Line number: 1468 + Line number: 1592
    @@ -1328,7 +1328,7 @@

    Remediation

    @@ -1344,7 +1344,7 @@

    Container is running with multiple open ports

    • - Public ID: SNYK-CC-K8S-36 + Public ID: SNYK-CC-K8S-36
    • Introduced through: @@ -1363,7 +1363,7 @@

      Container is running with multiple open ports

    • - Line number: 709 + Line number: 792
    @@ -1380,7 +1380,7 @@

    Remediation

    @@ -1396,14 +1396,12 @@

    Container is running with writable root filesystem

    • - Public ID: SNYK-CC-K8S-8 + Public ID: SNYK-CC-K8S-8
    • Introduced through: [DocId: 38] - input - spec template @@ -1419,7 +1417,7 @@

      Container is running with writable root filesystem

    • - Line number: 869 + Line number: 963
    @@ -1429,14 +1427,14 @@

    Impact

    Compromised process could abuse writable root filesystem to elevate privileges

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    +

    Set `spec.{containers, initContainers}.securityContext.readOnlyRootFilesystem` to `true`


    @@ -1452,7 +1450,7 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: @@ -1471,7 +1469,7 @@

      Container is running without liveness probe

    • - Line number: 616 + Line number: 640
    @@ -1488,7 +1486,7 @@

    Remediation

    @@ -1504,7 +1502,7 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: @@ -1523,7 +1521,7 @@

      Container is running without liveness probe

    • - Line number: 695 + Line number: 778
    @@ -1540,7 +1538,7 @@

    Remediation

    @@ -1556,11 +1554,11 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 36] + [DocId: 38] spec @@ -1568,14 +1566,14 @@

      Container is running without liveness probe

      spec - initContainers[copyutil] + containers[redis] livenessProbe
    • - Line number: 729 + Line number: 953
    @@ -1592,12 +1590,12 @@

    Remediation

    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1608,11 +1606,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 38] + [DocId: 35] + + input spec @@ -1620,36 +1620,40 @@

      Container is running without liveness probe

      spec - containers[redis] + containers[argocd-applicationset-controller] - livenessProbe + resources + + limits + + memory
    • - Line number: 859 + Line number: 640

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1660,11 +1664,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 39] + [DocId: 36] + + input spec @@ -1672,31 +1678,35 @@

      Container is running without liveness probe

      spec - initContainers[copyutil] + containers[dex] - livenessProbe + resources + + limits + + memory
    • - Line number: 1086 + Line number: 778

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    @@ -1712,11 +1722,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 35] + [DocId: 36] input @@ -1726,7 +1736,7 @@

      Container is running without memory limit

      spec - containers[argocd-applicationset-controller] + initContainers[copyutil] resources @@ -1737,7 +1747,7 @@

      Container is running without memory limit

    • - Line number: 616 + Line number: 812
    @@ -1754,7 +1764,7 @@

    Remediation

    @@ -1770,11 +1780,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 36] + [DocId: 37] input @@ -1784,7 +1794,7 @@

      Container is running without memory limit

      spec - containers[dex] + containers[argocd-notifications-controller] resources @@ -1795,7 +1805,7 @@

      Container is running without memory limit

    • - Line number: 695 + Line number: 872
    @@ -1812,7 +1822,7 @@

    Remediation

    @@ -1828,11 +1838,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 36] + [DocId: 38] input @@ -1842,7 +1852,7 @@

      Container is running without memory limit

      spec - initContainers[copyutil] + containers[redis] resources @@ -1853,7 +1863,7 @@

      Container is running without memory limit

    • - Line number: 729 + Line number: 953
    @@ -1870,7 +1880,7 @@

    Remediation

    @@ -1886,11 +1896,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 37] + [DocId: 39] input @@ -1900,7 +1910,7 @@

      Container is running without memory limit

      spec - containers[argocd-notifications-controller] + initContainers[copyutil] resources @@ -1911,7 +1921,7 @@

      Container is running without memory limit

    • - Line number: 785 + Line number: 1197
    @@ -1928,7 +1938,7 @@

    Remediation

    @@ -1944,11 +1954,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 38] + [DocId: 39] input @@ -1958,7 +1968,7 @@

      Container is running without memory limit

      spec - containers[redis] + containers[argocd-repo-server] resources @@ -1969,7 +1979,7 @@

      Container is running without memory limit

    • - Line number: 859 + Line number: 1009
    @@ -1986,7 +1996,7 @@

    Remediation

    @@ -2002,11 +2012,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 39] + [DocId: 40] input @@ -2016,7 +2026,7 @@

      Container is running without memory limit

      spec - initContainers[copyutil] + containers[argocd-server] resources @@ -2027,7 +2037,7 @@

      Container is running without memory limit

    • - Line number: 1086 + Line number: 1282
    @@ -2044,7 +2054,7 @@

    Remediation

    @@ -2060,11 +2070,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 39] + [DocId: 41] input @@ -2074,7 +2084,7 @@

      Container is running without memory limit

      spec - containers[argocd-repo-server] + containers[argocd-application-controller] resources @@ -2085,7 +2095,7 @@

      Container is running without memory limit

    • - Line number: 915 + Line number: 1592
    @@ -2102,12 +2112,12 @@

    Remediation

    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2118,7 +2128,399 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 35] + + input + + spec + + template + + spec + + containers[argocd-applicationset-controller] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 715 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 36] + + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 820 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 36] + + input + + spec + + template + + spec + + containers[dex] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 795 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 37] + + input + + spec + + template + + spec + + containers[argocd-notifications-controller] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 887 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 38] + + input + + spec + + template + + spec + + containers[redis] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 963 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 39] + + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 1204 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 39] + + input + + spec + + template + + spec + + containers[argocd-repo-server] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 1170 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11
    • Introduced through: @@ -2134,38 +2536,36 @@

      Container is running without memory limit

      containers[argocd-server] - resources - - limits + securityContext - memory + runAsUser
    • - Line number: 1171 + Line number: 1502

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2176,7 +2576,7 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: @@ -2192,33 +2592,31 @@

      Container is running without memory limit

      containers[argocd-application-controller] - resources - - limits + securityContext - memory + runAsUser
    • - Line number: 1468 + Line number: 1734

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    diff --git a/docs/snyk/v2.3.13/argocd-test.html b/docs/snyk/v2.7.17/argocd-test.html similarity index 61% rename from docs/snyk/v2.3.13/argocd-test.html rename to docs/snyk/v2.7.17/argocd-test.html index b1f378aa6e9e7..f130f831d96d1 100644 --- a/docs/snyk/v2.3.13/argocd-test.html +++ b/docs/snyk/v2.7.17/argocd-test.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,19 +456,20 @@

    Snyk test report

    -

    January 22nd 2023, 12:23:14 am

    +

    March 24th 2024, 12:21:51 am (UTC+00:00)

    Scanned the following paths:
      -
    • /argo-cd/argoproj/argo-cd/v2 (gomodules)
    • /argo-cd (yarn)
    • +
    • /argo-cd/argoproj/argo-cd/v2/go.mod (gomodules)
    • +
    • /argo-cd/ui/yarn.lock (yarn)
    -
    12 known vulnerabilities
    -
    113 vulnerable dependency paths
    -
    1467 dependencies
    +
    10 known vulnerabilities
    +
    106 vulnerable dependency paths
    +
    1755 dependencies
    @@ -477,620 +478,32 @@

    Snyk test report

    -

    Prototype Poisoning

    -
    - -
    - high severity -
    - -
    - -
      -
    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - qs -
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, superagent@3.8.3 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - superagent@3.8.3 - - qs@6.10.1 - - - -
    • -
    • - Introduced through: - argo-cd-ui@1.0.0 - - git-url-parse@11.1.2 - - git-up@4.0.5 - - parse-url@6.0.5 - - parse-path@4.0.3 - - qs@6.10.1 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    qs is a querystring parser that supports nesting and arrays, with a depth limit.

    -

    Affected versions of this package are vulnerable to Prototype Poisoning which allows attackers to cause a Node process to hang, processing an Array object whose prototype has been replaced by one with an excessive length value.

    -

    Note: In many typical Express use cases, an unauthenticated remote attacker can place the attack payload in the query string of the URL that is used to visit the application, such as a[__proto__]=b&a[__proto__]&a[length]=100000000.

    -

    Details

    -

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

    -

    Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

    -

    One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

    -

    When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

    -

    Two common types of DoS vulnerabilities:

    -
      -
    • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

      -
    • -
    • Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

      -
    • -
    -

    Remediation

    -

    Upgrade qs to version 6.2.4, 6.3.3, 6.4.1, 6.5.3, 6.6.1, 6.7.3, 6.8.3, 6.9.7, 6.10.3 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Regular Expression Denial of Service (ReDoS)

    -
    - -
    - high severity -
    - -
    - -
      -
    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - moment -
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, argo-ui@1.0.0 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - argo-ui@1.0.0 - - moment@2.29.1 - - - -
    • -
    • - Introduced through: - argo-cd-ui@1.0.0 - - argo-ui@1.0.0 - - antd@4.18.3 - - moment@2.29.1 - - - -
    • -
    • - Introduced through: - argo-cd-ui@1.0.0 - - argo-ui@1.0.0 - - moment-timezone@0.5.33 - - moment@2.29.1 - - - -
    • -
    • - Introduced through: - argo-cd-ui@1.0.0 - - argo-ui@1.0.0 - - antd@4.18.3 - - rc-picker@2.5.19 - - moment@2.29.1 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    moment is a lightweight JavaScript date library for parsing, validating, manipulating, and formatting dates.

    -

    Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) via the preprocessRFC2822() function in from-string.js, when processing a very long crafted string (over 10k characters).

    -

    PoC:

    -
    moment("(".repeat(500000))
    -        
    -

    Details

    -

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.

    -

    The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.

    -

    Let’s take the following regular expression as an example:

    -
    regex = /A(B|C+)+D/
    -        
    -

    This regular expression accomplishes the following:

    -
      -
    • A The string must start with the letter 'A'
    • -
    • (B|C+)+ The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the + matches one or more times). The + at the end of this section states that we can look for one or more matches of this section.
    • -
    • D Finally, we ensure this section of the string ends with a 'D'
    • -
    -

    The expression would match inputs such as ABBD, ABCCCCD, ABCBCCCD and ACCCCCD

    -

    It most cases, it doesn't take very long for a regex engine to find a match:

    -
    $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD")'
    -        0.04s user 0.01s system 95% cpu 0.052 total
    -        
    -        $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX")'
    -        1.79s user 0.02s system 99% cpu 1.812 total
    -        
    -

    The entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.

    -

    Most Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as catastrophic backtracking.

    -

    Let's look at how our expression runs into this problem, using a shorter string: "ACCCX". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:

    -
      -
    1. CCC
    2. -
    3. CC+C
    4. -
    5. C+CC
    6. -
    7. C+C+C.
    8. -
    -

    The engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use RegEx 101 debugger to see the engine has to take a total of 38 steps before it can determine the string doesn't match.

    -

    From there, the number of steps the engine must use to validate a string just continues to grow.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    StringNumber of C'sNumber of steps
    ACCCX338
    ACCCCX471
    ACCCCCX5136
    ACCCCCCCCCCCCCCX1465,553
    -

    By the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.

    -

    Remediation

    -

    Upgrade moment to version 2.29.4 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Server-side Request Forgery (SSRF)

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - parse-url -
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, git-url-parse@11.1.2 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - git-url-parse@11.1.2 - - git-up@4.0.5 - - parse-url@6.0.5 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    parse-url is an An advanced url parser supporting git urls too.

    -

    Affected versions of this package are vulnerable to Server-side Request Forgery (SSRF) due to improper detection of protocol, resource, and pathname fields. Exploiting this vulnerability results in bypassing protocol verification.

    -

    PoC:

    -
    import parseUrl from "parse-url";
    -        import fetch from 'node-fetch';
    -        var parsed=parseUrl("http://nnnn@localhost:808:/?id=xss")
    -        if(parsed.resource=="localhost"){
    -        console.log("internal network access is blocked")
    -        }
    -        else{
    -           const response = await fetch('http://'+parsed.resource+parsed.pathname);
    -                console.log(response)
    -         }
    -        
    -

    Remediation

    -

    Upgrade parse-url to version 8.1.0 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - parse-url -
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, git-url-parse@11.1.2 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - git-url-parse@11.1.2 - - git-up@4.0.5 - - parse-url@6.0.5 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    parse-url is an An advanced url parser supporting git urls too.

    -

    Affected versions of this package are vulnerable to Improper Input Validation due to incorrect parsing of URLs. This allows the attacker to craft a malformed URL which can lead to a phishing attack.

    -
    
    -        const parseUrl = require("parse-url");
    -        const Url = require("url");
    -        
    -        const express = require('express');
    -        const app = express();
    -        
    -        var url = "https://www.google.com:x@fakesite.com:x";
    -        parsed = parseUrl(url);
    -        console.log("[*]`parse-url` output: ")
    -        console.log(parsed);
    -        
    -        parsed2 = Url.parse(url);
    -        console.log("[*]`url` output: ")
    -        console.log(parsed2)
    -        
    -        app.get('/', (req, res) => {
    -            if (parsed.host == "www.google.com") {
    -                res.send("<a href=\'" + parsed2.href + "\'>CLICK ME!</a>")
    -            }
    -        })
    -        
    -        app.listen(8888,"0.0.0.0");
    -        
    -

    Remediation

    -

    Upgrade parse-url to version 8.1.0 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Information Exposure

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - node-fetch -
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, argo-ui@1.0.0 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - argo-ui@1.0.0 - - portable-fetch@3.0.0 - - node-fetch@1.7.3 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    node-fetch is a light-weight module that brings window.fetch to node.js

    -

    Affected versions of this package are vulnerable to Information Exposure when fetching a remote url with Cookie, if it get a Location response header, it will follow that url and try to fetch that url with provided cookie. This can lead to forwarding secure headers to 3th party.

    -

    Remediation

    -

    Upgrade node-fetch to version 2.6.7, 3.1.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Denial of Service

    +

    Regular Expression Denial of Service (ReDoS)

    -
    - medium severity +
    + high severity

    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - node-fetch + Manifest file: /argo-cd ui/yarn.lock
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, argo-ui@1.0.0 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - argo-ui@1.0.0 - - portable-fetch@3.0.0 - - node-fetch@1.7.3 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    node-fetch is a light-weight module that brings window.fetch to node.js

    -

    Affected versions of this package are vulnerable to Denial of Service. Node Fetch did not honor the size option after following a redirect, which means that when a content size was over the limit, a FetchError would never get thrown and the process would end without failure.

    -

    Remediation

    -

    Upgrade node-fetch to version 2.6.1, 3.0.0-beta.9 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Regular Expression Denial of Service (ReDoS)

    -
    - -
    - medium severity -
    - -
    - -
    • Package Manager: npm
    • Vulnerable module: - minimatch + semver
    • Introduced through: - argo-cd-ui@1.0.0 and minimatch@3.0.4 + argo-cd-ui@1.0.0, superagent@8.0.9 and others
    @@ -1104,20 +517,9 @@

    Detailed paths

    Introduced through: argo-cd-ui@1.0.0 - minimatch@3.0.4 - - - - -
  • - Introduced through: - argo-cd-ui@1.0.0 - - redoc@2.0.0-rc.64 + superagent@8.0.9 - @redocly/openapi-core@1.0.0-beta.82 - - minimatch@3.0.4 + semver@7.3.8 @@ -1129,8 +531,26 @@

    Detailed paths


    Overview

    -

    minimatch is a minimal matching utility.

    -

    Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) via the braceExpand function in minimatch.js.

    +

    semver is a semantic version parser used by npm.

    +

    Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) via the function new Range, when untrusted user data is provided as a range.

    +

    PoC

    +
    
    +        const semver = require('semver')
    +        const lengths_2 = [2000, 4000, 8000, 16000, 32000, 64000, 128000]
    +        
    +        console.log("n[+] Valid range - Test payloads")
    +        for (let i = 0; i =1.2.3' + ' '.repeat(lengths_2[i]) + '<1.3.0';
    +        const start = Date.now()
    +        semver.validRange(value)
    +        // semver.minVersion(value)
    +        // semver.maxSatisfying(["1.2.3"], value)
    +        // semver.minSatisfying(["1.2.3"], value)
    +        // new semver.Range(value, {})
    +        
    +        const end = Date.now();
    +        console.log('length=%d, time=%d ms', value.length, end - start);
    +        }
    +        

    Details

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.

    The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.

    @@ -1193,21 +613,27 @@

    Details

    By the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.

    Remediation

    -

    Upgrade minimatch to version 3.0.5 or higher.

    +

    Upgrade semver to version 5.7.2, 6.3.1, 7.5.2 or higher.

    References


  • -

    Denial of Service (DoS)

    +

    Infinite loop

    @@ -1217,19 +643,22 @@

    Denial of Service (DoS)


      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • Package Manager: golang
    • Vulnerable module: - golang.org/x/net/http2 + google.golang.org/protobuf/internal/encoding/json
    • Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0, k8s.io/client-go/rest@0.23.1 and others + github.com/argoproj/argo-cd/v2@0.0.0, github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 and others
    @@ -1243,20 +672,13 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - golang.org/x/net/http2@#491a49abca63 - - - - -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + github.com/golang/protobuf/jsonpb@1.4.2 - github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1265,20 +687,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/argoproj/pkg/grpc/http@#a4dd357b057e - golang.org/x/net/http2@#491a49abca63 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - github.com/soheilhy/cmux@0.1.5 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1287,11 +704,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/cache@0.23.1 + google.golang.org/grpc@1.58.3 + + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1300,24 +721,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.58.3 - golang.org/x/net/http2@#491a49abca63 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/dynamic@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1326,24 +740,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/transport/spdy@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.58.3 - golang.org/x/net/http2@#491a49abca63 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + google.golang.org/grpc/internal/transport@1.58.3 - github.com/argoproj/pkg/kubeclientmetrics@#36c59d8fafe0 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1352,24 +759,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/testing@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.3.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.58.3 - golang.org/x/net/http2@#491a49abca63 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/kubernetes@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1378,11 +778,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/azure@0.23.1 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + + google.golang.org/grpc@1.58.3 + + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1391,11 +797,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/gcp@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 + + google.golang.org/grpc@1.58.3 + + google.golang.org/grpc/internal/transport@1.58.3 + + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1404,11 +816,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/oidc@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/grpc/internal/transport@1.58.3 + + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1417,11 +835,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/watch@0.23.1 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.31.0 + + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1430,11 +854,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc@1.15.0 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.3.0 + + google.golang.org/grpc@1.58.3 - google.golang.org/grpc/internal/transport@1.15.0 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1443,13 +873,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a + + google.golang.org/grpc@1.58.3 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1458,13 +892,19 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/listers/core/v1@0.23.1 + google.golang.org/grpc/reflection@1.58.3 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.58.3 + + google.golang.org/grpc@1.58.3 + + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/tools/cache@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1473,13 +913,19 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#91deed20b998 + google.golang.org/grpc/health@1.58.3 - k8s.io/client-go/tools/cache@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/grpc/internal/transport@1.58.3 + + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1488,28 +934,98 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/core/v1@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 + + google.golang.org/grpc@1.58.3 + + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/tools/cache@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/internal/encoding/json@1.31.0 -
  • + + + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Stack-based Buffer Overflow

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/client-go/tools/cache@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1518,13 +1034,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/clientcmd@0.23.1 + github.com/argoproj/pkg/grpc/http@#a4dd357b057e - k8s.io/client-go/tools/auth@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/client-go/rest@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1533,13 +1049,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#91deed20b998 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/tools/cache@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1548,13 +1064,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery/fake@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 - k8s.io/client-go/testing@0.23.1 + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.7.0 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1563,13 +1079,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/fake@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 + + google.golang.org/grpc@1.58.3 - k8s.io/client-go/testing@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1578,13 +1096,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/remotecommand@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 + + google.golang.org/grpc@1.58.3 - k8s.io/client-go/transport/spdy@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1593,13 +1113,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.3.0 + + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1608,13 +1130,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + + google.golang.org/grpc@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1623,13 +1147,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 - google.golang.org/grpc@1.15.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc/internal/transport@1.15.0 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1638,13 +1164,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.3.0 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - google.golang.org/grpc@1.15.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc/internal/transport@1.15.0 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1653,13 +1181,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.31.0 - google.golang.org/grpc@1.15.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc/internal/transport@1.15.0 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1668,13 +1198,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/health/grpc_health_v1@1.15.0 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.3.0 + + google.golang.org/grpc@1.58.3 - google.golang.org/grpc@1.15.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc/internal/transport@1.15.0 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1683,13 +1215,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 + github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a + + google.golang.org/grpc@1.58.3 - google.golang.org/grpc@1.15.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc/internal/transport@1.15.0 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1698,13 +1232,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + google.golang.org/grpc@1.58.3 + + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc@1.15.0 + google.golang.org/grpc/internal/pretty@1.58.3 - google.golang.org/grpc/internal/transport@1.15.0 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1713,13 +1249,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/retry@1.3.0 + google.golang.org/grpc/reflection@1.58.3 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.58.3 - google.golang.org/grpc@1.15.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc/internal/transport@1.15.0 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1728,15 +1268,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/cache@0.6.2 + google.golang.org/grpc/health@1.58.3 + + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1745,15 +1287,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync@0.6.2 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 + + google.golang.org/grpc@1.58.3 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1762,15 +1306,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@0.6.2 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 + + google.golang.org/grpc@1.58.3 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1779,15 +1325,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#91deed20b998 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.3.0 + + google.golang.org/grpc@1.58.3 - k8s.io/client-go/tools/clientcmd@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/tools/auth@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1796,15 +1344,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/term@0.23.1 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + + google.golang.org/grpc@1.58.3 - k8s.io/client-go/tools/remotecommand@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/transport/spdy@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1813,15 +1363,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1830,15 +1382,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/api/rbac/v1@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 + + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1847,15 +1401,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1@0.23.1 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.31.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1864,15 +1420,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/api/core/v1@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.3.0 + + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1881,15 +1439,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/errors@0.23.1 + github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a + + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1898,15 +1458,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/reflection@1.58.3 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1915,15 +1479,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/dynamic@0.23.1 + google.golang.org/grpc/health@1.58.3 + + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1932,15 +1500,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/transport/spdy@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1949,32 +1523,95 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/pkg/kubeclientmetrics@#36c59d8fafe0 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 + + google.golang.org/grpc@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Stack-based Buffer Overflow when processing input that uses pathologically deep nesting.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/testing@0.23.1 - - k8s.io/client-go/rest@0.23.1 - - k8s.io/client-go/transport@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1983,15 +1620,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes@0.23.1 + github.com/argoproj/pkg/grpc/http@#a4dd357b057e - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/client-go/transport@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2000,15 +1635,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/azure@0.23.1 - - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2017,15 +1650,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/gcp@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 - k8s.io/client-go/rest@0.23.1 + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - k8s.io/client-go/transport@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.7.0 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2034,15 +1665,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/oidc@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2051,15 +1682,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/health@1.15.0 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 - google.golang.org/grpc/health/grpc_health_v1@1.15.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc@1.15.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc/internal/transport@1.15.0 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2068,15 +1699,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/reflection@1.15.0 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.3.0 - google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.15.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc@1.15.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc/internal/transport@1.15.0 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2085,17 +1716,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/health@0.6.2 - - github.com/argoproj/gitops-engine/pkg/utils/kube@0.6.2 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2104,17 +1733,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/common@0.6.2 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 - github.com/argoproj/gitops-engine/pkg/utils/kube@0.6.2 + google.golang.org/grpc@1.58.3 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2123,17 +1750,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/envtest@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane@0.11.0 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/client-go/tools/clientcmd@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/tools/auth@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2142,17 +1767,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.31.0 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2161,17 +1784,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/common@0.6.2 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.3.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2180,17 +1801,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/hook@0.6.2 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2199,17 +1818,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/resource@0.6.2 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2218,17 +1835,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/ignore@0.6.2 + google.golang.org/grpc/reflection@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2237,17 +1854,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/syncwaves@0.6.2 + google.golang.org/grpc/health@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2256,17 +1873,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/testing@0.6.2 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2275,17 +1892,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/cache@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 - k8s.io/client-go/tools/pager@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2294,17 +1911,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/resource@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.3.0 - k8s.io/api/core/v1@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2313,17 +1930,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/health@0.6.2 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - k8s.io/kubectl/pkg/util/podutils@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2332,17 +1949,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/portforward@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.11.1 - k8s.io/api/core/v1@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2351,17 +1968,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery/fake@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/client-go/testing@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2370,17 +1987,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/fake@0.23.1 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.31.0 - k8s.io/client-go/testing@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2389,17 +2006,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/remotecommand@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.3.0 - k8s.io/client-go/transport/spdy@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2408,17 +2025,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.3.0 + github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a - github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 + google.golang.org/grpc@1.58.3 - github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc@1.15.0 + google.golang.org/grpc/internal/pretty@1.58.3 - google.golang.org/grpc/internal/transport@1.15.0 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2427,19 +2044,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/cache@0.6.2 + google.golang.org/grpc/reflection@1.58.3 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.58.3 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2448,19 +2065,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync@0.6.2 + google.golang.org/grpc/health@1.58.3 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2469,19 +2086,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@0.6.2 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 - k8s.io/client-go/discovery@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2490,40 +2109,94 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/runtime/serializer@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 + + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#491a49abca63 + google.golang.org/protobuf/encoding/protojson@1.31.0
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Authentication Bypass by Capture-replay

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/crypto/ssh +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@0.0.0 and golang.org/x/crypto/ssh@0.16.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/listers/core/v1@0.23.1 - - k8s.io/client-go/tools/cache@0.23.1 - - k8s.io/client-go/tools/pager@0.23.1 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 - - k8s.io/apimachinery/pkg/watch@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2532,19 +2205,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#91deed20b998 + golang.org/x/crypto/ssh/knownhosts@0.16.0 - k8s.io/client-go/tools/cache@0.23.1 - - k8s.io/client-go/tools/pager@0.23.1 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 - - k8s.io/apimachinery/pkg/watch@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2553,19 +2216,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/core/v1@0.23.1 - - k8s.io/client-go/tools/cache@0.23.1 - - k8s.io/client-go/tools/pager@0.23.1 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/watch@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2574,19 +2227,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers@0.23.1 - - k8s.io/client-go/tools/cache@0.23.1 - - k8s.io/client-go/tools/pager@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/skeema/knownhosts@1.2.1 - k8s.io/apimachinery/pkg/watch@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2595,19 +2240,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#91deed20b998 - - k8s.io/client-go/tools/cache@0.23.1 - - k8s.io/client-go/tools/pager@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/watch@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2616,19 +2253,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/term@0.23.1 - - k8s.io/client-go/tools/remotecommand@0.23.1 - - k8s.io/client-go/transport/spdy@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/rest@0.23.1 + github.com/skeema/knownhosts@1.2.1 - k8s.io/client-go/transport@0.23.1 + golang.org/x/crypto/ssh/knownhosts@0.16.0 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2637,21 +2268,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/hook@0.6.2 - - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@0.6.2 - - github.com/argoproj/gitops-engine/pkg/sync/common@0.6.2 - - github.com/argoproj/gitops-engine/pkg/utils/kube@0.6.2 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/discovery@0.23.1 + github.com/skeema/knownhosts@1.2.1 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2660,21 +2283,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/syncwaves@0.6.2 - - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@0.6.2 - - github.com/argoproj/gitops-engine/pkg/sync/common@0.6.2 - - github.com/argoproj/gitops-engine/pkg/utils/kube@0.6.2 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + github.com/xanzy/ssh-agent@0.3.3 - k8s.io/client-go/discovery@0.23.1 + golang.org/x/crypto/ssh/agent@0.16.0 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2683,21 +2298,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/scheme@0.23.1 - - k8s.io/apimachinery/pkg/runtime/serializer@0.23.1 - - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.23.1 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + github.com/go-git/go-git/v5@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/apimachinery/pkg/watch@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2706,21 +2313,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/clientcmd@0.23.1 - - k8s.io/client-go/tools/clientcmd/api/latest@0.23.1 - - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.23.1 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/watch@0.23.1 + github.com/skeema/knownhosts@1.2.1 - k8s.io/apimachinery/pkg/util/net@0.23.1 + golang.org/x/crypto/ssh/knownhosts@0.16.0 - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2729,23 +2330,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/ignore@0.6.2 - - github.com/argoproj/gitops-engine/pkg/sync/hook@0.6.2 - - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@0.6.2 - - github.com/argoproj/gitops-engine/pkg/sync/common@0.6.2 - - github.com/argoproj/gitops-engine/pkg/utils/kube@0.6.2 + github.com/go-git/go-git/v5@5.11.0 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/client-go/discovery@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/rest@0.23.1 + github.com/skeema/knownhosts@1.2.1 - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2754,48 +2347,34 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/diff@0.6.2 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/client-go/kubernetes/scheme@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/runtime/serializer@0.23.1 + github.com/xanzy/ssh-agent@0.3.3 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.23.1 + golang.org/x/crypto/ssh/agent@0.16.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 - - k8s.io/apimachinery/pkg/watch@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0
    • Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - sigs.k8s.io/controller-runtime/pkg/envtest@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/webhook/conversion@0.11.0 - - k8s.io/apimachinery/pkg/runtime/serializer@0.23.1 + github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.23.1 + github.com/go-git/go-git/v5@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/watch@0.23.1 + github.com/skeema/knownhosts@1.2.1 - k8s.io/apimachinery/pkg/util/net@0.23.1 + golang.org/x/crypto/ssh/knownhosts@0.16.0 - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2804,23 +2383,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#91deed20b998 - - k8s.io/client-go/tools/clientcmd@0.23.1 - - k8s.io/client-go/tools/clientcmd/api/latest@0.23.1 - - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.23.1 + github.com/go-git/go-git/v5@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/watch@0.23.1 + github.com/xanzy/ssh-agent@0.3.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + golang.org/x/crypto/ssh/agent@0.16.0 - golang.org/x/net/http2@#491a49abca63 + golang.org/x/crypto/ssh@0.16.0 @@ -2832,39 +2405,49 @@

      Detailed paths


      Overview

      -

      golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

      -

      Affected versions of this package are vulnerable to Denial of Service (DoS) due to improper checks and limitations for the number of entries in the cache, which can allow an attacker to consume unbounded amounts of memory by sending a small number of very large keys.

      -

      Details

      -

      Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

      -

      Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

      -

      One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

      -

      When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

      -

      Two common types of DoS vulnerabilities:

      -
        -
      • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

        +

        golang.org/x/crypto/ssh is a SSH client and server

        +

        Affected versions of this package are vulnerable to Authentication Bypass by Capture-replay during the establishment of the secure channel. An attacker can manipulate handshake sequence numbers to delete messages sent immediately after the channel is established.

        +

        Note:

        +
          +
        1. Sequence numbers are only validated once the channel is established and arbitrary messages are allowed during the handshake, allowing them to manipulate the sequence numbers.

        2. -
        3. Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

          +
        4. The potential consequences of the general Terrapin attack are dependent on the messages exchanged after the handshake concludes. If you are using a custom SSH service and do not resort to the authentication protocol, you should check that dropping the first few messages of a connection does not yield security risks.

        5. -
      + +

      Impact:

      +

      While cryptographically novel, there is no discernable impact on the integrity of SSH traffic beyond giving the attacker the ability to delete the message that enables some features related to keystroke timing obfuscation. To successfully carry out the exploitation, the connection needs to be protected using either the ChaCha20-Poly1305 or CBC with Encrypt-then-MAC encryption methods. The attacker must also be able to intercept and modify the connection's traffic.

      +

      Workaround

      +

      Temporarily disable the affected chacha20-poly1305@openssh.com encryption and *-etm@openssh.com MAC algorithms in the affected configuration, and use unaffected algorithms like AES-GCM instead.

      Remediation

      -

      Upgrade golang.org/x/net/http2 to version 0.4.0 or higher.

      +

      Upgrade golang.org/x/crypto/ssh to version 0.17.0 or higher.

      References


    -

    Improper Input Validation

    +

    MPL-2.0 license

    @@ -2874,19 +2457,22 @@

    Improper Input Validation


      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • Package Manager: golang
    • - Vulnerable module: + Module: - go.mongodb.org/mongo-driver/bson/bsonrw + github.com/r3labs/diff
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/r3labs/diff@1.1.0 - github.com/argoproj/argo-cd/v2@0.0.0, github.com/go-openapi/runtime/middleware@0.19.4 and others
    @@ -2900,34 +2486,69 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/go-openapi/runtime/middleware@0.19.4 - - github.com/go-openapi/validate@0.19.5 - - github.com/go-openapi/strfmt@0.19.3 - - go.mongodb.org/mongo-driver/bson@1.1.2 - - go.mongodb.org/mongo-driver/bson/bsonrw@1.1.2 + github.com/r3labs/diff@1.1.0 + + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-version +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, code.gitea.io/sdk/gitea@0.15.1 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/go-openapi/runtime/middleware@0.19.4 - - github.com/go-openapi/validate@0.19.5 - - github.com/go-openapi/strfmt@0.19.3 - - go.mongodb.org/mongo-driver/bson@1.1.2 - - go.mongodb.org/mongo-driver/bson/bsoncodec@1.1.2 + code.gitea.io/sdk/gitea@0.15.1 - go.mongodb.org/mongo-driver/bson/bsonrw@1.1.2 + github.com/hashicorp/go-version@1.2.1 @@ -2938,27 +2559,17 @@

      Detailed paths


      -

      Overview

      -

      go.mongodb.org/mongo-driver/bson/bsonrw is a The MongoDB supported driver for Go.

      -

      Affected versions of this package are vulnerable to Improper Input Validation. Specific cstrings input may not be properly validated in the MongoDB Go Driver when marshalling Go objects into BSON. A malicious user could use a Go object with specific string to potentially inject additional fields into marshalled documents.

      -

      Remediation

      -

      Upgrade go.mongodb.org/mongo-driver/bson/bsonrw to version 1.5.1 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Insecure Randomness

    +

    MPL-2.0 license

    @@ -2968,19 +2579,22 @@

    Insecure Randomness


      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/Masterminds/goutils + github.com/hashicorp/go-retryablehttp
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/hashicorp/go-retryablehttp@0.7.0 - github.com/argoproj/argo-cd/v2@0.0.0, github.com/argoproj/notifications-engine/pkg/api@#91deed20b998 and others
    @@ -2994,13 +2608,61 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#91deed20b998 + github.com/hashicorp/go-retryablehttp@0.7.0 + + + + +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/xanzy/go-gitlab@0.60.0 + + github.com/hashicorp/go-retryablehttp@0.7.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#f754726f03da + + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.0 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#f754726f03da - github.com/argoproj/notifications-engine/pkg/templates@#91deed20b998 + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da - github.com/Masterminds/sprig@2.22.0 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-retryablehttp@0.7.0 @@ -3009,15 +2671,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#91deed20b998 + github.com/argoproj/notifications-engine/pkg/api@#f754726f03da - github.com/argoproj/notifications-engine/pkg/api@#91deed20b998 + github.com/argoproj/notifications-engine/pkg/subscriptions@#f754726f03da - github.com/argoproj/notifications-engine/pkg/templates@#91deed20b998 + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da - github.com/Masterminds/sprig@2.22.0 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-retryablehttp@0.7.0 @@ -3026,15 +2688,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#91deed20b998 + github.com/argoproj/notifications-engine/pkg/controller@#f754726f03da - github.com/argoproj/notifications-engine/pkg/api@#91deed20b998 + github.com/argoproj/notifications-engine/pkg/subscriptions@#f754726f03da - github.com/argoproj/notifications-engine/pkg/templates@#91deed20b998 + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da - github.com/Masterminds/sprig@2.22.0 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-retryablehttp@0.7.0 @@ -3045,25 +2707,17 @@

    Detailed paths


    -

    Overview

    -

    github.com/masterminds/goutils is a provides users with utility functions to manipulate strings in various ways.

    -

    Affected versions of this package are vulnerable to Insecure Randomness via the RandomAlphaNumeric(int) and CryptoRandomAlphaNumeric(int) functions. Small values of int in the functions above will return a smaller subset of results than they should. For example, RandomAlphaNumeric(1) would always return a digit in the 0-9 range, while RandomAlphaNumeric(4) return around ~7 million of the ~13M possible permutations.

    -

    Remediation

    -

    Upgrade github.com/masterminds/goutils to version 1.1.1 or higher.

    -

    References

    - +

    MPL-2.0 license


  • -

    Insecure Randomness

    +

    MPL-2.0 license

    @@ -3073,19 +2727,22 @@

    Insecure Randomness


      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/Masterminds/goutils + github.com/hashicorp/go-cleanhttp
    • Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0, github.com/argoproj/notifications-engine/pkg/api@#91deed20b998 and others + github.com/argoproj/argo-cd/v2@0.0.0, github.com/hashicorp/go-retryablehttp@0.7.0 and others
    @@ -3099,13 +2756,82 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#91deed20b998 + github.com/hashicorp/go-retryablehttp@0.7.0 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + + +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/xanzy/go-gitlab@0.60.0 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/xanzy/go-gitlab@0.60.0 + + github.com/hashicorp/go-retryablehttp@0.7.0 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.0 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#f754726f03da + + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.0 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#f754726f03da + + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da - github.com/argoproj/notifications-engine/pkg/templates@#91deed20b998 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/sprig@2.22.0 + github.com/hashicorp/go-retryablehttp@0.7.0 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3114,15 +2840,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#91deed20b998 + github.com/argoproj/notifications-engine/pkg/api@#f754726f03da - github.com/argoproj/notifications-engine/pkg/api@#91deed20b998 + github.com/argoproj/notifications-engine/pkg/subscriptions@#f754726f03da - github.com/argoproj/notifications-engine/pkg/templates@#91deed20b998 + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da - github.com/Masterminds/sprig@2.22.0 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-retryablehttp@0.7.0 + + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3131,15 +2859,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#91deed20b998 + github.com/argoproj/notifications-engine/pkg/controller@#f754726f03da + + github.com/argoproj/notifications-engine/pkg/subscriptions@#f754726f03da - github.com/argoproj/notifications-engine/pkg/api@#91deed20b998 + github.com/argoproj/notifications-engine/pkg/services@#f754726f03da - github.com/argoproj/notifications-engine/pkg/templates@#91deed20b998 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/sprig@2.22.0 + github.com/hashicorp/go-retryablehttp@0.7.0 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3150,25 +2880,17 @@

    Detailed paths


    -

    Overview

    -

    github.com/masterminds/goutils is a provides users with utility functions to manipulate strings in various ways.

    -

    Affected versions of this package are vulnerable to Insecure Randomness when randomly-generated alphanumeric strings contain significantly less entropy than expected, the RandomAlphaNumeric and CryptoRandomAlphaNumeric functions always return strings containing at least one digit from 0 to 9. This significantly reduces the amount of entropy in short strings generated by these functions.

    -

    Remediation

    -

    Upgrade github.com/masterminds/goutils to version 1.1.1 or higher.

    -

    References

    - +

    MPL-2.0 license


  • -

    Regular Expression Denial of Service (ReDoS)

    +

    MPL-2.0 license

    @@ -3179,18 +2901,21 @@

    Regular Expression Denial of Service (ReDoS)

    • - Package Manager: npm + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod
    • - Vulnerable module: + Package Manager: golang +
    • +
    • + Module: - cookiejar + github.com/gosimple/slug
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/gosimple/slug@1.13.1 - argo-cd-ui@1.0.0, superagent@3.8.3 and others
    @@ -3202,11 +2927,9 @@

    Detailed paths

    • Introduced through: - argo-cd-ui@1.0.0 - - superagent@3.8.3 + github.com/argoproj/argo-cd/v2@0.0.0 - cookiejar@2.1.2 + github.com/gosimple/slug@1.13.1 @@ -3217,92 +2940,12 @@

      Detailed paths


      -

      Overview

      -

      Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) via the Cookie.parse function, which uses an insecure regular expression.

      -

      PoC

      -
      const { CookieJar } = require("cookiejar");
      -        
      -        const jar = new CookieJar();
      -        
      -        const start = performance.now();
      -        const attack = "a" + "t".repeat(50_000);
      -        jar.setCookie(attack);
      -        console.log(`CookieJar.setCookie(): ${performance.now() - start}`);
      -        
      -

      Details

      -

      Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.

      -

      The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.

      -

      Let’s take the following regular expression as an example:

      -
      regex = /A(B|C+)+D/
      -        
      -

      This regular expression accomplishes the following:

      -
        -
      • A The string must start with the letter 'A'
      • -
      • (B|C+)+ The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the + matches one or more times). The + at the end of this section states that we can look for one or more matches of this section.
      • -
      • D Finally, we ensure this section of the string ends with a 'D'
      • -
      -

      The expression would match inputs such as ABBD, ABCCCCD, ABCBCCCD and ACCCCCD

      -

      It most cases, it doesn't take very long for a regex engine to find a match:

      -
      $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD")'
      -        0.04s user 0.01s system 95% cpu 0.052 total
      -        
      -        $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX")'
      -        1.79s user 0.02s system 99% cpu 1.812 total
      -        
      -

      The entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.

      -

      Most Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as catastrophic backtracking.

      -

      Let's look at how our expression runs into this problem, using a shorter string: "ACCCX". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:

      -
        -
      1. CCC
      2. -
      3. CC+C
      4. -
      5. C+CC
      6. -
      7. C+C+C.
      8. -
      -

      The engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use RegEx 101 debugger to see the engine has to take a total of 38 steps before it can determine the string doesn't match.

      -

      From there, the number of steps the engine must use to validate a string just continues to grow.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      StringNumber of C'sNumber of steps
      ACCCX338
      ACCCCX471
      ACCCCCX5136
      ACCCCCCCCCCCCCCX1465,553
      -

      By the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.

      -

      Remediation

      -

      Upgrade cookiejar to version 2.1.4 or higher.

      -

      References

      - +

      MPL-2.0 license


    diff --git a/docs/snyk/v2.7.17/ghcr.io_dexidp_dex_v2.37.0.html b/docs/snyk/v2.7.17/ghcr.io_dexidp_dex_v2.37.0.html new file mode 100644 index 0000000000000..2bc1adb34dcef --- /dev/null +++ b/docs/snyk/v2.7.17/ghcr.io_dexidp_dex_v2.37.0.html @@ -0,0 +1,4337 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    March 24th 2024, 12:21:56 am (UTC+00:00)

    +
    +
    + Scanned the following paths: +
      +
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (apk)
    • +
    • ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3//usr/local/bin/gomplate (gomodules)
    • +
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex//usr/local/bin/docker-entrypoint (gomodules)
    • +
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex//usr/local/bin/dex (gomodules)
    • +
    +
    + +
    +
    42 known vulnerabilities
    +
    121 vulnerable dependency paths
    +
    786 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    Path Traversal

    +
    + +
    + critical severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-git/go-git/v5 +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/go-git/go-git/v5@v5.4.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/go-git/go-git/v5@v5.4.2 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Path Traversal via malicious server replies. An attacker can create and amend files across the filesystem and potentially achieve remote code execution by sending crafted responses to the client.

    +

    Notes:

    +
      +
    1. This is only exploitable if the client is using ChrootOS, which is the default for certain functions such as PlainClone.

      +
    2. +
    3. Applications using BoundOS or in-memory filesystems are not affected by this issue.

      +
    4. +
    5. Users running versions of go-git from v4 and above are recommended to upgrade to v5.11 in order to mitigate this vulnerability.

      +
    6. +
    +

    Workaround

    +

    This vulnerability can be mitigated by limiting the client's use to trustworthy Git servers.

    +

    Remediation

    +

    Upgrade github.com/go-git/go-git/v5 to version 5.11.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + critical severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + busybox/busybox +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and busybox/busybox@1.36.1-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/busybox@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + alpine-baselayout/alpine-baselayout@3.4.3-r1 + + busybox/busybox-binsh@1.36.1-r0 + + busybox/busybox@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/busybox-binsh@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + alpine-baselayout/alpine-baselayout@3.4.3-r1 + + busybox/busybox-binsh@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream busybox package and not the busybox package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    There is a stack overflow vulnerability in ash.c:6030 in busybox before 1.35. In the environment of Internet of Vehicles, this vulnerability can be executed from command to arbitrary code execution.

    +

    Remediation

    +

    Upgrade Alpine:3.18 busybox to version 1.36.1-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-5363

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: A bug has been identified in the processing of key and + initialisation vector (IV) lengths. This can lead to potential truncation + or overruns during the initialisation of some symmetric ciphers.

    +

    Impact summary: A truncation in the IV can result in non-uniqueness, + which could result in loss of confidentiality for some cipher modes.

    +

    When calling EVP_EncryptInit_ex2(), EVP_DecryptInit_ex2() or + EVP_CipherInit_ex2() the provided OSSL_PARAM array is processed after + the key and IV have been established. Any alterations to the key length, + via the "keylen" parameter or the IV length, via the "ivlen" parameter, + within the OSSL_PARAM array will not take effect as intended, potentially + causing truncation or overreading of these values. The following ciphers + and cipher modes are impacted: RC2, RC4, RC5, CCM, GCM and OCB.

    +

    For the CCM, GCM and OCB cipher modes, truncation of the IV can result in + loss of confidentiality. For example, when following NIST's SP 800-38D + section 8.2.1 guidance for constructing a deterministic IV for AES in + GCM mode, truncation of the counter portion could lead to IV reuse.

    +

    Both truncations and overruns of the key and overruns of the IV will + produce incorrect results and could, in some cases, trigger a memory + exception. However, these issues are not currently assessed as security + critical.

    +

    Changing the key and/or IV lengths is not considered to be a common operation + and the vulnerable API was recently introduced. Furthermore it is likely that + application developers will have spotted this problem during testing since + decryption would fail unless both peers in the communication were similarly + vulnerable. For these reasons we expect the probability of an application being + vulnerable to this to be quite low. However if an application is vulnerable then + this issue is considered very serious. For these reasons we have assessed this + issue as Moderate severity overall.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this because + the issue lies outside of the FIPS provider boundary.

    +

    OpenSSL 3.1 and 3.0 are vulnerable to this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/grpc +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/grpc@v1.46.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/grpc@v1.46.2 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/grpc@v1.56.1 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    google.golang.org/grpc is a Go implementation of gRPC

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) in the implementation of the HTTP/2 protocol. An attacker can cause a denial of service (including via DDoS) by rapidly resetting many streams through request cancellation.

    +

    Remediation

    +

    Upgrade google.golang.org/grpc to version 1.56.3, 1.57.1, 1.58.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and golang.org/x/net/http2@v0.7.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + golang.org/x/net/http2@v0.7.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + golang.org/x/net/http2@v0.11.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) in the implementation of the HTTP/2 protocol. An attacker can cause a denial of service (including via DDoS) by rapidly resetting many streams through request cancellation.

    +

    Remediation

    +

    Upgrade golang.org/x/net/http2 to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Heap-based Buffer Overflow

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/mattn/go-sqlite3 +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/mattn/go-sqlite3@v1.14.17 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/mattn/go-sqlite3@v1.14.17 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Heap-based Buffer Overflow via the sessionReadRecord function in the ext/session/sqlite3session.c file. An attacker can cause a program crash or execute arbitrary code by manipulating the input to trigger a heap-based buffer overflow.

    +

    Remediation

    +

    Upgrade github.com/mattn/go-sqlite3 to version 1.14.18 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-jose/go-jose/v3 +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/go-jose/go-jose/v3@v3.0.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/go-jose/go-jose/v3@v3.0.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) when decrypting JWE inputs. An attacker can cause a denial-of-service by providing a PBES2 encrypted JWE blob with a very large p2c value.

    +

    Details

    +

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

    +

    Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

    +

    One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

    +

    When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

    +

    Two common types of DoS vulnerabilities:

    +
      +
    • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

      +
    • +
    • Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

      +
    • +
    +

    Remediation

    +

    Upgrade github.com/go-jose/go-jose/v3 to version 3.0.1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Authentication

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The AES-SIV cipher implementation contains a bug that causes + it to ignore empty associated data entries which are unauthenticated as + a consequence.

    +

    Impact summary: Applications that use the AES-SIV algorithm and want to + authenticate empty data entries as associated data can be mislead by removing + adding or reordering such empty entries as these are ignored by the OpenSSL + implementation. We are currently unaware of any such applications.

    +

    The AES-SIV algorithm allows for authentication of multiple associated + data entries along with the encryption. To authenticate empty data the + application has to call EVP_EncryptUpdate() (or EVP_CipherUpdate()) with + NULL pointer as the output buffer and 0 as the input buffer length. + The AES-SIV implementation in OpenSSL just returns success for such a call + instead of performing the associated data authentication operation. + The empty data thus will not be authenticated.

    +

    As this issue does not affect non-empty associated data authentication and + we expect it to be rare for an application to use empty associated data + entries this is qualified as Low severity issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.1-r2 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Inefficient Regular Expression Complexity

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Checking excessively long DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_check(), DH_check_ex() + or EVP_PKEY_param_check() to check a DH key or DH parameters may experience long + delays. Where the key or parameters that are being checked have been obtained + from an untrusted source this may lead to a Denial of Service.

    +

    The function DH_check() performs various checks on DH parameters. One of those + checks confirms that the modulus ('p' parameter) is not too large. Trying to use + a very large modulus is slow and OpenSSL will not normally use a modulus which + is over 10,000 bits in length.

    +

    However the DH_check() function checks numerous aspects of the key or parameters + that have been supplied. Some of those checks use the supplied modulus value + even if it has already been found to be too large.

    +

    An application that calls DH_check() and supplies a key or parameters obtained + from an untrusted source could be vulernable to a Denial of Service attack.

    +

    The function DH_check() is itself called by a number of other OpenSSL functions. + An application calling any of those other functions may similarly be affected. + The other functions affected by this are DH_check_ex() and + EVP_PKEY_param_check().

    +

    Also vulnerable are the OpenSSL dhparam and pkeyparam command line applications + when using the '-check' option.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue. + The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.1-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Excessive Iteration

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Checking excessively long DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_check(), DH_check_ex() + or EVP_PKEY_param_check() to check a DH key or DH parameters may experience long + delays. Where the key or parameters that are being checked have been obtained + from an untrusted source this may lead to a Denial of Service.

    +

    The function DH_check() performs various checks on DH parameters. After fixing + CVE-2023-3446 it was discovered that a large q parameter value can also trigger + an overly long computation during some of these checks. A correct q value, + if present, cannot be larger than the modulus p parameter, thus it is + unnecessary to perform these checks if q is larger than p.

    +

    An application that calls DH_check() and supplies a key or parameters obtained + from an untrusted source could be vulnerable to a Denial of Service attack.

    +

    The function DH_check() is itself called by a number of other OpenSSL functions. + An application calling any of those other functions may similarly be affected. + The other functions affected by this are DH_check_ex() and + EVP_PKEY_param_check().

    +

    Also vulnerable are the OpenSSL dhparam and pkeyparam command line applications + when using the "-check" option.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.2-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Check for Unusual or Exceptional Conditions

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Generating excessively long X9.42 DH keys or checking + excessively long X9.42 DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_generate_key() to + generate an X9.42 DH key may experience long delays. Likewise, applications + that use DH_check_pub_key(), DH_check_pub_key_ex() or EVP_PKEY_public_check() + to check an X9.42 DH key or X9.42 DH parameters may experience long delays. + Where the key or parameters that are being checked have been obtained from + an untrusted source this may lead to a Denial of Service.

    +

    While DH_check() performs all the necessary checks (as of CVE-2023-3817), + DH_check_pub_key() doesn't make any of these checks, and is therefore + vulnerable for excessively large P and Q parameters.

    +

    Likewise, while DH_generate_key() performs a check for an excessively large + P, it doesn't check for an excessively large Q.

    +

    An application that calls DH_generate_key() or DH_check_pub_key() and + supplies a key or parameters obtained from an untrusted source could be + vulnerable to a Denial of Service attack.

    +

    DH_generate_key() and DH_check_pub_key() are also called by a number of + other OpenSSL functions. An application calling any of those other + functions may similarly be affected. The other functions affected by this + are DH_check_pub_key_ex(), EVP_PKEY_public_check(), and EVP_PKEY_generate().

    +

    Also vulnerable are the OpenSSL pkey command line application when using the + "-pubcheck" option, as well as the OpenSSL genpkey command line application.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

    +

    Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

    +

    The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

    +

    The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

    +

    The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-0727

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

    +

    Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

    +

    A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

    +

    OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

    +

    We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

    +

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r5 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/internal/encoding/json +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/internal/encoding/json@v1.28.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/protobuf/internal/encoding/json@v1.28.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/protobuf/internal/encoding/json@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Stack-based Buffer Overflow

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/encoding/protojson@v1.28.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/protobuf/encoding/protojson@v1.28.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Stack-based Buffer Overflow when processing input that uses pathologically deep nesting.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/encoding/protojson@v1.28.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/protobuf/encoding/protojson@v1.28.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Allocation of Resources Without Limits or Throttling

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and golang.org/x/net/http2@v0.7.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + golang.org/x/net/http2@v0.7.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + golang.org/x/net/http2@v0.11.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

    +

    Affected versions of this package are vulnerable to Allocation of Resources Without Limits or Throttling when MaxConcurrentStreams handler goroutines running. A a handler is started until one of the existing handlers exits.

    +

    Note:

    +

    This issue is related to CVE-2023-44487

    +

    Remediation

    +

    Upgrade golang.org/x/net/http2 to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Cross-site Scripting (XSS)

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/html +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and golang.org/x/net/html@v0.11.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + golang.org/x/net/html@v0.11.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/net/html is a package that implements an HTML5-compliant tokenizer and parser.

    +

    Affected versions of this package are vulnerable to Cross-site Scripting (XSS) in the render1() function in render.go. Text nodes not in the HTML namespace are incorrectly literally rendered, causing text which should be escaped to not be.

    +

    Details

    +

    A cross-site scripting attack occurs when the attacker tricks a legitimate web-based application or site to accept a request as originating from a trusted source.

    +

    This is done by escaping the context of the web application; the web application then delivers that data to its users along with other trusted dynamic content, without validating it. The browser unknowingly executes malicious script on the client side (through client-side languages; usually JavaScript or HTML) in order to perform actions that are otherwise typically blocked by the browser’s Same Origin Policy.

    +

    Injecting malicious code is the most prevalent manner by which XSS is exploited; for this reason, escaping characters in order to prevent this manipulation is the top method for securing code against this vulnerability.

    +

    Escaping means that the application is coded to mark key characters, and particularly key characters included in user input, to prevent those characters from being interpreted in a dangerous context. For example, in HTML, < can be coded as &lt; and > can be coded as &gt; in order to be interpreted and displayed as themselves in text, while within the code itself, they are used for HTML tags. If malicious content is injected into an application that escapes special characters and that malicious content uses < and > as HTML tags, those characters are nonetheless not interpreted as HTML tags by the browser if they’ve been correctly escaped in the application code and in this way the attempted attack is diverted.

    +

    The most prominent use of XSS is to steal cookies (source: OWASP HttpOnly) and hijack user sessions, but XSS exploits have been used to expose sensitive information, enable access to privileged services and functionality and deliver malware.

    +

    Types of attacks

    +

    There are a few methods by which XSS can be manipulated:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeOriginDescription
    StoredServerThe malicious code is inserted in the application (usually as a link) by the attacker. The code is activated every time a user clicks the link.
    ReflectedServerThe attacker delivers a malicious link externally from the vulnerable web site application to a user. When clicked, malicious code is sent to the vulnerable web site, which reflects the attack back to the user’s browser.
    DOM-basedClientThe attacker forces the user’s browser to render a malicious page. The data in the page itself delivers the cross-site scripting data.
    MutatedThe attacker injects code that appears safe, but is then rewritten and modified by the browser, while parsing the markup. An example is rebalancing unclosed quotation marks or even adding quotation marks to unquoted parameters.
    +

    Affected environments

    +

    The following environments are susceptible to an XSS attack:

    +
      +
    • Web servers
    • +
    • Application servers
    • +
    • Web application environments
    • +
    +

    How to prevent

    +

    This section describes the top best practices designed to specifically protect your code:

    +
      +
    • Sanitize data input in an HTTP request before reflecting it back, ensuring all data is validated, filtered or escaped before echoing anything back to the user, such as the values of query parameters during searches.
    • +
    • Convert special characters such as ?, &, /, <, > and spaces to their respective HTML or URL encoded equivalents.
    • +
    • Give users the option to disable client-side scripts.
    • +
    • Redirect invalid requests.
    • +
    • Detect simultaneous logins, including those from two separate IP addresses, and invalidate those sessions.
    • +
    • Use and enforce a Content Security Policy (source: Wikipedia) to disable any features that might be manipulated for an XSS attack.
    • +
    • Read the documentation for any of the libraries referenced in your code to understand which elements allow for embedded HTML.
    • +
    +

    Remediation

    +

    Upgrade golang.org/x/net/html to version 0.13.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Authentication Bypass by Capture-replay

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/crypto/ssh +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and golang.org/x/crypto/ssh@v0.0.0-20220525230936-793ad666bf5e + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + golang.org/x/crypto/ssh@v0.0.0-20220525230936-793ad666bf5e + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/crypto/ssh is a SSH client and server

    +

    Affected versions of this package are vulnerable to Authentication Bypass by Capture-replay during the establishment of the secure channel. An attacker can manipulate handshake sequence numbers to delete messages sent immediately after the channel is established.

    +

    Note:

    +
      +
    1. Sequence numbers are only validated once the channel is established and arbitrary messages are allowed during the handshake, allowing them to manipulate the sequence numbers.

      +
    2. +
    3. The potential consequences of the general Terrapin attack are dependent on the messages exchanged after the handshake concludes. If you are using a custom SSH service and do not resort to the authentication protocol, you should check that dropping the first few messages of a connection does not yield security risks.

      +
    4. +
    +

    Impact:

    +

    While cryptographically novel, there is no discernable impact on the integrity of SSH traffic beyond giving the attacker the ability to delete the message that enables some features related to keystroke timing obfuscation. To successfully carry out the exploitation, the connection needs to be protected using either the ChaCha20-Poly1305 or CBC with Encrypt-then-MAC encryption methods. The attacker must also be able to intercept and modify the connection's traffic.

    +

    Workaround

    +

    Temporarily disable the affected chacha20-poly1305@openssh.com encryption and *-etm@openssh.com MAC algorithms in the affected configuration, and use unaffected algorithms like AES-GCM instead.

    +

    Remediation

    +

    Upgrade golang.org/x/crypto/ssh to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/vault/sdk/helper/certutil +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/vault/sdk/helper/certutil@v0.5.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/certutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/compressutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/consts@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/jsonutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/pluginutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/strutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/logical@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/physical@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/physical/inmem@v0.5.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/vault/api +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/vault/api@v1.6.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/api@v1.6.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/serf/coordinate +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/serf/coordinate@v0.9.7 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/serf/coordinate@v0.9.7 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/hcl/v2 +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/hashicorp/hcl/v2@v2.13.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/ext/customdecode@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/ext/tryfunc@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/gohcl@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/hclparse@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/hclsyntax@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/hclwrite@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/json@v2.13.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/hcl +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/hcl@v1.0.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl@v1.0.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/hcl/parser@v1.0.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/hcl/strconv@v1.0.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/hcl/token@v1.0.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/json/parser@v1.0.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/golang-lru/simplelru +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/golang-lru/simplelru@v0.5.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/golang-lru/simplelru@v0.5.4 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-version +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-version@v1.5.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-version@v1.5.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-sockaddr +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-sockaddr@v1.0.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-sockaddr@v1.0.2 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-sockaddr/template@v1.0.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-secure-stdlib/strutil +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-secure-stdlib/strutil@v0.1.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-secure-stdlib/strutil@v0.1.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-secure-stdlib/parseutil +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-secure-stdlib/parseutil@v0.1.5 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-secure-stdlib/parseutil@v0.1.5 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-secure-stdlib/mlock +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-secure-stdlib/mlock@v0.1.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-secure-stdlib/mlock@v0.1.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-rootcerts +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-rootcerts@v1.0.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-rootcerts@v1.0.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-retryablehttp +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-retryablehttp@v0.7.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-retryablehttp@v0.7.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-plugin +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-plugin@v1.4.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-plugin@v1.4.4 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-plugin/internal/plugin@v1.4.4 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-immutable-radix +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-immutable-radix@v1.3.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-immutable-radix@v1.3.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-cleanhttp +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-cleanhttp@v0.5.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-cleanhttp@v0.5.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/errwrap +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/errwrap@v1.1.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/errwrap@v1.1.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/consul/api +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/consul/api@v1.13.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/consul/api@v1.13.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/gosimple/slug +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/gosimple/slug@v1.12.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/gosimple/slug@v1.12.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/go-sql-driver/mysql +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/go-sql-driver/mysql@v1.7.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/go-sql-driver/mysql@v1.7.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    Improper Handling of Highly Compressed Data (Data Amplification)

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-jose/go-jose/v3 +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/go-jose/go-jose/v3@v3.0.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/go-jose/go-jose/v3@v3.0.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Improper Handling of Highly Compressed Data (Data Amplification). An attacker could send a JWE containing compressed data that, when decompressed by Decrypt or DecryptMulti, would use large amounts of memory and CPU.

    +

    Remediation

    +

    Upgrade github.com/go-jose/go-jose/v3 to version 3.0.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Uncontrolled Resource Consumption ('Resource Exhaustion')

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-git/go-git/v5/plumbing +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/go-git/go-git/v5/plumbing@v5.4.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/go-git/go-git/v5/plumbing@v5.4.2 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    github.com/go-git/go-git/v5/plumbing is a highly extensible git implementation library written in pure Go.

    +

    Affected versions of this package are vulnerable to Uncontrolled Resource Consumption ('Resource Exhaustion') via specially crafted responses from a Git server, which triggers resource exhaustion in clients.

    +

    Note + This is only exploitable if the client is not using the in-memory filesystem supported by the library.

    +

    Workaround

    +

    In cases where a bump to the latest version of go-git is not possible, we recommend limiting its use to only trust-worthy Git servers.

    +

    Details

    +

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

    +

    Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

    +

    One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

    +

    When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

    +

    Two common types of DoS vulnerabilities:

    +
      +
    • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

      +
    • +
    • Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

      +
    • +
    +

    Remediation

    +

    Upgrade github.com/go-git/go-git/v5/plumbing to version 5.11.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-6237

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    This vulnerability has not been analyzed by NVD yet.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r4 or higher.

    + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.7.17/haproxy_2.6.14-alpine.html b/docs/snyk/v2.7.17/haproxy_2.6.14-alpine.html new file mode 100644 index 0000000000000..4487d720d3a0c --- /dev/null +++ b/docs/snyk/v2.7.17/haproxy_2.6.14-alpine.html @@ -0,0 +1,1376 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    March 24th 2024, 12:22:00 am (UTC+00:00)

    +
    +
    + Scanned the following path: +
      +
    • haproxy:2.6.14-alpine (apk)
    • +
    +
    + +
    +
    5 known vulnerabilities
    +
    45 vulnerable dependency paths
    +
    18 dependencies
    +
    +
    +
    +
    +
    + + + + + + + +
    Project docker-image|haproxy
    Path haproxy:2.6.14-alpine
    Package Manager apk
    +
    +
    +
    +
    +

    CVE-2023-5363

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: A bug has been identified in the processing of key and + initialisation vector (IV) lengths. This can lead to potential truncation + or overruns during the initialisation of some symmetric ciphers.

    +

    Impact summary: A truncation in the IV can result in non-uniqueness, + which could result in loss of confidentiality for some cipher modes.

    +

    When calling EVP_EncryptInit_ex2(), EVP_DecryptInit_ex2() or + EVP_CipherInit_ex2() the provided OSSL_PARAM array is processed after + the key and IV have been established. Any alterations to the key length, + via the "keylen" parameter or the IV length, via the "ivlen" parameter, + within the OSSL_PARAM array will not take effect as intended, potentially + causing truncation or overreading of these values. The following ciphers + and cipher modes are impacted: RC2, RC4, RC5, CCM, GCM and OCB.

    +

    For the CCM, GCM and OCB cipher modes, truncation of the IV can result in + loss of confidentiality. For example, when following NIST's SP 800-38D + section 8.2.1 guidance for constructing a deterministic IV for AES in + GCM mode, truncation of the counter portion could lead to IV reuse.

    +

    Both truncations and overruns of the key and overruns of the IV will + produce incorrect results and could, in some cases, trigger a memory + exception. However, these issues are not currently assessed as security + critical.

    +

    Changing the key and/or IV lengths is not considered to be a common operation + and the vulnerable API was recently introduced. Furthermore it is likely that + application developers will have spotted this problem during testing since + decryption would fail unless both peers in the communication were similarly + vulnerable. For these reasons we expect the probability of an application being + vulnerable to this to be quite low. However if an application is vulnerable then + this issue is considered very serious. For these reasons we have assessed this + issue as Moderate severity overall.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this because + the issue lies outside of the FIPS provider boundary.

    +

    OpenSSL 3.1 and 3.0 are vulnerable to this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Check for Unusual or Exceptional Conditions

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Generating excessively long X9.42 DH keys or checking + excessively long X9.42 DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_generate_key() to + generate an X9.42 DH key may experience long delays. Likewise, applications + that use DH_check_pub_key(), DH_check_pub_key_ex() or EVP_PKEY_public_check() + to check an X9.42 DH key or X9.42 DH parameters may experience long delays. + Where the key or parameters that are being checked have been obtained from + an untrusted source this may lead to a Denial of Service.

    +

    While DH_check() performs all the necessary checks (as of CVE-2023-3817), + DH_check_pub_key() doesn't make any of these checks, and is therefore + vulnerable for excessively large P and Q parameters.

    +

    Likewise, while DH_generate_key() performs a check for an excessively large + P, it doesn't check for an excessively large Q.

    +

    An application that calls DH_generate_key() or DH_check_pub_key() and + supplies a key or parameters obtained from an untrusted source could be + vulnerable to a Denial of Service attack.

    +

    DH_generate_key() and DH_check_pub_key() are also called by a number of + other OpenSSL functions. An application calling any of those other + functions may similarly be affected. The other functions affected by this + are DH_check_pub_key_ex(), EVP_PKEY_public_check(), and EVP_PKEY_generate().

    +

    Also vulnerable are the OpenSSL pkey command line application when using the + "-pubcheck" option, as well as the OpenSSL genpkey command line application.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

    +

    Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

    +

    The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

    +

    The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

    +

    The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-0727

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

    +

    Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

    +

    A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

    +

    OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

    +

    We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

    +

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r5 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-6237

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    This vulnerability has not been analyzed by NVD yet.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r4 or higher.

    + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.3.13/quay.io_argoproj_argocd-applicationset_v0.4.1.html b/docs/snyk/v2.7.17/quay.io_argoproj_argocd_v2.7.17.html similarity index 51% rename from docs/snyk/v2.3.13/quay.io_argoproj_argocd-applicationset_v0.4.1.html rename to docs/snyk/v2.7.17/quay.io_argoproj_argocd_v2.7.17.html index c2fd5b45a92ef..88785b4be1777 100644 --- a/docs/snyk/v2.3.13/quay.io_argoproj_argocd-applicationset_v0.4.1.html +++ b/docs/snyk/v2.7.17/quay.io_argoproj_argocd_v2.7.17.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,37 +456,32 @@

    Snyk test report

    -

    January 22nd 2023, 12:23:34 am

    +

    March 24th 2024, 12:22:17 am (UTC+00:00)

    - Scanned the following path: + Scanned the following paths:
      -
    • quay.io/argoproj/argocd-applicationset:v0.4.1/argoproj/argocd-applicationset (deb)
    • +
    • quay.io/argoproj/argocd:v2.7.17/argoproj/argocd/Dockerfile (deb)
    • +
    • quay.io/argoproj/argocd:v2.7.17/argoproj/argo-cd/v2//usr/local/bin/argocd (gomodules)
    • +
    • quay.io/argoproj/argocd:v2.7.17/kustomize/kustomize/v5//usr/local/bin/kustomize (gomodules)
    • +
    • quay.io/argoproj/argocd:v2.7.17/helm/v3//usr/local/bin/helm (gomodules)
    • +
    • quay.io/argoproj/argocd:v2.7.17/git-lfs/git-lfs//usr/bin/git-lfs (gomodules)
    -
    71 known vulnerabilities
    -
    244 vulnerable dependency paths
    -
    161 dependencies
    +
    46 known vulnerabilities
    +
    224 vulnerable dependency paths
    +
    2070 dependencies
    -
    - - - - - - - -
    Project docker-image|quay.io/argoproj/argocd-applicationset
    Path quay.io/argoproj/argocd-applicationset:v0.4.1/argoproj/argocd-applicationset
    Package Manager deb
    Manifest Dockerfile
    -
    +
    -

    Loop with Unreachable Exit Condition ('Infinite Loop')

    +

    Denial of Service (DoS)

    @@ -497,17 +492,20 @@

    Loop with Unreachable Exit Condition ('Infinite Loo
    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/helm/v3 /usr/local/bin/helm +
    • +
    • + Package Manager: golang
    • Vulnerable module: - openssl/libssl1.1 + golang.org/x/net/http2/hpack
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and openssl/libssl1.1@1.1.1l-1ubuntu1.1 + helm.sh/helm/v3@* and golang.org/x/net/http2/hpack@v0.5.0
    @@ -520,113 +518,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg-2.1build1 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - libfido2/libfido2-1@1.6.0-2build1 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - openssh/openssh-client@1:8.4p1-6ubuntu2.1 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - ca-certificates@20210119ubuntu1 - - openssl@1.1.1l-1ubuntu1.1 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 - - libssh/libssh-4@0.9.6-1 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-1ubuntu9 - - pam/libpam-modules@1.3.1-5ubuntu11 - - libnsl/libnsl2@1.3.0-2build1 - - libtirpc/libtirpc3@1.3.2-2 - - krb5/libgssapi-krb5-2@1.18.3-6 - - krb5/libkrb5-3@1.18.3-6 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - openssl@1.1.1l-1ubuntu1.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - ca-certificates@20210119ubuntu1 + helm.sh/helm/v3@* - openssl@1.1.1l-1ubuntu1.1 + golang.org/x/net/http2/hpack@v0.5.0 @@ -637,54 +531,39 @@

      Detailed paths


      -

      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream openssl package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      The BN_mod_sqrt() function, which computes a modular square root, contains a bug that can cause it to loop forever for non-prime moduli. Internally this function is used when parsing certificates that contain elliptic curve public keys in compressed form or explicit elliptic curve parameters with a base point encoded in compressed form. It is possible to trigger the infinite loop by crafting a certificate that has invalid explicit curve parameters. Since certificate parsing happens prior to verification of the certificate signature, any process that parses an externally supplied certificate may thus be subject to a denial of service attack. The infinite loop can also be reached when parsing crafted private keys as they can contain explicit elliptic curve parameters. Thus vulnerable situations include: - TLS clients consuming server certificates - TLS servers consuming client certificates - Hosting providers taking certificates or private keys from customers - Certificate authorities parsing certification requests from subscribers - Anything else which parses ASN.1 elliptic curve parameters Also any other applications that use the BN_mod_sqrt() where the attacker can control the parameter values are vulnerable to this DoS issue. In the OpenSSL 1.0.2 version the public key is not parsed during initial parsing of the certificate which makes it slightly harder to trigger the infinite loop. However any operation which requires the public key from the certificate will trigger the infinite loop. In particular the attacker can use a self-signed certificate to trigger the loop during verification of the certificate signature. This issue affects OpenSSL versions 1.0.2, 1.1.1 and 3.0. It was addressed in the releases of 1.1.1n and 3.0.2 on the 15th March 2022. Fixed in OpenSSL 3.0.2 (Affected 3.0.0,3.0.1). Fixed in OpenSSL 1.1.1n (Affected 1.1.1-1.1.1m). Fixed in OpenSSL 1.0.2zd (Affected 1.0.2-1.0.2zc).

      +

      Overview

      +

      Affected versions of this package are vulnerable to Denial of Service (DoS) such that a maliciously crafted HTTP/2 stream could cause excessive CPU consumption in the HPACK decoder.

      +

      Details

      +

      Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

      +

      Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

      +

      One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

      +

      When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

      +

      Two common types of DoS vulnerabilities:

      +
        +
      • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

        +
      • +
      • Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

        +
      • +

      Remediation

      -

      Upgrade Ubuntu:21.10 openssl to version 1.1.1l-1ubuntu1.2 or higher.

      +

      Upgrade golang.org/x/net/http2/hpack to version 0.7.0 or higher.

      References


    -

    Exposure of Resource to Wrong Sphere

    +

    Denial of Service (DoS)

    @@ -695,18 +574,21 @@

    Exposure of Resource to Wrong Sphere

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/helm/v3 /usr/local/bin/helm +
    • +
    • + Package Manager: golang
    • Vulnerable module: - expat/libexpat1 + golang.org/x/net/http2
    • Introduced through: + helm.sh/helm/v3@* and golang.org/x/net/http2@v0.5.0 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others
    @@ -718,11 +600,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + helm.sh/helm/v3@* - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 + golang.org/x/net/http2@v0.5.0 @@ -733,37 +613,40 @@

      Detailed paths


      -

      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      xmlparse.c in Expat (aka libexpat) before 2.4.5 allows attackers to insert namespace-separator characters into namespace URIs.

      +

      Overview

      +

      golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

      +

      Affected versions of this package are vulnerable to Denial of Service (DoS) such that a maliciously crafted HTTP/2 stream could cause excessive CPU consumption in the HPACK decoder.

      +

      Details

      +

      Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

      +

      Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

      +

      One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

      +

      When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

      +

      Two common types of DoS vulnerabilities:

      +
        +
      • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

        +
      • +
      • Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

        +
      • +

      Remediation

      -

      Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.1 or higher.

      +

      Upgrade golang.org/x/net/http2 to version 0.7.0 or higher.

      References


    -

    Improper Encoding or Escaping of Output

    +

    Denial of Service (DoS)

    @@ -774,18 +657,21 @@

    Improper Encoding or Escaping of Output

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/helm/v3 /usr/local/bin/helm +
    • +
    • + Package Manager: golang
    • Vulnerable module: - expat/libexpat1 + golang.org/x/net/http2
    • Introduced through: + helm.sh/helm/v3@* and golang.org/x/net/http2@v0.5.0 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others
    @@ -797,11 +683,9 @@

    Detailed paths

    -

    SQL Injection

    +

    Directory Traversal

    @@ -852,17 +738,20 @@

    SQL Injection

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/helm/v3 /usr/local/bin/helm +
    • +
    • + Package Manager: golang
    • Vulnerable module: - cyrus-sasl2/libsasl2-modules + github.com/cyphar/filepath-securejoin
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and cyrus-sasl2/libsasl2-modules@2.1.27+dfsg-2.1build1 + helm.sh/helm/v3@* and github.com/cyphar/filepath-securejoin@v0.2.3
    @@ -875,41 +764,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg-2.1build1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 - - openldap/libldap-2.5-0@2.5.6+dfsg-1~exp1ubuntu1 - - cyrus-sasl2/libsasl2-2@2.1.27+dfsg-2.1build1 - - cyrus-sasl2/libsasl2-modules-db@2.1.27+dfsg-2.1build1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + helm.sh/helm/v3@* - git@1:2.32.0-1ubuntu1 - - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 - - openldap/libldap-2.5-0@2.5.6+dfsg-1~exp1ubuntu1 - - cyrus-sasl2/libsasl2-2@2.1.27+dfsg-2.1build1 + github.com/cyphar/filepath-securejoin@v0.2.3 @@ -920,36 +777,47 @@

      Detailed paths


      -

      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream cyrus-sasl2 package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      In Cyrus SASL 2.1.17 through 2.1.27 before 2.1.28, plugins/sql.c does not escape the password for a SQL INSERT or UPDATE statement.

      +

      Overview

      +

      Affected versions of this package are vulnerable to Directory Traversal via the filepath.FromSlash() function, allwoing attackers to generate paths that were outside of the provided rootfs.

      +

      Note: + This vulnerability is only exploitable on Windows OS.

      +

      Details

      +

      A Directory Traversal attack (also known as path traversal) aims to access files and directories that are stored outside the intended folder. By manipulating files with "dot-dot-slash (../)" sequences and its variations, or by using absolute file paths, it may be possible to access arbitrary files and directories stored on file system, including application source code, configuration, and other critical system files.

      +

      Directory Traversal vulnerabilities can be generally divided into two types:

      +
        +
      • Information Disclosure: Allows the attacker to gain information about the folder structure or read the contents of sensitive files on the system.
      • +
      +

      st is a module for serving static files on web pages, and contains a vulnerability of this type. In our example, we will serve files from the public route.

      +

      If an attacker requests the following URL from our server, it will in turn leak the sensitive private key of the root user.

      +
      curl http://localhost:8080/public/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/root/.ssh/id_rsa
      +        
      +

      Note %2e is the URL encoded version of . (dot).

      +
        +
      • Writing arbitrary files: Allows the attacker to create or replace existing files. This type of vulnerability is also known as Zip-Slip.
      • +
      +

      One way to achieve this is by using a malicious zip archive that holds path traversal filenames. When each filename in the zip archive gets concatenated to the target extraction folder, without validation, the final path ends up outside of the target folder. If an executable or a configuration file is overwritten with a file containing malicious code, the problem can turn into an arbitrary code execution issue quite easily.

      +

      The following is an example of a zip archive with one benign file and one malicious file. Extracting the malicious file will result in traversing out of the target folder, ending up in /root/.ssh/ overwriting the authorized_keys file:

      +
      2018-04-15 22:04:29 .....           19           19  good.txt
      +        2018-04-15 22:04:42 .....           20           20  ../../../../../../root/.ssh/authorized_keys
      +        

      Remediation

      -

      Upgrade Ubuntu:21.10 cyrus-sasl2 to version 2.1.27+dfsg-2.1ubuntu0.1 or higher.

      +

      Upgrade github.com/cyphar/filepath-securejoin to version 0.2.4 or higher.

      References


    -

    Out-of-bounds Write

    +

    CVE-2020-22916

    @@ -960,18 +828,21 @@

    Out-of-bounds Write

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - zlib/zlib1g + xz-utils/liblzma5
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 and xz-utils/liblzma5@5.2.5-2ubuntu1 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, meta-common-packages@meta and others
    @@ -983,11 +854,9 @@

    Detailed paths

    -

    Improper Input Validation

    +

    CVE-2023-51767

    @@ -1057,18 +904,21 @@

    Improper Input Validation

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - xz-utils/liblzma5 + openssh/openssh-client
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 and openssh/openssh-client@1:8.9p1-3ubuntu0.6 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, meta-common-packages@meta and others
    @@ -1080,11 +930,9 @@

    Detailed paths

    -

    Files or Directories Accessible to External Parties

    +

    Information Exposure

    @@ -1134,17 +980,20 @@

    Files or Directories Accessible to External Parties

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - util-linux/libblkid1 + libgcrypt20
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and util-linux/libblkid1@2.36.1-8ubuntu2 + docker-image|quay.io/argoproj/argocd@v2.7.17 and libgcrypt20@1.9.4-3ubuntu3
    @@ -1157,183 +1006,221 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/libblkid1@2.36.1-8ubuntu2 + libgcrypt20@1.9.4-3ubuntu3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - e2fsprogs@1.46.3-1ubuntu3 + gnupg2/dirmngr@2.2.27-3ubuntu2.1 - util-linux/libblkid1@2.36.1-8ubuntu2 + libgcrypt20@1.9.4-3ubuntu3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/libmount1@2.36.1-8ubuntu2 + gnupg2/gpg@2.2.27-3ubuntu2.1 - util-linux/libblkid1@2.36.1-8ubuntu2 + libgcrypt20@1.9.4-3ubuntu3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/mount@2.36.1-8ubuntu2 + apt@2.4.11 - util-linux/libblkid1@2.36.1-8ubuntu2 + apt/libapt-pkg6.0@2.4.11 + + libgcrypt20@1.9.4-3ubuntu3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/mount@2.36.1-8ubuntu2 + apt@2.4.11 - util-linux@2.36.1-8ubuntu2 + gnupg2/gpgv@2.2.27-3ubuntu2.1 - util-linux/libblkid1@2.36.1-8ubuntu2 + libgcrypt20@1.9.4-3ubuntu3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/libuuid1@2.36.1-8ubuntu2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + gnupg2/gpg@2.2.27-3ubuntu2.1 - e2fsprogs@1.46.3-1ubuntu3 + gnupg2/gpgconf@2.2.27-3ubuntu2.1 - util-linux/libuuid1@2.36.1-8ubuntu2 + libgcrypt20@1.9.4-3ubuntu3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/mount@2.36.1-8ubuntu2 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - util-linux@2.36.1-8ubuntu2 + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 - util-linux/libuuid1@2.36.1-8ubuntu2 + libgcrypt20@1.9.4-3ubuntu3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux@2.36.1-8ubuntu2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - util-linux/mount@2.36.1-8ubuntu2 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - util-linux@2.36.1-8ubuntu2 + libgcrypt20@1.9.4-3ubuntu3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/libmount1@2.36.1-8ubuntu2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - util-linux/mount@2.36.1-8ubuntu2 + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 - util-linux/libmount1@2.36.1-8ubuntu2 + libgcrypt20@1.9.4-3ubuntu3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/mount@2.36.1-8ubuntu2 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - util-linux@2.36.1-8ubuntu2 + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 - util-linux/libmount1@2.36.1-8ubuntu2 + libgcrypt20@1.9.4-3ubuntu3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/libsmartcols1@2.36.1-8ubuntu2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - util-linux/mount@2.36.1-8ubuntu2 + gnupg2/gpgsm@2.2.27-3ubuntu2.1 - util-linux/libsmartcols1@2.36.1-8ubuntu2 + libgcrypt20@1.9.4-3ubuntu3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + apt@2.4.11 - util-linux/mount@2.36.1-8ubuntu2 + apt/libapt-pkg6.0@2.4.11 - util-linux@2.36.1-8ubuntu2 + systemd/libsystemd0@249.11-0ubuntu3.12 - util-linux/libsmartcols1@2.36.1-8ubuntu2 + libgcrypt20@1.9.4-3ubuntu3
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream libgcrypt20 package and not the libgcrypt20 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A timing-based side-channel flaw was found in libgcrypt's RSA implementation. This issue may allow a remote attacker to initiate a Bleichenbacher-style attack, which can lead to the decryption of RSA ciphertexts.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 libgcrypt20.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2022-48624

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + less +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.7.17 and less@590-1ubuntu0.22.04.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/mount@2.36.1-8ubuntu2 + less@590-1ubuntu0.22.04.1 @@ -1345,34 +1232,28 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream util-linux package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      A logic error was found in the libmount library of util-linux in the function that allows an unprivileged user to unmount a FUSE filesystem. This flaw allows a local user on a vulnerable system to unmount other users' filesystems that are either world-writable themselves (like /tmp) or mounted in a world-writable directory. An attacker may use this flaw to cause a denial of service to applications that use the affected filesystems.

      +

      Note: Versions mentioned in the description apply only to the upstream less package and not the less package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      close_altfile in filename.c in less before 606 omits shell_quote calls for LESSCLOSE.

      Remediation

      -

      Upgrade Ubuntu:21.10 util-linux to version 2.36.1-8ubuntu2.2 or higher.

      +

      Upgrade Ubuntu:22.04 less to version 590-1ubuntu0.22.04.2 or higher.

      References


    -

    Files or Directories Accessible to External Parties

    +

    CVE-2024-26461

    @@ -1383,17 +1264,20 @@

    Files or Directories Accessible to External Parties

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - util-linux/libblkid1 + krb5/libk5crypto3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and util-linux/libblkid1@2.36.1-8ubuntu2 + docker-image|quay.io/argoproj/argocd@v2.7.17 and krb5/libk5crypto3@1.19.2-2ubuntu0.3
    @@ -1406,183 +1290,159 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/libblkid1@2.36.1-8ubuntu2 + krb5/libk5crypto3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - e2fsprogs@1.46.3-1ubuntu3 + adduser@3.118ubuntu5 - util-linux/libblkid1@2.36.1-8ubuntu2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + shadow/passwd@1:4.8.1-2ubuntu2.1 - util-linux/libmount1@2.36.1-8ubuntu2 + pam/libpam-modules@1.4.0-11ubuntu2.4 - util-linux/libblkid1@2.36.1-8ubuntu2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - util-linux/mount@2.36.1-8ubuntu2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - util-linux/libblkid1@2.36.1-8ubuntu2 + krb5/libk5crypto3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - util-linux/mount@2.36.1-8ubuntu2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - util-linux@2.36.1-8ubuntu2 + krb5/libkrb5-3@1.19.2-2ubuntu0.3 - util-linux/libblkid1@2.36.1-8ubuntu2 + krb5/libk5crypto3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/libuuid1@2.36.1-8ubuntu2 + krb5/libkrb5-3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - e2fsprogs@1.46.3-1ubuntu3 + adduser@3.118ubuntu5 - util-linux/libuuid1@2.36.1-8ubuntu2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.4 - util-linux/mount@2.36.1-8ubuntu2 + libnsl/libnsl2@1.3.0-2build2 - util-linux@2.36.1-8ubuntu2 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - util-linux/libuuid1@2.36.1-8ubuntu2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux@2.36.1-8ubuntu2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/mount@2.36.1-8ubuntu2 + openssh/openssh-client@1:8.9p1-3ubuntu0.6 - util-linux@2.36.1-8ubuntu2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/libmount1@2.36.1-8ubuntu2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + git@1:2.34.1-1ubuntu1.10 - util-linux/mount@2.36.1-8ubuntu2 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - util-linux/libmount1@2.36.1-8ubuntu2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + git@1:2.34.1-1ubuntu1.10 - util-linux/mount@2.36.1-8ubuntu2 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - util-linux@2.36.1-8ubuntu2 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - util-linux/libmount1@2.36.1-8ubuntu2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/libsmartcols1@2.36.1-8ubuntu2 + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/mount@2.36.1-8ubuntu2 - - util-linux/libsmartcols1@2.36.1-8ubuntu2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - util-linux/mount@2.36.1-8ubuntu2 - - util-linux@2.36.1-8ubuntu2 - - util-linux/libsmartcols1@2.36.1-8ubuntu2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - util-linux/mount@2.36.1-8ubuntu2 + krb5/libkrb5support0@1.19.2-2ubuntu0.3 @@ -1594,33 +1454,26 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream util-linux package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      A logic error was found in the libmount library of util-linux in the function that allows an unprivileged user to unmount a FUSE filesystem. This flaw allows an unprivileged local attacker to unmount FUSE filesystems that belong to certain other users who have a UID that is a prefix of the UID of the attacker in its string form. An attacker may use this flaw to cause a denial of service to applications that use the affected filesystems.

      +

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      Kerberos 5 (aka krb5) 1.21.2 contains a memory leak vulnerability in /krb5/src/lib/gssapi/krb5/k5sealv3.c.

      Remediation

      -

      Upgrade Ubuntu:21.10 util-linux to version 2.36.1-8ubuntu2.2 or higher.

      +

      There is no fixed version for Ubuntu:22.04 krb5.

      References


    -

    Out-of-bounds Read

    +

    CVE-2024-26462

    @@ -1631,18 +1484,21 @@

    Out-of-bounds Read

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - sqlite3/libsqlite3-0 + krb5/libk5crypto3
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, gnupg2/gpg@2.2.20-1ubuntu4 and others
    @@ -1654,313 +1510,159 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - gnupg2/gpg@2.2.20-1ubuntu4 - - sqlite3/libsqlite3-0@3.35.5-1 + krb5/libk5crypto3@1.19.2-2ubuntu0.3
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream sqlite3 package.

    -

    An out-of-bounds read was addressed with improved bounds checking. This issue is fixed in iOS 13.5 and iPadOS 13.5, macOS Catalina 10.15.5, tvOS 13.4.5, watchOS 6.2.5, iTunes 12.10.7 for Windows, iCloud for Windows 11.2, iCloud for Windows 7.19. A malicious application may cause a denial of service or potentially disclose memory contents.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:21.10 sqlite3.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Verification of Cryptographic Signature

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - perl/perl-modules-5.32 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git@1:2.32.0-1ubuntu1 + adduser@3.118ubuntu5 - perl@5.32.1-3ubuntu3 + shadow/passwd@1:4.8.1-2ubuntu2.1 - perl/perl-modules-5.32@5.32.1-3ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + pam/libpam-modules@1.4.0-11ubuntu2.4 - git@1:2.32.0-1ubuntu1 + libnsl/libnsl2@1.3.0-2build2 - perl@5.32.1-3ubuntu3 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - perl/libperl5.32@5.32.1-3ubuntu3 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - perl/perl-modules-5.32@5.32.1-3ubuntu3 + krb5/libk5crypto3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git@1:2.32.0-1ubuntu1 + adduser@3.118ubuntu5 - perl@5.32.1-3ubuntu3 + shadow/passwd@1:4.8.1-2ubuntu2.1 - perl/libperl5.32@5.32.1-3ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + pam/libpam-modules@1.4.0-11ubuntu2.4 - git@1:2.32.0-1ubuntu1 + libnsl/libnsl2@1.3.0-2build2 - perl@5.32.1-3ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - meta-common-packages@meta + krb5/libkrb5-3@1.19.2-2ubuntu0.3 - perl/perl-base@5.32.1-3ubuntu3 + krb5/libk5crypto3@1.19.2-2ubuntu0.3
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream perl package.

    -

    CPAN 2.28 allows Signature Verification Bypass.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:21.10 perl.

    -

    References

    - - -
    - - - -
    -
    -

    OS Command Injection

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - openssl/libssl1.1 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and openssl/libssl1.1@1.1.1l-1ubuntu1.1 - -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libkrb5-3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg-2.1build1 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - libfido2/libfido2-1@1.6.0-2build1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - openssh/openssh-client@1:8.4p1-6ubuntu2.1 + openssh/openssh-client@1:8.9p1-3ubuntu0.6 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - ca-certificates@20210119ubuntu1 + git@1:2.34.1-1ubuntu1.10 - openssl@1.1.1l-1ubuntu1.1 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git@1:2.32.0-1ubuntu1 + git@1:2.34.1-1ubuntu1.10 - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - libssh/libssh-4@0.9.6-1 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-1ubuntu9 - - pam/libpam-modules@1.3.1-5ubuntu11 - - libnsl/libnsl2@1.3.0-2build1 + shadow/passwd@1:4.8.1-2ubuntu2.1 - libtirpc/libtirpc3@1.3.2-2 + pam/libpam-modules@1.4.0-11ubuntu2.4 - krb5/libgssapi-krb5-2@1.18.3-6 + libnsl/libnsl2@1.3.0-2build2 - krb5/libkrb5-3@1.18.3-6 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - openssl@1.1.1l-1ubuntu1.1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - ca-certificates@20210119ubuntu1 - - openssl@1.1.1l-1ubuntu1.1 + krb5/libkrb5support0@1.19.2-2ubuntu0.3 @@ -1972,38 +1674,26 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream openssl package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      The c_rehash script does not properly sanitise shell metacharacters to prevent command injection. This script is distributed by some operating systems in a manner where it is automatically executed. On such operating systems, an attacker could execute arbitrary commands with the privileges of the script. Use of the c_rehash script is considered obsolete and should be replaced by the OpenSSL rehash command line tool. Fixed in OpenSSL 3.0.3 (Affected 3.0.0,3.0.1,3.0.2). Fixed in OpenSSL 1.1.1o (Affected 1.1.1-1.1.1n). Fixed in OpenSSL 1.0.2ze (Affected 1.0.2-1.0.2zd).

      +

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      Kerberos 5 (aka krb5) 1.21.2 contains a memory leak vulnerability in /krb5/src/kdc/ndr.c.

      Remediation

      -

      Upgrade Ubuntu:21.10 openssl to version 1.1.1l-1ubuntu1.3 or higher.

      +

      There is no fixed version for Ubuntu:22.04 krb5.

      References


    -

    OS Command Injection

    +

    CVE-2024-26458

    @@ -2014,17 +1704,20 @@

    OS Command Injection

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - openssl/libssl1.1 + krb5/libk5crypto3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and openssl/libssl1.1@1.1.1l-1ubuntu1.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 and krb5/libk5crypto3@1.19.2-2ubuntu0.3
    @@ -2037,113 +1730,159 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libk5crypto3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg-2.1build1 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - libfido2/libfido2-1@1.6.0-2build1 + krb5/libkrb5-3@1.19.2-2ubuntu0.3 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libk5crypto3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - openssh/openssh-client@1:8.4p1-6ubuntu2.1 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libkrb5-3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 - ca-certificates@20210119ubuntu1 + pam/libpam-modules@1.4.0-11ubuntu2.4 - openssl@1.1.1l-1ubuntu1.1 + libnsl/libnsl2@1.3.0-2build2 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git@1:2.32.0-1ubuntu1 - - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 - libssh/libssh-4@0.9.6-1 + openssh/openssh-client@1:8.9p1-3ubuntu0.6 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - adduser@3.118ubuntu5 + docker-image|quay.io/argoproj/argocd@v2.7.17 - shadow/passwd@1:4.8.1-1ubuntu9 + git@1:2.34.1-1ubuntu1.10 - pam/libpam-modules@1.3.1-5ubuntu11 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - libnsl/libnsl2@1.3.0-2build1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 - libtirpc/libtirpc3@1.3.2-2 + git@1:2.34.1-1ubuntu1.10 - krb5/libgssapi-krb5-2@1.18.3-6 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - krb5/libkrb5-3@1.18.3-6 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 - openssl@1.1.1l-1ubuntu1.1 + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - ca-certificates@20210119ubuntu1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - openssl@1.1.1l-1ubuntu1.1 + krb5/libkrb5support0@1.19.2-2ubuntu0.3 @@ -2155,34 +1894,26 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream openssl package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      In addition to the c_rehash shell command injection identified in CVE-2022-1292, further circumstances where the c_rehash script does not properly sanitise shell metacharacters to prevent command injection were found by code review. When the CVE-2022-1292 was fixed it was not discovered that there are other places in the script where the file names of certificates being hashed were possibly passed to a command executed through the shell. This script is distributed by some operating systems in a manner where it is automatically executed. On such operating systems, an attacker could execute arbitrary commands with the privileges of the script. Use of the c_rehash script is considered obsolete and should be replaced by the OpenSSL rehash command line tool. Fixed in OpenSSL 3.0.4 (Affected 3.0.0,3.0.1,3.0.2,3.0.3). Fixed in OpenSSL 1.1.1p (Affected 1.1.1-1.1.1o). Fixed in OpenSSL 1.0.2zf (Affected 1.0.2-1.0.2ze).

      +

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      Kerberos 5 (aka krb5) 1.21.2 contains a memory leak in /krb5/src/lib/rpc/pmap_rmt.c.

      Remediation

      -

      Upgrade Ubuntu:21.10 openssl to version 1.1.1l-1ubuntu1.5 or higher.

      +

      There is no fixed version for Ubuntu:22.04 krb5.

      References


    -

    Inadequate Encryption Strength

    +

    Infinite loop

    @@ -2193,17 +1924,20 @@

    Inadequate Encryption Strength

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang
    • Vulnerable module: - openssl/libssl1.1 + google.golang.org/protobuf/internal/encoding/json
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and openssl/libssl1.1@1.1.1l-1ubuntu1.1 + github.com/argoproj/argo-cd/v2@* and google.golang.org/protobuf/internal/encoding/json@v1.31.0
    @@ -2216,113 +1950,148 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + github.com/argoproj/argo-cd/v2@* - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + google.golang.org/protobuf/internal/encoding/json@v1.31.0
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - cyrus-sasl2/libsasl2-modules@2.1.27+dfsg-2.1build1 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 - - +
    - +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Stack-based Buffer Overflow

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + github.com/argoproj/argo-cd/v2@* - libfido2/libfido2-1@1.6.0-2build1 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 + google.golang.org/protobuf/encoding/protojson@v1.31.0
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - openssh/openssh-client@1:8.4p1-6ubuntu2.1 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 - - +
    - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - ca-certificates@20210119ubuntu1 - - openssl@1.1.1l-1ubuntu1.1 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 - - +
  • - -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 - - libssh/libssh-4@0.9.6-1 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 - - +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Stack-based Buffer Overflow when processing input that uses pathologically deep nesting.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

    +

    References

    + -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-1ubuntu9 - - pam/libpam-modules@1.3.1-5ubuntu11 - - libnsl/libnsl2@1.3.0-2build1 - - libtirpc/libtirpc3@1.3.2-2 - - krb5/libgssapi-krb5-2@1.18.3-6 - - krb5/libkrb5-3@1.18.3-6 - - openssl/libssl1.1@1.1.1l-1ubuntu1.1 - - +
    -
  • -
  • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - openssl@1.1.1l-1ubuntu1.1 - - + -
  • +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + github.com/argoproj/argo-cd/v2@* - ca-certificates@20210119ubuntu1 - - openssl@1.1.1l-1ubuntu1.1 + google.golang.org/protobuf/encoding/protojson@v1.31.0 @@ -2333,35 +2102,28 @@

      Detailed paths


      -

      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream openssl package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      AES OCB mode for 32-bit x86 platforms using the AES-NI assembly optimised implementation will not encrypt the entirety of the data under some circumstances. This could reveal sixteen bytes of data that was preexisting in the memory that wasn't written. In the special case of "in place" encryption, sixteen bytes of the plaintext would be revealed. Since OpenSSL does not support OCB based cipher suites for TLS and DTLS, they are both unaffected. Fixed in OpenSSL 3.0.5 (Affected 3.0.0-3.0.4). Fixed in OpenSSL 1.1.1q (Affected 1.1.1-1.1.1p).

      +

      Overview

      +

      Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

      +

      Note:

      +

      This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

      Remediation

      -

      Upgrade Ubuntu:21.10 openssl to version 1.1.1l-1ubuntu1.6 or higher.

      +

      Upgrade google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

      References


    -

    SQL Injection

    +

    Allocation of Resources Without Limits or Throttling

    @@ -2372,18 +2134,21 @@

    SQL Injection

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/helm/v3 /usr/local/bin/helm +
    • +
    • + Package Manager: golang
    • Vulnerable module: - openldap/libldap-2.5-0 + golang.org/x/net/http2
    • Introduced through: + helm.sh/helm/v3@* and golang.org/x/net/http2@v0.5.0 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, gnupg2/dirmngr@2.2.20-1ubuntu4 and others
    @@ -2395,33 +2160,81 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/dirmngr@2.2.20-1ubuntu4 + helm.sh/helm/v3@* - openldap/libldap-2.5-0@2.5.6+dfsg-1~exp1ubuntu1 + golang.org/x/net/http2@v0.5.0
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 - - openldap/libldap-2.5-0@2.5.6+dfsg-1~exp1ubuntu1 - - +
    - +
    + +
    + +

    Overview

    +

    golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

    +

    Affected versions of this package are vulnerable to Allocation of Resources Without Limits or Throttling when MaxConcurrentStreams handler goroutines running. A a handler is started until one of the existing handlers exits.

    +

    Note:

    +

    This issue is related to CVE-2023-44487

    +

    Remediation

    +

    Upgrade golang.org/x/net/http2 to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Authentication Bypass by Capture-replay

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/crypto/ssh +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and golang.org/x/crypto/ssh@v0.16.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + github.com/argoproj/argo-cd/v2@* - openldap/libldap-common@2.5.6+dfsg-1~exp1ubuntu1 + golang.org/x/crypto/ssh@v0.16.0 @@ -2432,30 +2245,50 @@

      Detailed paths


      -

      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream openldap package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      In OpenLDAP 2.x before 2.5.12 and 2.6.x before 2.6.2, a SQL injection vulnerability exists in the experimental back-sql backend to slapd, via a SQL statement within an LDAP query. This can occur during an LDAP search operation when the search filter is processed, due to a lack of proper escaping.

      +

      Overview

      +

      golang.org/x/crypto/ssh is a SSH client and server

      +

      Affected versions of this package are vulnerable to Authentication Bypass by Capture-replay during the establishment of the secure channel. An attacker can manipulate handshake sequence numbers to delete messages sent immediately after the channel is established.

      +

      Note:

      +
        +
      1. Sequence numbers are only validated once the channel is established and arbitrary messages are allowed during the handshake, allowing them to manipulate the sequence numbers.

        +
      2. +
      3. The potential consequences of the general Terrapin attack are dependent on the messages exchanged after the handshake concludes. If you are using a custom SSH service and do not resort to the authentication protocol, you should check that dropping the first few messages of a connection does not yield security risks.

        +
      4. +
      +

      Impact:

      +

      While cryptographically novel, there is no discernable impact on the integrity of SSH traffic beyond giving the attacker the ability to delete the message that enables some features related to keystroke timing obfuscation. To successfully carry out the exploitation, the connection needs to be protected using either the ChaCha20-Poly1305 or CBC with Encrypt-then-MAC encryption methods. The attacker must also be able to intercept and modify the connection's traffic.

      +

      Workaround

      +

      Temporarily disable the affected chacha20-poly1305@openssh.com encryption and *-etm@openssh.com MAC algorithms in the affected configuration, and use unaffected algorithms like AES-GCM instead.

      Remediation

      -

      Upgrade Ubuntu:21.10 openldap to version 2.5.6+dfsg-1~exp1ubuntu1.1 or higher.

      +

      Upgrade golang.org/x/crypto/ssh to version 0.17.0 or higher.

      References


    -

    NULL Pointer Dereference

    +

    Information Exposure

    @@ -2466,17 +2299,20 @@

    NULL Pointer Dereference

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - krb5/libk5crypto3 + gnutls28/libgnutls30
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and krb5/libk5crypto3@1.18.3-6 + docker-image|quay.io/argoproj/argocd@v2.7.17 and gnutls28/libgnutls30@3.7.3-4ubuntu1.4
    @@ -2489,161 +2325,212 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - krb5/libk5crypto3@1.18.3-6 + gnutls28/libgnutls30@3.7.3-4ubuntu1.4
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - adduser@3.118ubuntu5 + docker-image|quay.io/argoproj/argocd@v2.7.17 - shadow/passwd@1:4.8.1-1ubuntu9 + apt@2.4.11 - pam/libpam-modules@1.3.1-5ubuntu11 - - libnsl/libnsl2@1.3.0-2build1 - - libtirpc/libtirpc3@1.3.2-2 - - krb5/libgssapi-krb5-2@1.18.3-6 - - krb5/libk5crypto3@1.18.3-6 + gnutls28/libgnutls30@3.7.3-4ubuntu1.4
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-1ubuntu9 - - pam/libpam-modules@1.3.1-5ubuntu11 + docker-image|quay.io/argoproj/argocd@v2.7.17 - libnsl/libnsl2@1.3.0-2build1 + gnupg2/dirmngr@2.2.27-3ubuntu2.1 - libtirpc/libtirpc3@1.3.2-2 - - krb5/libgssapi-krb5-2@1.18.3-6 - - krb5/libkrb5-3@1.18.3-6 - - krb5/libk5crypto3@1.18.3-6 + gnutls28/libgnutls30@3.7.3-4ubuntu1.4
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + git@1:2.34.1-1ubuntu1.10 - krb5/libkrb5-3@1.18.3-6 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - adduser@3.118ubuntu5 + git@1:2.34.1-1ubuntu1.10 - shadow/passwd@1:4.8.1-1ubuntu9 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - pam/libpam-modules@1.3.1-5ubuntu11 + openldap/libldap-2.5-0@2.5.16+dfsg-0ubuntu0.22.04.2 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 - libnsl/libnsl2@1.3.0-2build1 + git@1:2.34.1-1ubuntu1.10 - libtirpc/libtirpc3@1.3.2-2 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - krb5/libgssapi-krb5-2@1.18.3-6 + rtmpdump/librtmp1@2.4+20151223.gitfa8646d.1-2build4 - krb5/libkrb5-3@1.18.3-6 + gnutls28/libgnutls30@3.7.3-4ubuntu1.4
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream gnutls28 package and not the gnutls28 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A flaw was found in GnuTLS. The Minerva attack is a cryptographic vulnerability that exploits deterministic behavior in systems like GnuTLS, leading to side-channel leaks. In specific scenarios, such as when using the GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE flag, it can result in a noticeable step in nonce size from 513 to 512 bits, exposing a potential timing side-channel.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 gnutls28.

    +

    References

    + + +
    + + + +
    +
    +

    Uncaught Exception

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gnutls28/libgnutls30 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.7.17 and gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - krb5/libgssapi-krb5-2@1.18.3-6 + gnutls28/libgnutls30@3.7.3-4ubuntu1.4
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - openssh/openssh-client@1:8.4p1-6ubuntu2.1 + apt@2.4.11 - krb5/libgssapi-krb5-2@1.18.3-6 + gnutls28/libgnutls30@3.7.3-4ubuntu1.4
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git@1:2.32.0-1ubuntu1 + gnupg2/dirmngr@2.2.27-3ubuntu2.1 - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 - - krb5/libgssapi-krb5-2@1.18.3-6 + gnutls28/libgnutls30@3.7.3-4ubuntu1.4
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + git@1:2.34.1-1ubuntu1.10 - libssh/libssh-4@0.9.6-1 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - krb5/libgssapi-krb5-2@1.18.3-6 + gnutls28/libgnutls30@3.7.3-4ubuntu1.4
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-1ubuntu9 + git@1:2.34.1-1ubuntu1.10 - pam/libpam-modules@1.3.1-5ubuntu11 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - libnsl/libnsl2@1.3.0-2build1 + openldap/libldap-2.5-0@2.5.16+dfsg-0ubuntu0.22.04.2 - libtirpc/libtirpc3@1.3.2-2 - - krb5/libgssapi-krb5-2@1.18.3-6 + gnutls28/libgnutls30@3.7.3-4ubuntu1.4
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + git@1:2.34.1-1ubuntu1.10 - meta-common-packages@meta + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - krb5/libkrb5support0@1.18.3-6 + rtmpdump/librtmp1@2.4+20151223.gitfa8646d.1-2build4 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 @@ -2655,32 +2542,28 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream krb5 package.

      -

      The Key Distribution Center (KDC) in MIT Kerberos 5 (aka krb5) before 1.18.5 and 1.19.x before 1.19.3 has a NULL pointer dereference in kdc/do_tgs_req.c via a FAST inner body that lacks a server field.

      +

      Note: Versions mentioned in the description apply only to the upstream gnutls28 package and not the gnutls28 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      A flaw has been discovered in GnuTLS where an application crash can be induced when attempting to verify a specially crafted .pem bundle using the "certtool --verify-chain" command.

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 krb5.

      +

      There is no fixed version for Ubuntu:22.04 gnutls28.

      References


    -

    Improper Input Validation

    +

    MPL-2.0 license

    @@ -2691,17 +2574,20 @@

    Improper Input Validation

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argo-cd/v2 /usr/local/bin/argocd
    • - Vulnerable module: + Package Manager: golang +
    • +
    • + Module: - gzip + github.com/r3labs/diff
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and gzip@1.10-4ubuntu1 + github.com/argoproj/argo-cd/v2@* and github.com/r3labs/diff@v1.1.0
    @@ -2714,9 +2600,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + github.com/argoproj/argo-cd/v2@* - gzip@1.10-4ubuntu1 + github.com/r3labs/diff@v1.1.0 @@ -2727,35 +2613,17 @@

      Detailed paths


      -

      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream gzip package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      An arbitrary file write vulnerability was found in GNU gzip's zgrep utility. When zgrep is applied on the attacker's chosen file name (for example, a crafted file name), this can overwrite an attacker's content to an arbitrary attacker-selected file. This flaw occurs due to insufficient validation when processing filenames with two or more newlines where selected content and the target file names are embedded in crafted multi-line file names. This flaw allows a remote, low privileged attacker to force zgrep to write arbitrary files on the system.

      -

      Remediation

      -

      Upgrade Ubuntu:21.10 gzip to version 1.10-4ubuntu1.1 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Arbitrary Code Injection

    +

    MPL-2.0 license

    @@ -2766,17 +2634,20 @@

    Arbitrary Code Injection

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argo-cd/v2 /usr/local/bin/argocd
    • - Vulnerable module: + Package Manager: golang +
    • +
    • + Module: - gnupg2/gpgv + github.com/hashicorp/go-version
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and gnupg2/gpgv@2.2.20-1ubuntu4 + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-version@v1.2.1
    @@ -2789,1829 +2660,30 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + github.com/argoproj/argo-cd/v2@* - gnupg2/gpgv@2.2.20-1ubuntu4 + github.com/hashicorp/go-version@v1.2.1
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - apt@2.3.9 - - gnupg2/gpgv@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpgv@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/dirmngr@2.2.20-1ubuntu4 - - gnupg2/gpgconf@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gpg@2.2.20-1ubuntu4 - - gnupg2/gpgconf@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpg-agent@2.2.20-1ubuntu4 - - gnupg2/gpgconf@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpgsm@2.2.20-1ubuntu4 - - gnupg2/gpgconf@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/dirmngr@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/dirmngr@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpg-wks-client@2.2.20-1ubuntu4 - - gnupg2/dirmngr@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg-l10n@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gnupg-l10n@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg-utils@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gnupg-utils@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gpg@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpg@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpg-wks-client@2.2.20-1ubuntu4 - - gnupg2/gpg@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpg-wks-server@2.2.20-1ubuntu4 - - gnupg2/gpg@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gpg-agent@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpg-agent@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpg-wks-client@2.2.20-1ubuntu4 - - gnupg2/gpg-agent@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpg-wks-server@2.2.20-1ubuntu4 - - gnupg2/gpg-agent@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gpg-wks-client@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpg-wks-client@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gpg-wks-server@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpg-wks-server@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gpgsm@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - gnupg2/gpgsm@2.2.20-1ubuntu4 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - gnupg2/gnupg@2.2.20-1ubuntu4 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream gnupg2 package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    GnuPG through 2.3.6, in unusual situations where an attacker possesses any secret-key information from a victim's keyring and other constraints (e.g., use of GPGME) are met, allows signature forgery via injection into the status line.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 gnupg2 to version 2.2.20-1ubuntu4.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Off-by-one Error

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - glibc/libc-bin -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and glibc/libc-bin@2.34-0ubuntu3 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - glibc/libc-bin@2.34-0ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - meta-common-packages@meta - - glibc/libc6@2.34-0ubuntu3 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream glibc package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    A flaw was found in glibc. An off-by-one buffer overflow and underflow in getcwd() may lead to memory corruption when the size of the buffer is exactly 1. A local attacker who can control the input buffer and size passed to getcwd() in a setuid program could use this flaw to potentially execute arbitrary code and escalate their privileges on the system.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 glibc to version 2.34-0ubuntu3.2 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Out-of-bounds Read

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - glibc/libc-bin -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and glibc/libc-bin@2.34-0ubuntu3 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - glibc/libc-bin@2.34-0ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - meta-common-packages@meta - - glibc/libc6@2.34-0ubuntu3 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream glibc package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    A flaw was found in glibc. The realpath() function can mistakenly return an unexpected value, potentially leading to information leakage and disclosure of sensitive data.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 glibc to version 2.34-0ubuntu3.2 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Uncontrolled Search Path Element

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - git/git-man -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - git/git-man@1:2.32.0-1ubuntu1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git-lfs@2.13.2-1 - - git@1:2.32.0-1ubuntu1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream git package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    Git for Windows is a fork of Git containing Windows-specific patches. This vulnerability affects users working on multi-user machines, where untrusted parties have write access to the same hard disk. Those untrusted parties could create the folder C:\.git, which would be picked up by Git operations run supposedly outside a repository while searching for a Git directory. Git would then respect any config in said Git directory. Git Bash users who set GIT_PS1_SHOWDIRTYSTATE are vulnerable as well. Users who installed posh-gitare vulnerable simply by starting a PowerShell. Users of IDEs such as Visual Studio are vulnerable: simply creating a new project would already read and respect the config specified in C:\.git\config. Users of the Microsoft fork of Git are vulnerable simply by starting a Git Bash. The problem has been patched in Git for Windows v2.35.2. Users unable to upgrade may create the folder .git on all drives where Git commands are run, and remove read/write access from those folders as a workaround. Alternatively, define or extend GIT_CEILING_DIRECTORIES to cover the parent directory of the user profile, e.g. C:\Users if the user profile is located in C:\Users\my-user-name.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 git to version 1:2.32.0-1ubuntu1.2 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Uncontrolled Search Path Element

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - git/git-man -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - git/git-man@1:2.32.0-1ubuntu1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git-lfs@2.13.2-1 - - git@1:2.32.0-1ubuntu1 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream git package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    Git is a distributed revision control system. Git prior to versions 2.37.1, 2.36.2, 2.35.4, 2.34.4, 2.33.4, 2.32.3, 2.31.4, and 2.30.5, is vulnerable to privilege escalation in all platforms. An unsuspecting user could still be affected by the issue reported in CVE-2022-24765, for example when navigating as root into a shared tmp directory that is owned by them, but where an attacker could create a git repository. Versions 2.37.1, 2.36.2, 2.35.4, 2.34.4, 2.33.4, 2.32.3, 2.31.4, and 2.30.5 contain a patch for this issue. The simplest way to avoid being affected by the exploit described in the example is to avoid running git as root (or an Administrator in Windows), and if needed to reduce its use to a minimum. While a generic workaround is not possible, a system could be hardened from the exploit described in the example by removing any such repository if it exists already and creating one as root to block any future attacks.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 git to version 1:2.32.0-1ubuntu1.3 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - expat/libexpat1 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    In doProlog in xmlparse.c in Expat (aka libexpat) before 2.4.3, an integer overflow exists for m_groupSize.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - expat/libexpat1 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    storeAtts in xmlparse.c in Expat (aka libexpat) before 2.4.3 has an integer overflow.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - expat/libexpat1 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    nextScaffoldPart in xmlparse.c in Expat (aka libexpat) before 2.4.3 has an integer overflow.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - expat/libexpat1 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    lookup in xmlparse.c in Expat (aka libexpat) before 2.4.3 has an integer overflow.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - expat/libexpat1 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    defineAttribute in xmlparse.c in Expat (aka libexpat) before 2.4.3 has an integer overflow.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - expat/libexpat1 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    build_model in xmlparse.c in Expat (aka libexpat) before 2.4.3 has an integer overflow.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - expat/libexpat1 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    addBinding in xmlparse.c in Expat (aka libexpat) before 2.4.3 has an integer overflow.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - expat/libexpat1 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    Expat (aka libexpat) before 2.4.4 has a signed integer overflow in XML_GetBuffer, for configurations with a nonzero XML_CONTEXT_BYTES.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - expat/libexpat1 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    Expat (aka libexpat) before 2.4.4 has an integer overflow in the doProlog function.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - expat/libexpat1 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    In Expat (aka libexpat) before 2.4.5, there is an integer overflow in storeRawNames.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.3 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Integer Overflow or Wraparound

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - expat/libexpat1 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    In Expat (aka libexpat) before 2.4.5, there is an integer overflow in copyString.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.3 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Resource Exhaustion

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - expat/libexpat1 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 - - expat/libexpat1@2.4.1-2 - - - -
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    In Expat (aka libexpat) before 2.4.5, an attacker can trigger stack exhaustion in build_model via a large nesting depth in the DTD element.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.3 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Out-of-bounds Read

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - e2fsprogs/libcom-err2 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and e2fsprogs/libcom-err2@1.46.3-1ubuntu3 - -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - e2fsprogs/libcom-err2@1.46.3-1ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - e2fsprogs@1.46.3-1ubuntu3 - - e2fsprogs/libcom-err2@1.46.3-1ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - e2fsprogs@1.46.3-1ubuntu3 - - e2fsprogs/libss2@1.46.3-1ubuntu3 - - e2fsprogs/libcom-err2@1.46.3-1ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-1ubuntu9 - - pam/libpam-modules@1.3.1-5ubuntu11 - - libnsl/libnsl2@1.3.0-2build1 - - libtirpc/libtirpc3@1.3.2-2 - - krb5/libgssapi-krb5-2@1.18.3-6 - - e2fsprogs/libcom-err2@1.46.3-1ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-1ubuntu9 - - pam/libpam-modules@1.3.1-5ubuntu11 - - libnsl/libnsl2@1.3.0-2build1 - - libtirpc/libtirpc3@1.3.2-2 - - krb5/libgssapi-krb5-2@1.18.3-6 - - krb5/libkrb5-3@1.18.3-6 - - e2fsprogs/libcom-err2@1.46.3-1ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - e2fsprogs/libext2fs2@1.46.3-1ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - e2fsprogs@1.46.3-1ubuntu3 - - e2fsprogs/libext2fs2@1.46.3-1ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - e2fsprogs/libss2@1.46.3-1ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - e2fsprogs@1.46.3-1ubuntu3 - - e2fsprogs/libss2@1.46.3-1ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - e2fsprogs/logsave@1.46.3-1ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - e2fsprogs@1.46.3-1ubuntu3 - - e2fsprogs/logsave@1.46.3-1ubuntu3 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - e2fsprogs@1.46.3-1ubuntu3 - - - -
    • -
    +

    -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream e2fsprogs package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    An out-of-bounds read/write vulnerability was found in e2fsprogs 1.46.5. This issue leads to a segmentation fault and possibly arbitrary code execution via a specially crafted filesystem.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 e2fsprogs to version 1.46.3-1ubuntu3.1 or higher.

    -

    References

    - +

    MPL-2.0 license


    -

    Directory Traversal

    +

    MPL-2.0 license

    @@ -4622,18 +2694,21 @@

    Directory Traversal

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argo-cd/v2 /usr/local/bin/argocd
    • - Vulnerable module: + Package Manager: golang +
    • +
    • + Module: - dpkg + github.com/hashicorp/go-retryablehttp
    • Introduced through: + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-retryablehttp@v0.7.0 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, meta-common-packages@meta and others
    @@ -4645,11 +2720,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + github.com/argoproj/argo-cd/v2@* - meta-common-packages@meta - - dpkg@1.20.9ubuntu2 + github.com/hashicorp/go-retryablehttp@v0.7.0 @@ -4660,33 +2733,17 @@

      Detailed paths


      -

      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream dpkg package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      Dpkg::Source::Archive in dpkg, the Debian package management system, before version 1.21.8, 1.20.10, 1.19.8, 1.18.26 is prone to a directory traversal vulnerability. When extracting untrusted source packages in v2 and v3 source package formats that include a debian.tar, the in-place extraction can lead to directory traversal situations on specially crafted orig.tar and debian.tar tarballs.

      -

      Remediation

      -

      Upgrade Ubuntu:21.10 dpkg to version 1.20.9ubuntu2.2 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Improper Authentication

    +

    MPL-2.0 license

    @@ -4697,18 +2754,21 @@

    Improper Authentication

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argo-cd/v2 /usr/local/bin/argocd
    • - Vulnerable module: + Package Manager: golang +
    • +
    • + Module: - curl/libcurl3-gnutls + github.com/hashicorp/go-cleanhttp
    • Introduced through: + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-cleanhttp@v0.5.2 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others
    @@ -4720,11 +2780,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + github.com/argoproj/argo-cd/v2@* - git@1:2.32.0-1ubuntu1 - - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + github.com/hashicorp/go-cleanhttp@v0.5.2 @@ -4735,31 +2793,17 @@

      Detailed paths


      -

      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream curl package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      An improper authentication vulnerability exists in curl 7.33.0 to and including 7.82.0 which might allow reuse OAUTH2-authenticated connections without properly making sure that the connection was authenticated with the same credentials as set for this transfer. This affects SASL-enabled protocols: SMPTP(S), IMAP(S), POP3(S) and LDAP(S) (openldap only).

      -

      Remediation

      -

      Upgrade Ubuntu:21.10 curl to version 7.74.0-1.3ubuntu2.1 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Insufficiently Protected Credentials

    +

    MPL-2.0 license

    @@ -4770,18 +2814,21 @@

    Insufficiently Protected Credentials

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argo-cd/v2 /usr/local/bin/argocd
    • - Vulnerable module: + Package Manager: golang +
    • +
    • + Module: - curl/libcurl3-gnutls + github.com/gosimple/slug
    • Introduced through: + github.com/argoproj/argo-cd/v2@* and github.com/gosimple/slug@v1.13.1 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others
    @@ -4793,11 +2840,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 + github.com/argoproj/argo-cd/v2@* - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + github.com/gosimple/slug@v1.13.1 @@ -4808,30 +2853,17 @@

      Detailed paths


      -

      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream curl package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      An insufficiently protected credentials vulnerability exists in curl 4.9 to and include curl 7.82.0 are affected that could allow an attacker to extract credentials when follows HTTP(S) redirects is used with authentication could leak credentials to other services that exist on different protocols or port numbers.

      -

      Remediation

      -

      Upgrade Ubuntu:21.10 curl to version 7.74.0-1.3ubuntu2.1 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Improper Certificate Validation

    +

    Denial of Service (DoS)

    @@ -4842,18 +2874,21 @@

    Improper Certificate Validation

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/helm/v3 /usr/local/bin/helm +
    • +
    • + Package Manager: golang
    • Vulnerable module: - curl/libcurl3-gnutls + github.com/docker/distribution/registry/api/v2
    • Introduced through: + helm.sh/helm/v3@* and github.com/docker/distribution/registry/api/v2@v2.8.1+incompatible - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others
    @@ -4865,11 +2900,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 + helm.sh/helm/v3@* - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + github.com/docker/distribution/registry/api/v2@v2.8.1+incompatible @@ -4880,31 +2913,26 @@

      Detailed paths


      -

      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream curl package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      libcurl would reuse a previously created connection even when a TLS or SSHrelated option had been changed that should have prohibited reuse.libcurl keeps previously used connections in a connection pool for subsequenttransfers to reuse if one of them matches the setup. However, several TLS andSSH settings were left out from the configuration match checks, making themmatch too easily.

      +

      Overview

      +

      Affected versions of this package are vulnerable to Denial of Service (DoS) due to improper validation of the value passed to the n parameter in the /v2/_catalog endpoint. + Exploiting this vulnerability is possible by sending a crafted malicious request to the /v2/_catalog API endpoint, which results in an allocation of a massive string array and excessive use of memory.

      Remediation

      -

      Upgrade Ubuntu:21.10 curl to version 7.74.0-1.3ubuntu2.2 or higher.

      +

      Upgrade github.com/docker/distribution/registry/api/v2 to version 2.8.2-beta.1 or higher.

      References


    -

    Out-of-bounds Write

    +

    Resource Exhaustion

    @@ -4915,18 +2943,21 @@

    Out-of-bounds Write

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - curl/libcurl3-gnutls + expat/libexpat1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others + docker-image|quay.io/argoproj/argocd@v2.7.17, git@1:2.34.1-1ubuntu1.10 and others
    @@ -4938,11 +2969,11 @@

    Detailed paths

    -

    Incorrect Default Permissions

    +

    CVE-2024-28757

    @@ -4992,18 +3017,21 @@

    Incorrect Default Permissions

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - curl/libcurl3-gnutls + expat/libexpat1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others + docker-image|quay.io/argoproj/argocd@v2.7.17, git@1:2.34.1-1ubuntu1.10 and others
    @@ -5015,11 +3043,11 @@

    Detailed paths

    -

    Allocation of Resources Without Limits or Throttling

    +

    Out-of-bounds Write

    @@ -5068,18 +3094,21 @@

    Allocation of Resources Without Limits or Throttling

  • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
  • +
  • + Package Manager: ubuntu:22.04
  • Vulnerable module: - curl/libcurl3-gnutls + bash
  • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 and bash@5.1-6ubuntu1 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others
  • @@ -5091,11 +3120,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + bash@5.1-6ubuntu1 @@ -5107,57 +3134,51 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream curl package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      curl < 7.84.0 supports "chained" HTTP compression algorithms, meaning that a serverresponse can be compressed multiple times and potentially with different algorithms. The number of acceptable "links" in this "decompression chain" was unbounded, allowing a malicious server to insert a virtually unlimited number of compression steps.The use of such a decompression chain could result in a "malloc bomb", makingcurl end up spending enormous amounts of allocated heap memory, or trying toand returning out of memory errors.

      +

      Note: Versions mentioned in the description apply only to the upstream bash package and not the bash package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      A flaw was found in the bash package, where a heap-buffer overflow can occur in valid parameter_transform. This issue may lead to memory problems.

      Remediation

      -

      Upgrade Ubuntu:21.10 curl to version 7.74.0-1.3ubuntu2.3 or higher.

      +

      Upgrade Ubuntu:22.04 bash to version 5.1-6ubuntu1.1 or higher.

      References


    -
    -

    Allocation of Resources Without Limits or Throttling

    +
    +

    CVE-2023-7008

    -
    - medium severity +
    + low severity

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - curl/libcurl3-gnutls + systemd/libsystemd0
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 and systemd/libsystemd0@249.11-0ubuntu3.12 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others
    @@ -5169,316 +3190,110 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + systemd/libsystemd0@249.11-0ubuntu3.12
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream curl package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    A malicious server can serve excessive amounts of Set-Cookie: headers in a HTTP response to curl and curl < 7.84.0 stores all of them. A sufficiently large amount of (big) cookies make subsequent HTTP requests to this, or other servers to which the cookies match, create requests that become larger than the threshold that curl uses internally to avoid sending crazy large requests (1048576 bytes) and instead returns an error.This denial state might remain for as long as the same cookies are kept, match and haven't expired. Due to cookie matching rules, a server on foo.example.com can set cookies that also would match for bar.example.com, making it it possible for a "sister server" to effectively cause a denial of service for a sibling site on the same second level domain using this method.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 curl to version 7.74.0-1.3ubuntu2.3 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    NULL Pointer Dereference

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - tar -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, meta-common-packages@meta and others -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - meta-common-packages@meta + apt@2.4.11 - tar@1.34+dfsg-1build1 + systemd/libsystemd0@249.11-0ubuntu3.12
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream tar package.

    -

    pax_decode_header in sparse.c in GNU Tar before 1.32 had a NULL pointer dereference when parsing certain archives that have malformed extended headers.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:21.10 tar.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2021-36690

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - sqlite3/libsqlite3-0 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, gnupg2/gpg@2.2.20-1ubuntu4 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - gnupg2/gpg@2.2.20-1ubuntu4 + procps/libprocps8@2:3.3.17-6ubuntu2.1 - sqlite3/libsqlite3-0@3.35.5-1 + systemd/libsystemd0@249.11-0ubuntu3.12
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream sqlite3 package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    ** DISPUTED ** A segmentation fault can occur in the sqlite3.exe command-line component of SQLite 3.36.0 via the idxGetTableInfo function when there is a crafted SQL query. NOTE: the vendor disputes the relevance of this report because a sqlite3.exe user already has full privileges (e.g., is intentionally allowed to execute commands). This report does NOT imply any problem in the SQLite library.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 sqlite3 to version 3.35.5-1ubuntu0.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2020-9991

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - sqlite3/libsqlite3-0 -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, gnupg2/gpg@2.2.20-1ubuntu4 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - gnupg2/gpg@2.2.20-1ubuntu4 + util-linux@2.37.2-4ubuntu3 - sqlite3/libsqlite3-0@3.35.5-1 + systemd/libsystemd0@249.11-0ubuntu3.12
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream sqlite3 package.

    -

    This issue was addressed with improved checks. This issue is fixed in macOS Big Sur 11.0.1, watchOS 7.0, iOS 14.0 and iPadOS 14.0, iCloud for Windows 7.21, tvOS 14.0. A remote attacker may be able to cause a denial of service.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:21.10 sqlite3.

    -

    References

    - - -
    - - - -
    -
    -

    Information Exposure

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - sqlite3/libsqlite3-0 -
    • - -
    • Introduced through: - +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + util-linux/bsdutils@1:2.37.2-4ubuntu3 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, gnupg2/gpg@2.2.20-1ubuntu4 and others -
    • -
    + +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + -
    +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + systemd/libudev1@249.11-0ubuntu3.12 + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + libfido2/libfido2-1@1.10.0-1 + + systemd/libudev1@249.11-0ubuntu3.12 + + -

    Detailed paths

    +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + util-linux@2.37.2-4ubuntu3 + + systemd/libudev1@249.11-0ubuntu3.12 + + -
  • -

    Time-of-check Time-of-use (TOCTOU)

    +

    Arbitrary Code Injection

    @@ -5526,7 +3340,10 @@

    Time-of-check Time-of-use (TOCTOU)

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: @@ -5536,7 +3353,7 @@

      Time-of-check Time-of-use (TOCTOU)

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and shadow/passwd@1:4.8.1-1ubuntu9 + docker-image|quay.io/argoproj/argocd@v2.7.17 and shadow/passwd@1:4.8.1-2ubuntu2.1
    @@ -5549,53 +3366,40 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - shadow/passwd@1:4.8.1-1ubuntu9 + shadow/passwd@1:4.8.1-2ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-1ubuntu9 + shadow/passwd@1:4.8.1-2ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - openssh/openssh-client@1:8.4p1-6ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - shadow/passwd@1:4.8.1-1ubuntu9 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + openssh/openssh-client@1:8.9p1-3ubuntu0.6 - shadow/login@1:4.8.1-1ubuntu9 + shadow/passwd@1:4.8.1-2ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux/mount@2.36.1-8ubuntu2 - - util-linux@2.36.1-8ubuntu2 - - shadow/login@1:4.8.1-1ubuntu9 + shadow/login@1:4.8.1-2ubuntu2.1 @@ -5607,29 +3411,29 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream shadow package.

      -

      shadow: TOCTOU (time-of-check time-of-use) race condition when copying and removing directory trees

      +

      Note: Versions mentioned in the description apply only to the upstream shadow package and not the shadow package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      In Shadow 4.13, it is possible to inject control characters into fields provided to the SUID program chfn (change finger). Although it is not possible to exploit this directly (e.g., adding a new user fails because \n is in the block list), it is possible to misrepresent the /etc/passwd file when viewed. Use of \r manipulations and Unicode characters to work around blocking of the : character make it possible to give the impression that a new user has been added. In other words, an adversary may be able to convince a system administrator to take the system offline (an indirect, social-engineered denial of service) by demonstrating that "cat /etc/passwd" shows a rogue user account.

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 shadow.

      +

      There is no fixed version for Ubuntu:22.04 shadow.

      References


    -

    Out-of-bounds Read

    +

    Improper Authentication

    @@ -5640,17 +3444,20 @@

    Out-of-bounds Read

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - pcre3/libpcre3 + shadow/passwd
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and pcre3/libpcre3@2:8.39-13build3 + docker-image|quay.io/argoproj/argocd@v2.7.17 and shadow/passwd@1:4.8.1-2ubuntu2.1
    @@ -5663,20 +3470,40 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + adduser@3.118ubuntu5 - pcre3/libpcre3@2:8.39-13build3 + shadow/passwd@1:4.8.1-2ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 - grep@3.7-0ubuntu1 + shadow/passwd@1:4.8.1-2ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 - pcre3/libpcre3@2:8.39-13build3 + shadow/login@1:4.8.1-2ubuntu2.1 @@ -5688,28 +3515,24 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream pcre3 package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      libpcre in PCRE before 8.43 allows a subject buffer over-read in JIT when UTF is disabled, and \X or \R has more than one fixed quantifier, a related issue to CVE-2019-20454.

      +

      Note: Versions mentioned in the description apply only to the upstream shadow package and not the shadow package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      A flaw was found in shadow-utils. When asking for a new password, shadow-utils asks the password twice. If the password fails on the second attempt, shadow-utils fails in cleaning the buffer used to store the first entry. This may allow an attacker with enough access to retrieve the password from the memory.

      Remediation

      -

      Upgrade Ubuntu:21.10 pcre3 to version 2:8.39-13ubuntu0.21.10.1 or higher.

      +

      Upgrade Ubuntu:22.04 shadow to version 1:4.8.1-2ubuntu2.2 or higher.

      References


    @@ -5725,7 +3548,10 @@

    Uncontrolled Recursion

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: @@ -5735,7 +3561,7 @@

      Uncontrolled Recursion

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and pcre3/libpcre3@2:8.39-13build3 + docker-image|quay.io/argoproj/argocd@v2.7.17 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1
    @@ -5748,20 +3574,20 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - pcre3/libpcre3@2:8.39-13build3 + pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - grep@3.7-0ubuntu1 + grep@3.7-1build1 - pcre3/libpcre3@2:8.39-13build3 + pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 @@ -5773,10 +3599,11 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream pcre3 package.

      +

      Note: Versions mentioned in the description apply only to the upstream pcre3 package and not the pcre3 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      In PCRE 8.41, the OP_KETRMAX feature in the match function in pcre_exec.c allows stack exhaustion (uncontrolled recursion) when processing a crafted regular expression.

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 pcre3.

      +

      There is no fixed version for Ubuntu:22.04 pcre3.

      References


    -

    Out-of-bounds Read

    +

    Release of Invalid Pointer or Reference

    @@ -5806,18 +3636,21 @@

    Out-of-bounds Read

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - pcre2/libpcre2-8-0 + patch
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 and patch@2.7.6-7build2 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, meta-common-packages@meta and others
    @@ -5829,11 +3662,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - meta-common-packages@meta + docker-image|quay.io/argoproj/argocd@v2.7.17 - pcre2/libpcre2-8-0@10.37-0ubuntu2 + patch@2.7.6-7build2 @@ -5845,31 +3676,26 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream pcre2 package.

      -

      An out-of-bounds read vulnerability was discovered in the PCRE2 library in the get_recurse_data_length() function of the pcre2_jit_compile.c file. This issue affects recursions in JIT-compiled regular expressions caused by duplicate data transfers.

      +

      Note: Versions mentioned in the description apply only to the upstream patch package and not the patch package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      An Invalid Pointer vulnerability exists in GNU patch 2.7 via the another_hunk function, which causes a Denial of Service.

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 pcre2.

      +

      There is no fixed version for Ubuntu:22.04 patch.

      References


    -

    Out-of-bounds Read

    +

    Double Free

    @@ -5880,18 +3706,21 @@

    Out-of-bounds Read

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - pcre2/libpcre2-8-0 + patch
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 and patch@2.7.6-7build2 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, meta-common-packages@meta and others
    @@ -5903,11 +3732,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - meta-common-packages@meta + docker-image|quay.io/argoproj/argocd@v2.7.17 - pcre2/libpcre2-8-0@10.37-0ubuntu2 + patch@2.7.6-7build2 @@ -5919,32 +3746,31 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream pcre2 package.

      -

      An out-of-bounds read vulnerability was discovered in the PCRE2 library in the compile_xclass_matchingpath() function of the pcre2_jit_compile.c file. This involves a unicode property matching issue in JIT-compiled regular expressions. The issue occurs because the character was not fully read in case-less matching within JIT.

      +

      Note: Versions mentioned in the description apply only to the upstream patch package and not the patch package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      A double free exists in the another_hunk function in pch.c in GNU patch through 2.7.6.

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 pcre2.

      +

      There is no fixed version for Ubuntu:22.04 patch.

      References


    -

    Double Free

    +

    Improper Check for Unusual or Exceptional Conditions

    @@ -5955,17 +3781,20 @@

    Double Free

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - patch + openssl/libssl3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and patch@2.7.6-7 + docker-image|quay.io/argoproj/argocd@v2.7.17 and openssl/libssl3@3.0.2-0ubuntu1.13
    @@ -5978,9 +3807,113 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + libfido2/libfido2-1@1.10.0-1 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + ca-certificates@20230311ubuntu0.22.04.1 + + openssl@3.0.2-0ubuntu1.13 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssl@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 - patch@2.7.6-7 + ca-certificates@20230311ubuntu0.22.04.1 + + openssl@3.0.2-0ubuntu1.13 @@ -5992,30 +3925,55 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream patch package.

      -

      A double free exists in the another_hunk function in pch.c in GNU patch through 2.7.6.

      +

      Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      Issue summary: Generating excessively long X9.42 DH keys or checking + excessively long X9.42 DH keys or parameters may be very slow.

      +

      Impact summary: Applications that use the functions DH_generate_key() to + generate an X9.42 DH key may experience long delays. Likewise, applications + that use DH_check_pub_key(), DH_check_pub_key_ex() or EVP_PKEY_public_check() + to check an X9.42 DH key or X9.42 DH parameters may experience long delays. + Where the key or parameters that are being checked have been obtained from + an untrusted source this may lead to a Denial of Service.

      +

      While DH_check() performs all the necessary checks (as of CVE-2023-3817), + DH_check_pub_key() doesn't make any of these checks, and is therefore + vulnerable for excessively large P and Q parameters.

      +

      Likewise, while DH_generate_key() performs a check for an excessively large + P, it doesn't check for an excessively large Q.

      +

      An application that calls DH_generate_key() or DH_check_pub_key() and + supplies a key or parameters obtained from an untrusted source could be + vulnerable to a Denial of Service attack.

      +

      DH_generate_key() and DH_check_pub_key() are also called by a number of + other OpenSSL functions. An application calling any of those other + functions may similarly be affected. The other functions affected by this + are DH_check_pub_key_ex(), EVP_PKEY_public_check(), and EVP_PKEY_generate().

      +

      Also vulnerable are the OpenSSL pkey command line application when using the + "-pubcheck" option, as well as the OpenSSL genpkey command line application.

      +

      The OpenSSL SSL/TLS implementation is not affected by this issue.

      +

      The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 patch.

      +

      Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.14 or higher.

      References


    -

    Release of Invalid Pointer or Reference

    +

    Out-of-bounds Write

    @@ -6026,17 +3984,20 @@

    Release of Invalid Pointer or Reference

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - patch + openssl/libssl3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and patch@2.7.6-7 + docker-image|quay.io/argoproj/argocd@v2.7.17 and openssl/libssl3@3.0.2-0ubuntu1.13
    @@ -6046,12 +4007,116 @@

    Release of Invalid Pointer or Reference

    Detailed paths

    -
      +
        +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + libfido2/libfido2-1@1.10.0-1 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + ca-certificates@20230311ubuntu0.22.04.1 + + openssl@3.0.2-0ubuntu1.13 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssl@3.0.2-0ubuntu1.13 + + + +
      • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - patch@2.7.6-7 + ca-certificates@20230311ubuntu0.22.04.1 + + openssl@3.0.2-0ubuntu1.13 @@ -6063,25 +4128,57 @@

        Detailed paths


        NVD Description

        -

        Note: Versions mentioned in the description apply to the upstream patch package.

        -

        An Invalid Pointer vulnerability exists in GNU patch 2.7 via the another_hunk function, which causes a Denial of Service.

        +

        Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

        +

        Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

        +

        Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

        +

        The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

        +

        The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

        +

        The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

        Remediation

        -

        There is no fixed version for Ubuntu:21.10 patch.

        +

        Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.14 or higher.

        References


    -

    CVE-2021-41617

    +

    CVE-2023-6237

    @@ -6092,17 +4189,20 @@

    CVE-2021-41617

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - openssh/openssh-client + openssl/libssl3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and openssh/openssh-client@1:8.4p1-6ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 and openssl/libssl3@3.0.2-0ubuntu1.13
    @@ -6115,9 +4215,113 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + libfido2/libfido2-1@1.10.0-1 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + ca-certificates@20230311ubuntu0.22.04.1 + + openssl@3.0.2-0ubuntu1.13 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssl@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + ca-certificates@20230311ubuntu0.22.04.1 - openssh/openssh-client@1:8.4p1-6ubuntu2.1 + openssl@3.0.2-0ubuntu1.13 @@ -6129,35 +4333,23 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream openssh package.

      -

      sshd in OpenSSH 6.2 through 8.x before 8.8, when certain non-default configurations are used, allows privilege escalation because supplemental groups are not initialized as expected. Helper programs for AuthorizedKeysCommand and AuthorizedPrincipalsCommand may run with privileges associated with group memberships of the sshd process, if the configuration specifies running the command as a different user.

      +

      This vulnerability has not been analyzed by NVD yet.

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 openssh.

      +

      Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.14 or higher.

      References


    -

    Information Exposure

    +

    CVE-2024-0727

    @@ -6168,17 +4360,20 @@

    Information Exposure

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - openssh/openssh-client + openssl/libssl3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and openssh/openssh-client@1:8.4p1-6ubuntu2.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 and openssl/libssl3@3.0.2-0ubuntu1.13
    @@ -6191,9 +4386,113 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + cyrus-sasl2/libsasl2-modules@2.1.27+dfsg2-3ubuntu1.2 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + libfido2/libfido2-1@1.10.0-1 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + ca-certificates@20230311ubuntu0.22.04.1 + + openssl@3.0.2-0ubuntu1.13 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.1 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + openssl/libssl3@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + openssl@3.0.2-0ubuntu1.13 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 - openssh/openssh-client@1:8.4p1-6ubuntu2.1 + ca-certificates@20230311ubuntu0.22.04.1 + + openssl@3.0.2-0ubuntu1.13 @@ -6205,32 +4504,47 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream openssh package.

      -

      The client side in OpenSSH 5.7 through 8.4 has an Observable Discrepancy leading to an information leak in the algorithm negotiation. This allows man-in-the-middle attackers to target initial connection attempts (where no host key for the server has been cached by the client). NOTE: some reports state that 8.5 and 8.6 are also affected.

      +

      Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

      +

      Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

      +

      A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

      +

      OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

      +

      We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

      +

      The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 openssh.

      +

      Upgrade Ubuntu:22.04 openssl to version 3.0.2-0ubuntu1.14 or higher.

      References


    -

    Out-of-bounds Read

    +

    CVE-2023-50495

    @@ -6241,7 +4555,10 @@

    Out-of-bounds Read

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: @@ -6251,7 +4568,7 @@

      Out-of-bounds Read

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and ncurses/libtinfo6@6.2+20201114-2build1 + docker-image|quay.io/argoproj/argocd@v2.7.17 and ncurses/libtinfo6@6.3-2ubuntu0.1
    @@ -6264,202 +4581,200 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - ncurses/libtinfo6@6.2+20201114-2build1 + ncurses/libtinfo6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - bash@5.1-3ubuntu2 + bash@5.1-6ubuntu1 - ncurses/libtinfo6@6.2+20201114-2build1 + ncurses/libtinfo6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - ncurses/libncursesw6@6.2+20201114-2build1 + ncurses/libncursesw6@6.3-2ubuntu0.1 - ncurses/libtinfo6@6.2+20201114-2build1 + ncurses/libtinfo6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - less@551-2 + less@590-1ubuntu0.22.04.1 - ncurses/libtinfo6@6.2+20201114-2build1 + ncurses/libtinfo6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - libedit/libedit2@3.1-20191231-2build1 + libedit/libedit2@3.1-20210910-1build1 - ncurses/libtinfo6@6.2+20201114-2build1 + ncurses/libtinfo6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - ncurses/libncurses6@6.2+20201114-2build1 + ncurses/libncurses6@6.3-2ubuntu0.1 - ncurses/libtinfo6@6.2+20201114-2build1 + ncurses/libtinfo6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - ncurses/ncurses-bin@6.2+20201114-2build1 + ncurses/ncurses-bin@6.3-2ubuntu0.1 - ncurses/libtinfo6@6.2+20201114-2build1 + ncurses/libtinfo6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - procps@2:3.3.17-5ubuntu3 + procps@2:3.3.17-6ubuntu2.1 - ncurses/libtinfo6@6.2+20201114-2build1 + ncurses/libtinfo6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - util-linux/mount@2.36.1-8ubuntu2 + docker-image|quay.io/argoproj/argocd@v2.7.17 - util-linux@2.36.1-8ubuntu2 + util-linux@2.37.2-4ubuntu3 - ncurses/libtinfo6@6.2+20201114-2build1 + ncurses/libtinfo6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - gnupg2/gpg@2.2.20-1ubuntu4 + gnupg2/gpg@2.2.27-3ubuntu2.1 - gnupg2/gpgconf@2.2.20-1ubuntu4 + gnupg2/gpgconf@2.2.27-3ubuntu2.1 - readline/libreadline8@8.1-2 + readline/libreadline8@8.1.2-1 - ncurses/libtinfo6@6.2+20201114-2build1 + ncurses/libtinfo6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - gnupg2/gnupg@2.2.20-1ubuntu4 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - gnupg2/gpg-agent@2.2.20-1ubuntu4 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - pinentry/pinentry-curses@1.1.1-1 + pinentry/pinentry-curses@1.1.1-1build2 - ncurses/libtinfo6@6.2+20201114-2build1 + ncurses/libtinfo6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - ncurses/libncursesw6@6.2+20201114-2build1 + ncurses/libncursesw6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - procps@2:3.3.17-5ubuntu3 + procps@2:3.3.17-6ubuntu2.1 - ncurses/libncursesw6@6.2+20201114-2build1 + ncurses/libncursesw6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - gnupg2/gnupg@2.2.20-1ubuntu4 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - gnupg2/gpg-agent@2.2.20-1ubuntu4 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - pinentry/pinentry-curses@1.1.1-1 + pinentry/pinentry-curses@1.1.1-1build2 - ncurses/libncursesw6@6.2+20201114-2build1 + ncurses/libncursesw6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - ncurses/libncurses6@6.2+20201114-2build1 + ncurses/libncurses6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - procps@2:3.3.17-5ubuntu3 + procps@2:3.3.17-6ubuntu2.1 - ncurses/libncurses6@6.2+20201114-2build1 + ncurses/libncurses6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - ncurses/ncurses-base@6.2+20201114-2build1 + ncurses/ncurses-base@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - ncurses/ncurses-bin@6.2+20201114-2build1 + ncurses/ncurses-bin@6.3-2ubuntu0.1 @@ -6471,29 +4786,29 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream ncurses package.

      -

      ncurses 6.3 before patch 20220416 has an out-of-bounds read and segmentation violation in convert_strings in tinfo/read_entry.c in the terminfo library.

      +

      Note: Versions mentioned in the description apply only to the upstream ncurses package and not the ncurses package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      NCurse v6.4-20230418 was discovered to contain a segmentation fault via the component _nc_wrap_entry().

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 ncurses.

      +

      There is no fixed version for Ubuntu:22.04 ncurses.

      References


    -

    Out-of-bounds Read

    +

    CVE-2023-45918

    @@ -6504,17 +4819,20 @@

    Out-of-bounds Read

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - libsepol/libsepol1 + ncurses/libtinfo6
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and libsepol/libsepol1@3.1-1ubuntu2 + docker-image|quay.io/argoproj/argocd@v2.7.17 and ncurses/libtinfo6@6.3-2ubuntu0.1
    @@ -6524,199 +4842,203 @@

    Out-of-bounds Read

    Detailed paths

    -
      +
        +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + bash@5.1-6ubuntu1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + less@590-1ubuntu0.22.04.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + libedit/libedit2@3.1-20210910-1build1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + ncurses/ncurses-bin@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + util-linux@2.37.2-4ubuntu3 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + readline/libreadline8@8.1.2-1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + pinentry/pinentry-curses@1.1.1-1build2 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
      • +
      • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
      • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + procps@2:3.3.17-6ubuntu2.1 - libsepol/libsepol1@3.1-1ubuntu2 + ncurses/libncursesw6@6.3-2ubuntu0.1
      • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - adduser@3.118ubuntu5 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - shadow/passwd@1:4.8.1-1ubuntu9 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - libsemanage/libsemanage1@3.1-1ubuntu2 + pinentry/pinentry-curses@1.1.1-1build2 - libsepol/libsepol1@3.1-1ubuntu2 + ncurses/libncursesw6@6.3-2ubuntu0.1
      • -
      - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream libsepol package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    The CIL compiler in SELinux 3.2 has a heap-based buffer over-read in ebitmap_match_any (called indirectly from cil_check_neverallow). This occurs because there is sometimes a lack of checks for invalid statements in an optional block.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 libsepol to version 3.1-1ubuntu2.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Use After Free

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - libsepol/libsepol1 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and libsepol/libsepol1@3.1-1ubuntu2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - libsepol/libsepol1@3.1-1ubuntu2 + ncurses/libncurses6@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-1ubuntu9 + procps@2:3.3.17-6ubuntu2.1 - libsemanage/libsemanage1@3.1-1ubuntu2 - - libsepol/libsepol1@3.1-1ubuntu2 + ncurses/libncurses6@6.3-2ubuntu0.1
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream libsepol package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    The CIL compiler in SELinux 3.2 has a use-after-free in __cil_verify_classperms (called from __verify_map_perm_classperms and hashtab_map).

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 libsepol to version 3.1-1ubuntu2.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Use After Free

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - libsepol/libsepol1 -
    • - -
    • Introduced through: - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and libsepol/libsepol1@3.1-1ubuntu2 - -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - libsepol/libsepol1@3.1-1ubuntu2 + ncurses/ncurses-base@6.3-2ubuntu0.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - adduser@3.118ubuntu5 + docker-image|quay.io/argoproj/argocd@v2.7.17 - shadow/passwd@1:4.8.1-1ubuntu9 - - libsemanage/libsemanage1@3.1-1ubuntu2 - - libsepol/libsepol1@3.1-1ubuntu2 + ncurses/ncurses-bin@6.3-2ubuntu0.1 @@ -6728,29 +5050,27 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream libsepol package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      The CIL compiler in SELinux 3.2 has a use-after-free in cil_reset_classpermission (called from cil_reset_classperms_set and cil_reset_classperms_list).

      +

      Note: Versions mentioned in the description apply only to the upstream ncurses package and not the ncurses package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      ncurses 6.4-20230610 has a NULL pointer dereference in tgetstr in tinfo/lib_termcap.c.

      Remediation

      -

      Upgrade Ubuntu:21.10 libsepol to version 3.1-1ubuntu2.1 or higher.

      +

      There is no fixed version for Ubuntu:22.04 ncurses.

      References


    -

    Use After Free

    +

    Resource Exhaustion

    @@ -6761,17 +5081,20 @@

    Use After Free

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - libsepol/libsepol1 + libzstd/libzstd1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and libsepol/libsepol1@3.1-1ubuntu2 + docker-image|quay.io/argoproj/argocd@v2.7.17 and libzstd/libzstd1@1.4.8+dfsg-3build1
    @@ -6784,24 +5107,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - libsepol/libsepol1@3.1-1ubuntu2 - - - -
    • -
    • - Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - adduser@3.118ubuntu5 - - shadow/passwd@1:4.8.1-1ubuntu9 - - libsemanage/libsemanage1@3.1-1ubuntu2 - - libsepol/libsepol1@3.1-1ubuntu2 + libzstd/libzstd1@1.4.8+dfsg-3build1 @@ -6813,24 +5121,28 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream libsepol package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      The CIL compiler in SELinux 3.2 has a use-after-free in __cil_verify_classperms (called from __cil_verify_classpermission and __cil_pre_verify_helper).

      +

      Note: Versions mentioned in the description apply only to the upstream libzstd package and not the libzstd package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      A vulnerability was found in zstd v1.4.10, where an attacker can supply empty string as an argument to the command line tool to cause buffer overrun.

      Remediation

      -

      Upgrade Ubuntu:21.10 libsepol to version 3.1-1ubuntu2.1 or higher.

      +

      There is no fixed version for Ubuntu:22.04 libzstd.

      References


    @@ -6846,7 +5158,10 @@

    Integer Overflow or Wraparound

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: @@ -6856,7 +5171,7 @@

      Integer Overflow or Wraparound

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and krb5/libk5crypto3@1.18.3-6 + docker-image|quay.io/argoproj/argocd@v2.7.17 and krb5/libk5crypto3@1.19.2-2ubuntu0.3
    @@ -6869,161 +5184,159 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - krb5/libk5crypto3@1.18.3-6 + krb5/libk5crypto3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-1ubuntu9 + shadow/passwd@1:4.8.1-2ubuntu2.1 - pam/libpam-modules@1.3.1-5ubuntu11 + pam/libpam-modules@1.4.0-11ubuntu2.4 - libnsl/libnsl2@1.3.0-2build1 + libnsl/libnsl2@1.3.0-2build2 - libtirpc/libtirpc3@1.3.2-2 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libgssapi-krb5-2@1.18.3-6 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - krb5/libk5crypto3@1.18.3-6 + krb5/libk5crypto3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-1ubuntu9 + shadow/passwd@1:4.8.1-2ubuntu2.1 - pam/libpam-modules@1.3.1-5ubuntu11 + pam/libpam-modules@1.4.0-11ubuntu2.4 - libnsl/libnsl2@1.3.0-2build1 + libnsl/libnsl2@1.3.0-2build2 - libtirpc/libtirpc3@1.3.2-2 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libgssapi-krb5-2@1.18.3-6 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - krb5/libkrb5-3@1.18.3-6 + krb5/libkrb5-3@1.19.2-2ubuntu0.3 - krb5/libk5crypto3@1.18.3-6 + krb5/libk5crypto3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - krb5/libkrb5-3@1.18.3-6 + krb5/libkrb5-3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-1ubuntu9 + shadow/passwd@1:4.8.1-2ubuntu2.1 - pam/libpam-modules@1.3.1-5ubuntu11 + pam/libpam-modules@1.4.0-11ubuntu2.4 - libnsl/libnsl2@1.3.0-2build1 + libnsl/libnsl2@1.3.0-2build2 - libtirpc/libtirpc3@1.3.2-2 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libgssapi-krb5-2@1.18.3-6 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 - krb5/libkrb5-3@1.18.3-6 + krb5/libkrb5-3@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - krb5/libgssapi-krb5-2@1.18.3-6 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - openssh/openssh-client@1:8.4p1-6ubuntu2.1 + openssh/openssh-client@1:8.9p1-3ubuntu0.6 - krb5/libgssapi-krb5-2@1.18.3-6 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git@1:2.32.0-1ubuntu1 + git@1:2.34.1-1ubuntu1.10 - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - krb5/libgssapi-krb5-2@1.18.3-6 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git@1:2.32.0-1ubuntu1 + git@1:2.34.1-1ubuntu1.10 - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 - libssh/libssh-4@0.9.6-1 + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 - krb5/libgssapi-krb5-2@1.18.3-6 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 adduser@3.118ubuntu5 - shadow/passwd@1:4.8.1-1ubuntu9 + shadow/passwd@1:4.8.1-2ubuntu2.1 - pam/libpam-modules@1.3.1-5ubuntu11 + pam/libpam-modules@1.4.0-11ubuntu2.4 - libnsl/libnsl2@1.3.0-2build1 + libnsl/libnsl2@1.3.0-2build2 - libtirpc/libtirpc3@1.3.2-2 + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 - krb5/libgssapi-krb5-2@1.18.3-6 + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - meta-common-packages@meta - - krb5/libkrb5support0@1.18.3-6 + krb5/libkrb5support0@1.19.2-2ubuntu0.3 @@ -7035,28 +5348,30 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream krb5 package.

      +

      Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      An issue was discovered in MIT Kerberos 5 (aka krb5) through 1.16. There is a variable "dbentry->n_key_data" in kadmin/dbutil/dump.c that can store 16-bit data but unknowingly the developer has assigned a "u4" variable to it, which is for 32-bit data. An attacker can use this vulnerability to affect other artifacts of the database as we know that a Kerberos database dump file contains trusted data.

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 krb5.

      +

      There is no fixed version for Ubuntu:22.04 krb5.

      References


    -

    Integer Overflow or Wraparound

    +

    Out-of-bounds Write

    @@ -7067,17 +5382,20 @@

    Integer Overflow or Wraparound

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - gmp/libgmp10 + gnupg2/gpgv
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and gmp/libgmp10@2:6.2.1+dfsg-1ubuntu2 + docker-image|quay.io/argoproj/argocd@v2.7.17 and gnupg2/gpgv@2.2.27-3ubuntu2.1
    @@ -7090,227 +5408,313 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - gmp/libgmp10@2:6.2.1+dfsg-1ubuntu2 + gnupg2/gpgv@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - coreutils@8.32-4ubuntu2 + apt@2.4.11 - gmp/libgmp10@2:6.2.1+dfsg-1ubuntu2 + gnupg2/gpgv@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - apt@2.3.9 + docker-image|quay.io/argoproj/argocd@v2.7.17 - gnutls28/libgnutls30@3.7.1-5ubuntu1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - gmp/libgmp10@2:6.2.1+dfsg-1ubuntu2 + gnupg2/gpgv@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - apt@2.3.9 + gnupg2/dirmngr@2.2.27-3ubuntu2.1 - gnutls28/libgnutls30@3.7.1-5ubuntu1 + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 - nettle/libhogweed6@3.7.3-1 + gnupg2/gpg@2.2.27-3ubuntu2.1 - gmp/libgmp10@2:6.2.1+dfsg-1ubuntu2 + gnupg2/gpgconf@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git@1:2.32.0-1ubuntu1 + gnupg2/gnupg@2.2.27-3ubuntu2.1 - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 - rtmpdump/librtmp1@2.4+20151223.gitfa8646d.1-2build3 - - gmp/libgmp10@2:6.2.1+dfsg-1ubuntu2 + gnupg2/gpgconf@2.2.27-3ubuntu2.1
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream gmp package.

    -

    GNU Multiple Precision Arithmetic Library (GMP) through 6.2.1 has an mpz/inp_raw.c integer overflow and resultant buffer overflow via crafted input, leading to a segmentation fault on 32-bit platforms.

    -

    Remediation

    -

    There is no fixed version for Ubuntu:21.10 gmp.

    -

    References

    - - -
    - - +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + -
  • -
    -

    Buffer Overflow

    -
    + +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + -
    - low severity -
    +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + -
    +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 + + - glibc/libc-bin -
    • + +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 + + -
    • Introduced through: +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 + + - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and glibc/libc-bin@2.34-0ubuntu3 +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 + + -
    • -
    +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + -
    +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + -

    Detailed paths

    +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + -
      +
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - glibc/libc-bin@2.34-0ubuntu3 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - meta-common-packages@meta + gnupg2/gnupg@2.2.27-3ubuntu2.1 - glibc/libc6@2.34-0ubuntu3 + gnupg2/gpg-agent@2.2.27-3ubuntu2.1
    • -
    - -
  • - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream glibc package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    The deprecated compatibility function clnt_create in the sunrpc module of the GNU C Library (aka glibc) through 2.34 copies its hostname argument on the stack without validating its length, which may result in a buffer overflow, potentially resulting in a denial of service or (if an application is not built with a stack protector enabled) arbitrary code execution.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 glibc to version 2.34-0ubuntu3.2 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Buffer Overflow

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - glibc/libc-bin -
    • - -
    • Introduced through: +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and glibc/libc-bin@2.34-0ubuntu3 +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + -
    • -
    + +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + -
    +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + -

    Detailed paths

    +
  • +
  • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + -
      +
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - glibc/libc-bin@2.34-0ubuntu3 + gnupg2/gpgsm@2.2.27-3ubuntu2.1
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 - meta-common-packages@meta + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 - glibc/libc6@2.34-0ubuntu3 + gnupg2/gnupg@2.2.27-3ubuntu2.1 @@ -7322,24 +5726,26 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream glibc package. - See How to fix? for Ubuntu:21.10 relevant versions.

      -

      The deprecated compatibility function svcunix_create in the sunrpc module of the GNU C Library (aka glibc) through 2.34 copies its path argument on the stack without validating its length, which may result in a buffer overflow, potentially resulting in a denial of service or (if an application is not built with a stack protector enabled) arbitrary code execution.

      +

      Note: Versions mentioned in the description apply only to the upstream gnupg2 package and not the gnupg2 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      +

      GnuPG can be made to spin on a relatively small input by (for example) crafting a public key with thousands of signatures attached, compressed down to just a few KB.

      Remediation

      -

      Upgrade Ubuntu:21.10 glibc to version 2.34-0ubuntu3.2 or higher.

      +

      There is no fixed version for Ubuntu:22.04 gnupg2.

      References


  • @@ -7355,7 +5761,10 @@

    Allocation of Resources Without Limits or Throttling

  • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
  • +
  • + Package Manager: ubuntu:22.04
  • Vulnerable module: @@ -7365,7 +5774,7 @@

    Allocation of Resources Without Limits or Throttling

    Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and glibc/libc-bin@2.34-0ubuntu3 + docker-image|quay.io/argoproj/argocd@v2.7.17 and glibc/libc-bin@2.35-0ubuntu3.6
  • @@ -7378,20 +5787,18 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - glibc/libc-bin@2.34-0ubuntu3 + glibc/libc-bin@2.35-0ubuntu3.6
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - meta-common-packages@meta - - glibc/libc6@2.34-0ubuntu3 + glibc/libc6@2.35-0ubuntu3.6 @@ -7403,22 +5810,23 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream glibc package.

      +

      Note: Versions mentioned in the description apply only to the upstream glibc package and not the glibc package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      sha256crypt and sha512crypt through 0.6 allow attackers to cause a denial of service (CPU consumption) because the algorithm's runtime is proportional to the square of the length of the password.

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 glibc.

      +

      There is no fixed version for Ubuntu:22.04 glibc.

      References


    @@ -7434,7 +5842,10 @@

    Improper Input Validation

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: @@ -7445,7 +5856,7 @@

      Improper Input Validation

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others + docker-image|quay.io/argoproj/argocd@v2.7.17, git@1:2.34.1-1ubuntu1.10 and others
    @@ -7457,31 +5868,31 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git@1:2.32.0-1ubuntu1 + git@1:2.34.1-1ubuntu1.10 - git/git-man@1:2.32.0-1ubuntu1 + git/git-man@1:2.34.1-1ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git@1:2.32.0-1ubuntu1 + git@1:2.34.1-1ubuntu1.10
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git-lfs@2.13.2-1 + git-lfs@3.0.2-1ubuntu0.2 - git@1:2.32.0-1ubuntu1 + git@1:2.34.1-1ubuntu1.10 @@ -7493,10 +5904,11 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream git package.

      +

      Note: Versions mentioned in the description apply only to the upstream git package and not the git package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      GIT version 2.15.1 and earlier contains a Input Validation Error vulnerability in Client that can result in problems including messing up terminal configuration to RCE. This attack appear to be exploitable via The user must interact with a malicious git server, (or have their traffic modified in a MITM attack).

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 git.

      +

      There is no fixed version for Ubuntu:22.04 git.

      References

    -

    Incorrect Calculation

    +

    Uncontrolled Recursion

    @@ -7523,18 +5935,21 @@

    Incorrect Calculation

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: - expat/libexpat1 + gcc-12/libstdc++6
    • Introduced through: + docker-image|quay.io/argoproj/argocd@v2.7.17 and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others
    @@ -7546,235 +5961,51 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 - - git@1:2.32.0-1ubuntu1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - expat/libexpat1@2.4.1-2 + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream expat package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    In Expat (aka libexpat) before 2.4.3, a left shift by 29 (or more) places in the storeAtts function in xmlparse.c can lead to realloc misbehavior (e.g., allocating too few bytes, or only freeing memory).

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 expat to version 2.4.1-2ubuntu0.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    CVE-2022-27775

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - curl/libcurl3-gnutls -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git@1:2.32.0-1ubuntu1 + apt@2.4.11 - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream curl package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    An information disclosure vulnerability exists in curl 7.65.0 to 7.82.0 are vulnerable that by using an IPv6 address that was in the connection pool but with a different zone id it could reuse a connection instead.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 curl to version 7.74.0-1.3ubuntu2.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Insufficiently Protected Credentials

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - curl/libcurl3-gnutls -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - git@1:2.32.0-1ubuntu1 + apt@2.4.11 - curl/libcurl3-gnutls@7.74.0-1.3ubuntu2 + apt/libapt-pkg6.0@2.4.11 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04
    • -
    - -
    - -
    - -

    NVD Description

    -

    Note: Versions mentioned in the description apply to the upstream curl package. - See How to fix? for Ubuntu:21.10 relevant versions.

    -

    A insufficiently protected credentials vulnerability in fixed in curl 7.83.0 might leak authentication or cookie header data on HTTP redirects to the same host but another port number.

    -

    Remediation

    -

    Upgrade Ubuntu:21.10 curl to version 7.74.0-1.3ubuntu2.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Loop with Unreachable Exit Condition ('Infinite Loop')

    -
    - -
    - low severity -
    - -
    - -
      -
    • - Package Manager: ubuntu:21.10 -
    • -
    • - Vulnerable module: - - curl/libcurl3-gnutls -
    • - -
    • Introduced through: - - - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1, git@1:2.32.0-1ubuntu1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
    @@ -7820,7 +6050,10 @@

    Improper Input Validation

    • - Package Manager: ubuntu:21.10 + Manifest file: quay.io/argoproj/argocd:v2.7.17/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04
    • Vulnerable module: @@ -7830,7 +6063,7 @@

      Improper Input Validation

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 and coreutils@8.32-4ubuntu2 + docker-image|quay.io/argoproj/argocd@v2.7.17 and coreutils@8.32-4.1ubuntu1
    @@ -7843,9 +6076,9 @@

    Detailed paths

    • Introduced through: - docker-image|quay.io/argoproj/argocd-applicationset@v0.4.1 + docker-image|quay.io/argoproj/argocd@v2.7.17 - coreutils@8.32-4ubuntu2 + coreutils@8.32-4.1ubuntu1 @@ -7857,10 +6090,11 @@

      Detailed paths


      NVD Description

      -

      Note: Versions mentioned in the description apply to the upstream coreutils package.

      +

      Note: Versions mentioned in the description apply only to the upstream coreutils package and not the coreutils package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

      chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.

      Remediation

      -

      There is no fixed version for Ubuntu:21.10 coreutils.

      +

      There is no fixed version for Ubuntu:22.04 coreutils.

      References


    diff --git a/docs/snyk/v2.7.17/redis_7.0.14-alpine.html b/docs/snyk/v2.7.17/redis_7.0.14-alpine.html new file mode 100644 index 0000000000000..ea9cd5f9152fd --- /dev/null +++ b/docs/snyk/v2.7.17/redis_7.0.14-alpine.html @@ -0,0 +1,993 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    March 24th 2024, 12:22:21 am (UTC+00:00)

    +
    +
    + Scanned the following paths: +
      +
    • redis:7.0.14-alpine (apk)
    • +
    • redis:7.0.14-alpine/tianon/gosu//usr/local/bin/gosu (gomodules)
    • +
    +
    + +
    +
    3 known vulnerabilities
    +
    27 vulnerable dependency paths
    +
    19 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.19 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.14-alpine and openssl/libcrypto3@3.1.4-r2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + busybox/ssl_client@1.36.1-r15 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libssl3@3.1.4-r2 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + busybox/ssl_client@1.36.1-r15 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.19 relevant fixed versions and status.

    +

    Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

    +

    Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

    +

    The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

    +

    The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

    +

    The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

    +

    Remediation

    +

    Upgrade Alpine:3.19 openssl to version 3.1.4-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-0727

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.19 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.14-alpine and openssl/libcrypto3@3.1.4-r2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + busybox/ssl_client@1.36.1-r15 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libssl3@3.1.4-r2 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + busybox/ssl_client@1.36.1-r15 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.19 relevant fixed versions and status.

    +

    Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

    +

    Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

    +

    A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

    +

    OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

    +

    We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

    +

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.19 openssl to version 3.1.4-r5 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-6237

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.19 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.14-alpine and openssl/libcrypto3@3.1.4-r2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + busybox/ssl_client@1.36.1-r15 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libssl3@3.1.4-r2 + + openssl/libcrypto3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + .redis-rundeps@20231208.201137 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + apk-tools/apk-tools@2.14.0-r5 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.14-alpine + + busybox/ssl_client@1.36.1-r15 + + openssl/libssl3@3.1.4-r2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    This vulnerability has not been analyzed by NVD yet.

    +

    Remediation

    +

    Upgrade Alpine:3.19 openssl to version 3.1.4-r4 or higher.

    + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.4.19/argocd-iac-install.html b/docs/snyk/v2.8.13/argocd-iac-install.html similarity index 73% rename from docs/snyk/v2.4.19/argocd-iac-install.html rename to docs/snyk/v2.8.13/argocd-iac-install.html index 0b3265dde5efc..8e0c8abdd40c3 100644 --- a/docs/snyk/v2.4.19/argocd-iac-install.html +++ b/docs/snyk/v2.8.13/argocd-iac-install.html @@ -456,7 +456,7 @@

    Snyk test report

    -

    January 22nd 2023, 12:22:53 am

    +

    March 24th 2024, 12:21:30 am (UTC+00:00)

    Scanned the following path: @@ -466,7 +466,7 @@

    Snyk test report

    -
    32 total issues
    +
    38 total issues
    @@ -483,7 +483,7 @@

    Snyk test report

    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -494,7 +494,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -507,29 +507,29 @@

      Role with dangerous permissions

    • - Line number: 9063 + Line number: 18466

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -540,7 +540,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -553,29 +553,29 @@

      Role with dangerous permissions

    • - Line number: 9140 + Line number: 18543

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -586,7 +586,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -599,29 +599,29 @@

      Role with dangerous permissions

    • - Line number: 9168 + Line number: 18571

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -632,42 +632,42 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: [DocId: 13] - rules[3] + rules[1] resources
    • - Line number: 9212 + Line number: 18601

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -678,42 +678,42 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: [DocId: 13] - rules[1] + rules[3] resources
    • - Line number: 9194 + Line number: 18619

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -724,7 +724,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -737,24 +737,24 @@

      Role with dangerous permissions

    • - Line number: 9228 + Line number: 18635

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    @@ -770,11 +770,11 @@

    Container could be running with outdated image

    • - Public ID: SNYK-CC-K8S-42 + Public ID: SNYK-CC-K8S-42
    • Introduced through: - [DocId: 46] + [DocId: 45] spec @@ -789,7 +789,7 @@

      Container could be running with outdated image

    • - Line number: 10100 + Line number: 19761
    @@ -806,7 +806,7 @@

    Remediation

    @@ -822,11 +822,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 42] + [DocId: 41] input @@ -847,7 +847,7 @@

      Container has no CPU limit

    • - Line number: 9686 + Line number: 19118
    @@ -864,7 +864,7 @@

    Remediation

    @@ -880,11 +880,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 43] + [DocId: 42] input @@ -905,7 +905,7 @@

      Container has no CPU limit

    • - Line number: 9786 + Line number: 19351
    @@ -922,7 +922,7 @@

    Remediation

    @@ -938,11 +938,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 43] + [DocId: 42] input @@ -963,7 +963,7 @@

      Container has no CPU limit

    • - Line number: 9763 + Line number: 19317
    @@ -980,7 +980,7 @@

    Remediation

    @@ -996,11 +996,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 44] + [DocId: 43] input @@ -1021,7 +1021,7 @@

      Container has no CPU limit

    • - Line number: 9829 + Line number: 19411
    @@ -1038,7 +1038,7 @@

    Remediation

    @@ -1054,11 +1054,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 45] + [DocId: 44] input @@ -1079,7 +1079,7 @@

      Container has no CPU limit

    • - Line number: 9901 + Line number: 19504
    @@ -1096,7 +1096,7 @@

    Remediation

    @@ -1112,11 +1112,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 46] + [DocId: 45] input @@ -1137,7 +1137,7 @@

      Container has no CPU limit

    • - Line number: 10100 + Line number: 19761
    @@ -1154,7 +1154,7 @@

    Remediation

    @@ -1170,11 +1170,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 46] + [DocId: 45] input @@ -1195,7 +1195,7 @@

      Container has no CPU limit

    • - Line number: 9955 + Line number: 19561
    @@ -1212,7 +1212,7 @@

    Remediation

    @@ -1228,11 +1228,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 47] + [DocId: 46] input @@ -1253,7 +1253,7 @@

      Container has no CPU limit

    • - Line number: 10183 + Line number: 19846
    @@ -1270,7 +1270,7 @@

    Remediation

    @@ -1286,11 +1286,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 48] + [DocId: 47] input @@ -1311,7 +1311,7 @@

      Container has no CPU limit

    • - Line number: 10443 + Line number: 20168
    @@ -1328,7 +1328,7 @@

    Remediation

    @@ -1344,11 +1344,11 @@

    Container is running with multiple open ports

    • - Public ID: SNYK-CC-K8S-36 + Public ID: SNYK-CC-K8S-36
    • Introduced through: - [DocId: 43] + [DocId: 42] spec @@ -1363,7 +1363,7 @@

      Container is running with multiple open ports

    • - Line number: 9770 + Line number: 19331
    @@ -1380,12 +1380,12 @@

    Remediation

    -

    Container is running with writable root filesystem

    +

    Container is running without liveness probe

    @@ -1396,13 +1396,11 @@

    Container is running with writable root filesystem

    • - Public ID: SNYK-CC-K8S-8 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 45] - - input + [DocId: 41] spec @@ -1410,33 +1408,31 @@

      Container is running with writable root filesystem

      spec - containers[redis] - - securityContext + containers[argocd-applicationset-controller] - readOnlyRootFilesystem + livenessProbe
    • - Line number: 9911 + Line number: 19118

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    +

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    +

    Add `livenessProbe` attribute


    @@ -1452,7 +1448,7 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: @@ -1464,14 +1460,14 @@

      Container is running without liveness probe

      spec - containers[argocd-applicationset-controller] + containers[dex] livenessProbe
    • - Line number: 9686 + Line number: 19317
    @@ -1488,7 +1484,7 @@

    Remediation

    @@ -1504,11 +1500,11 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 43] + [DocId: 44] spec @@ -1516,14 +1512,14 @@

      Container is running without liveness probe

      spec - containers[dex] + containers[redis] livenessProbe
    • - Line number: 9763 + Line number: 19504
    @@ -1540,12 +1536,12 @@

    Remediation

    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1556,11 +1552,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 43] + [DocId: 41] + + input spec @@ -1568,36 +1566,40 @@

      Container is running without liveness probe

      spec - initContainers[copyutil] + containers[argocd-applicationset-controller] - livenessProbe + resources + + limits + + memory
    • - Line number: 9786 + Line number: 19118

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1608,11 +1610,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 45] + [DocId: 42] + + input spec @@ -1620,36 +1624,40 @@

      Container is running without liveness probe

      spec - containers[redis] + containers[dex] - livenessProbe + resources + + limits + + memory
    • - Line number: 9901 + Line number: 19317

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1660,11 +1668,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 46] + [DocId: 42] + + input spec @@ -1674,29 +1684,33 @@

      Container is running without liveness probe

      initContainers[copyutil] - livenessProbe + resources + + limits + + memory
    • - Line number: 10100 + Line number: 19351

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    @@ -1712,11 +1726,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 42] + [DocId: 43] input @@ -1726,7 +1740,7 @@

      Container is running without memory limit

      spec - containers[argocd-applicationset-controller] + containers[argocd-notifications-controller] resources @@ -1737,7 +1751,7 @@

      Container is running without memory limit

    • - Line number: 9686 + Line number: 19411
    @@ -1754,7 +1768,7 @@

    Remediation

    @@ -1770,11 +1784,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 43] + [DocId: 44] input @@ -1784,7 +1798,7 @@

      Container is running without memory limit

      spec - containers[dex] + containers[redis] resources @@ -1795,7 +1809,7 @@

      Container is running without memory limit

    • - Line number: 9763 + Line number: 19504
    @@ -1812,7 +1826,7 @@

    Remediation

    @@ -1828,11 +1842,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 43] + [DocId: 45] input @@ -1853,7 +1867,7 @@

      Container is running without memory limit

    • - Line number: 9786 + Line number: 19761
    @@ -1870,7 +1884,7 @@

    Remediation

    @@ -1886,11 +1900,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 44] + [DocId: 45] input @@ -1900,7 +1914,7 @@

      Container is running without memory limit

      spec - containers[argocd-notifications-controller] + containers[argocd-repo-server] resources @@ -1911,7 +1925,7 @@

      Container is running without memory limit

    • - Line number: 9829 + Line number: 19561
    @@ -1928,7 +1942,7 @@

    Remediation

    @@ -1944,11 +1958,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 45] + [DocId: 46] input @@ -1958,7 +1972,7 @@

      Container is running without memory limit

      spec - containers[redis] + containers[argocd-server] resources @@ -1969,7 +1983,7 @@

      Container is running without memory limit

    • - Line number: 9901 + Line number: 19846
    @@ -1986,7 +2000,7 @@

    Remediation

    @@ -2002,11 +2016,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 46] + [DocId: 47] input @@ -2016,7 +2030,7 @@

      Container is running without memory limit

      spec - initContainers[copyutil] + containers[argocd-application-controller] resources @@ -2027,7 +2041,7 @@

      Container is running without memory limit

    • - Line number: 10100 + Line number: 20168
    @@ -2044,12 +2058,12 @@

    Remediation

    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2060,11 +2074,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: - [DocId: 46] + [DocId: 41] input @@ -2074,40 +2088,94 @@

      Container is running without memory limit

      spec - containers[argocd-repo-server] + containers[argocd-applicationset-controller] - resources + securityContext - limits + runAsUser + +
    • + +
    • + Line number: 19241 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 42] - memory + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser
    • - Line number: 9955 + Line number: 19359

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2118,11 +2186,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: - [DocId: 47] + [DocId: 42] input @@ -2132,40 +2200,94 @@

      Container is running without memory limit

      spec - containers[argocd-server] + containers[dex] - resources + securityContext - limits + runAsUser + +
    • + +
    • + Line number: 19334 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 43] - memory + input + + spec + + template + + spec + + containers[argocd-notifications-controller] + + securityContext + + runAsUser
    • - Line number: 10183 + Line number: 19438

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2176,11 +2298,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: - [DocId: 48] + [DocId: 44] input @@ -2190,35 +2312,257 @@

      Container is running without memory limit

      spec - containers[argocd-application-controller] + containers[redis] - resources + securityContext - limits + runAsUser + +
    • + +
    • + Line number: 19514 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 45] - memory + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser
    • - Line number: 10443 + Line number: 19768

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 45] + + input + + spec + + template + + spec + + containers[argocd-repo-server] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 19734 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 46] + + input + + spec + + template + + spec + + containers[argocd-server] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 20078 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 47] + + input + + spec + + template + + spec + + containers[argocd-application-controller] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 20316 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    diff --git a/docs/snyk/v2.6.0-rc4/argocd-iac-namespace-install.html b/docs/snyk/v2.8.13/argocd-iac-namespace-install.html similarity index 73% rename from docs/snyk/v2.6.0-rc4/argocd-iac-namespace-install.html rename to docs/snyk/v2.8.13/argocd-iac-namespace-install.html index 935671b03009d..17296cd003c37 100644 --- a/docs/snyk/v2.6.0-rc4/argocd-iac-namespace-install.html +++ b/docs/snyk/v2.8.13/argocd-iac-namespace-install.html @@ -456,7 +456,7 @@

    Snyk test report

    -

    January 22nd 2023, 12:19:39 am

    +

    March 24th 2024, 12:21:38 am (UTC+00:00)

    Scanned the following path: @@ -466,7 +466,7 @@

    Snyk test report

    -
    32 total issues
    +
    38 total issues
    @@ -483,7 +483,7 @@

    Snyk test report

    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -494,7 +494,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -514,22 +514,22 @@

      Role with dangerous permissions


      Impact

      -

      Using this role grants dangerous permissions

      +

      Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

      Remediation

      -

      Consider removing this permissions

      +

      Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -540,7 +540,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -560,22 +560,22 @@

      Role with dangerous permissions


      Impact

      -

      Using this role grants dangerous permissions

      +

      Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

      Remediation

      -

      Consider removing this permissions

      +

      Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -586,7 +586,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -606,22 +606,22 @@

      Role with dangerous permissions


      Impact

      -

      Using this role grants dangerous permissions

      +

      Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

      Remediation

      -

      Consider removing this permissions

      +

      Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -632,42 +632,42 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: [DocId: 10] - rules[3] + rules[1] resources
    • - Line number: 226 + Line number: 212

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -678,42 +678,42 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: [DocId: 10] - rules[1] + rules[3] resources
    • - Line number: 208 + Line number: 230

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -724,7 +724,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -737,24 +737,24 @@

      Role with dangerous permissions

    • - Line number: 242 + Line number: 246

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    @@ -770,11 +770,11 @@

    Container could be running with outdated image

    • - Public ID: SNYK-CC-K8S-42 + Public ID: SNYK-CC-K8S-42
    • Introduced through: - [DocId: 39] + [DocId: 38] spec @@ -789,7 +789,7 @@

      Container could be running with outdated image

    • - Line number: 1153 + Line number: 1267
    @@ -806,7 +806,7 @@

    Remediation

    @@ -822,11 +822,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 35] + [DocId: 34] input @@ -847,7 +847,7 @@

      Container has no CPU limit

    • - Line number: 616 + Line number: 624
    @@ -864,7 +864,7 @@

    Remediation

    @@ -880,11 +880,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 36] + [DocId: 35] input @@ -905,7 +905,7 @@

      Container has no CPU limit

    • - Line number: 789 + Line number: 857
    @@ -922,7 +922,7 @@

    Remediation

    @@ -938,11 +938,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 36] + [DocId: 35] input @@ -963,7 +963,7 @@

      Container has no CPU limit

    • - Line number: 755 + Line number: 823
    @@ -980,7 +980,7 @@

    Remediation

    @@ -996,11 +996,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 37] + [DocId: 36] input @@ -1021,7 +1021,7 @@

      Container has no CPU limit

    • - Line number: 845 + Line number: 917
    @@ -1038,7 +1038,7 @@

    Remediation

    @@ -1054,11 +1054,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 38] + [DocId: 37] input @@ -1079,7 +1079,7 @@

      Container has no CPU limit

    • - Line number: 919 + Line number: 1010
    @@ -1096,7 +1096,7 @@

    Remediation

    @@ -1112,11 +1112,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 39] + [DocId: 38] input @@ -1137,7 +1137,7 @@

      Container has no CPU limit

    • - Line number: 1153 + Line number: 1267
    @@ -1154,7 +1154,7 @@

    Remediation

    @@ -1170,11 +1170,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 39] + [DocId: 38] input @@ -1195,7 +1195,7 @@

      Container has no CPU limit

    • - Line number: 975 + Line number: 1067
    @@ -1212,7 +1212,7 @@

    Remediation

    @@ -1228,11 +1228,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 40] + [DocId: 39] input @@ -1253,7 +1253,7 @@

      Container has no CPU limit

    • - Line number: 1238 + Line number: 1352
    @@ -1270,7 +1270,7 @@

    Remediation

    @@ -1286,11 +1286,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 41] + [DocId: 40] input @@ -1311,7 +1311,7 @@

      Container has no CPU limit

    • - Line number: 1542 + Line number: 1674
    @@ -1328,7 +1328,7 @@

    Remediation

    @@ -1344,11 +1344,11 @@

    Container is running with multiple open ports

    • - Public ID: SNYK-CC-K8S-36 + Public ID: SNYK-CC-K8S-36
    • Introduced through: - [DocId: 36] + [DocId: 35] spec @@ -1363,7 +1363,7 @@

      Container is running with multiple open ports

    • - Line number: 769 + Line number: 837
    @@ -1380,12 +1380,12 @@

    Remediation

    -

    Container is running with writable root filesystem

    +

    Container is running without liveness probe

    @@ -1396,13 +1396,11 @@

    Container is running with writable root filesystem

    • - Public ID: SNYK-CC-K8S-8 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 38] - - input + [DocId: 34] spec @@ -1410,33 +1408,31 @@

      Container is running with writable root filesystem

      spec - containers[redis] - - securityContext + containers[argocd-applicationset-controller] - readOnlyRootFilesystem + livenessProbe
    • - Line number: 929 + Line number: 624

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    +

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    +

    Add `livenessProbe` attribute


    @@ -1452,7 +1448,7 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: @@ -1464,14 +1460,14 @@

      Container is running without liveness probe

      spec - containers[argocd-applicationset-controller] + containers[dex] livenessProbe
    • - Line number: 616 + Line number: 823
    @@ -1488,7 +1484,7 @@

    Remediation

    @@ -1504,11 +1500,11 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 36] + [DocId: 37] spec @@ -1516,14 +1512,14 @@

      Container is running without liveness probe

      spec - containers[dex] + containers[redis] livenessProbe
    • - Line number: 755 + Line number: 1010
    @@ -1540,12 +1536,12 @@

    Remediation

    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1556,11 +1552,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 36] + [DocId: 34] + + input spec @@ -1568,36 +1566,40 @@

      Container is running without liveness probe

      spec - initContainers[copyutil] + containers[argocd-applicationset-controller] - livenessProbe + resources + + limits + + memory
    • - Line number: 789 + Line number: 624

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1608,11 +1610,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 38] + [DocId: 35] + + input spec @@ -1620,36 +1624,40 @@

      Container is running without liveness probe

      spec - containers[redis] + containers[dex] - livenessProbe + resources + + limits + + memory
    • - Line number: 919 + Line number: 823

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1660,11 +1668,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 39] + [DocId: 35] + + input spec @@ -1674,29 +1684,33 @@

      Container is running without liveness probe

      initContainers[copyutil] - livenessProbe + resources + + limits + + memory
    • - Line number: 1153 + Line number: 857

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    @@ -1712,11 +1726,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 35] + [DocId: 36] input @@ -1726,7 +1740,7 @@

      Container is running without memory limit

      spec - containers[argocd-applicationset-controller] + containers[argocd-notifications-controller] resources @@ -1737,7 +1751,7 @@

      Container is running without memory limit

    • - Line number: 616 + Line number: 917
    @@ -1754,7 +1768,7 @@

    Remediation

    @@ -1770,11 +1784,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 36] + [DocId: 37] input @@ -1784,7 +1798,7 @@

      Container is running without memory limit

      spec - containers[dex] + containers[redis] resources @@ -1795,7 +1809,7 @@

      Container is running without memory limit

    • - Line number: 755 + Line number: 1010
    @@ -1812,7 +1826,7 @@

    Remediation

    @@ -1828,11 +1842,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 36] + [DocId: 38] input @@ -1853,7 +1867,7 @@

      Container is running without memory limit

    • - Line number: 789 + Line number: 1267
    @@ -1870,7 +1884,7 @@

    Remediation

    @@ -1886,11 +1900,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 37] + [DocId: 38] input @@ -1900,7 +1914,7 @@

      Container is running without memory limit

      spec - containers[argocd-notifications-controller] + containers[argocd-repo-server] resources @@ -1911,7 +1925,7 @@

      Container is running without memory limit

    • - Line number: 845 + Line number: 1067
    @@ -1928,7 +1942,7 @@

    Remediation

    @@ -1944,11 +1958,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 38] + [DocId: 39] input @@ -1958,7 +1972,7 @@

      Container is running without memory limit

      spec - containers[redis] + containers[argocd-server] resources @@ -1969,7 +1983,7 @@

      Container is running without memory limit

    • - Line number: 919 + Line number: 1352
    @@ -1986,7 +2000,7 @@

    Remediation

    @@ -2002,11 +2016,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 39] + [DocId: 40] input @@ -2016,7 +2030,7 @@

      Container is running without memory limit

      spec - initContainers[copyutil] + containers[argocd-application-controller] resources @@ -2027,7 +2041,7 @@

      Container is running without memory limit

    • - Line number: 1153 + Line number: 1674
    @@ -2044,12 +2058,12 @@

    Remediation

    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2060,11 +2074,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: - [DocId: 39] + [DocId: 34] input @@ -2074,40 +2088,94 @@

      Container is running without memory limit

      spec - containers[argocd-repo-server] + containers[argocd-applicationset-controller] - resources + securityContext - limits + runAsUser + +
    • + +
    • + Line number: 747 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 35] - memory + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser
    • - Line number: 975 + Line number: 865

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2118,11 +2186,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: - [DocId: 40] + [DocId: 35] input @@ -2132,40 +2200,94 @@

      Container is running without memory limit

      spec - containers[argocd-server] + containers[dex] - resources + securityContext - limits + runAsUser + +
    • + +
    • + Line number: 840 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 36] - memory + input + + spec + + template + + spec + + containers[argocd-notifications-controller] + + securityContext + + runAsUser
    • - Line number: 1238 + Line number: 944

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2176,11 +2298,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: - [DocId: 41] + [DocId: 37] input @@ -2190,35 +2312,257 @@

      Container is running without memory limit

      spec - containers[argocd-application-controller] + containers[redis] - resources + securityContext - limits + runAsUser + +
    • + +
    • + Line number: 1020 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 38] - memory + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser
    • - Line number: 1542 + Line number: 1274

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 38] + + input + + spec + + template + + spec + + containers[argocd-repo-server] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 1240 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 39] + + input + + spec + + template + + spec + + containers[argocd-server] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 1584 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 40] + + input + + spec + + template + + spec + + containers[argocd-application-controller] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 1822 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    diff --git a/docs/snyk/v2.5.7/argocd-test.html b/docs/snyk/v2.8.13/argocd-test.html similarity index 54% rename from docs/snyk/v2.5.7/argocd-test.html rename to docs/snyk/v2.8.13/argocd-test.html index 98e81d5448610..8f02f01423f2f 100644 --- a/docs/snyk/v2.5.7/argocd-test.html +++ b/docs/snyk/v2.8.13/argocd-test.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,19 +456,20 @@

    Snyk test report

    -

    January 22nd 2023, 12:19:59 am

    +

    March 24th 2024, 12:19:50 am (UTC+00:00)

    Scanned the following paths:
      -
    • /argo-cd/argoproj/argo-cd/v2 (gomodules)
    • /argo-cd (yarn)
    • +
    • /argo-cd/argoproj/argo-cd/v2/go.mod (gomodules)
    • +
    • /argo-cd/ui/yarn.lock (yarn)
    -
    8 known vulnerabilities
    -
    130 vulnerable dependency paths
    -
    1720 dependencies
    +
    12 known vulnerabilities
    +
    108 vulnerable dependency paths
    +
    1856 dependencies
    @@ -476,112 +477,33 @@

    Snyk test report

    -
    -

    Server-side Request Forgery (SSRF)

    +
    +

    Denial of Service (DoS)

    -
    - medium severity +
    + high severity

    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - parse-url + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, git-url-parse@11.6.0 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - git-url-parse@11.6.0 - - git-up@4.0.5 - - parse-url@6.0.5 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    parse-url is an An advanced url parser supporting git urls too.

    -

    Affected versions of this package are vulnerable to Server-side Request Forgery (SSRF) due to improper detection of protocol, resource, and pathname fields. Exploiting this vulnerability results in bypassing protocol verification.

    -

    PoC:

    -
    import parseUrl from "parse-url";
    -        import fetch from 'node-fetch';
    -        var parsed=parseUrl("http://nnnn@localhost:808:/?id=xss")
    -        if(parsed.resource=="localhost"){
    -        console.log("internal network access is blocked")
    -        }
    -        else{
    -           const response = await fetch('http://'+parsed.resource+parsed.pathname);
    -                console.log(response)
    -         }
    -        
    -

    Remediation

    -

    Upgrade parse-url to version 8.1.0 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    -
    - -
    - medium severity -
    - -
    - -
    • - Package Manager: npm + Package Manager: golang
    • Vulnerable module: - parse-url + github.com/go-jose/go-jose/v3
    • Introduced through: - argo-cd-ui@1.0.0, git-url-parse@11.6.0 and others + github.com/argoproj/argo-cd/v2@0.0.0, github.com/coreos/go-oidc/v3/oidc@3.6.0 and others
    @@ -593,13 +515,11 @@

    Detailed paths

    • Introduced through: - argo-cd-ui@1.0.0 - - git-url-parse@11.6.0 + github.com/argoproj/argo-cd/v2@0.0.0 - git-up@4.0.5 + github.com/coreos/go-oidc/v3/oidc@3.6.0 - parse-url@6.0.5 + github.com/go-jose/go-jose/v3@3.0.0 @@ -611,49 +531,36 @@

      Detailed paths


      Overview

      -

      parse-url is an An advanced url parser supporting git urls too.

      -

      Affected versions of this package are vulnerable to Improper Input Validation due to incorrect parsing of URLs. This allows the attacker to craft a malformed URL which can lead to a phishing attack.

      -
      
      -        const parseUrl = require("parse-url");
      -        const Url = require("url");
      -        
      -        const express = require('express');
      -        const app = express();
      -        
      -        var url = "https://www.google.com:x@fakesite.com:x";
      -        parsed = parseUrl(url);
      -        console.log("[*]`parse-url` output: ")
      -        console.log(parsed);
      -        
      -        parsed2 = Url.parse(url);
      -        console.log("[*]`url` output: ")
      -        console.log(parsed2)
      -        
      -        app.get('/', (req, res) => {
      -            if (parsed.host == "www.google.com") {
      -                res.send("<a href=\'" + parsed2.href + "\'>CLICK ME!</a>")
      -            }
      -        })
      -        
      -        app.listen(8888,"0.0.0.0");
      -        
      +

      Affected versions of this package are vulnerable to Denial of Service (DoS) when decrypting JWE inputs. An attacker can cause a denial-of-service by providing a PBES2 encrypted JWE blob with a very large p2c value.

      +

      Details

      +

      Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

      +

      Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

      +

      One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

      +

      When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

      +

      Two common types of DoS vulnerabilities:

      +
        +
      • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

        +
      • +
      • Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

        +
      • +

      Remediation

      -

      Upgrade parse-url to version 8.1.0 or higher.

      +

      Upgrade github.com/go-jose/go-jose/v3 to version 3.0.1 or higher.

      References


    -

    Regular Expression Denial of Service (ReDoS)

    +

    LGPL-3.0 license

    @@ -664,18 +571,21 @@

    Regular Expression Denial of Service (ReDoS)

    • - Package Manager: npm + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod
    • - Vulnerable module: + Package Manager: golang +
    • +
    • + Module: - minimatch + gopkg.in/retry.v1
    • Introduced through: - argo-cd-ui@1.0.0, redoc@2.0.0-rc.64 and others + github.com/argoproj/argo-cd/v2@0.0.0, github.com/Azure/kubelogin/pkg/token@0.0.20 and others
    @@ -687,13 +597,11 @@

    Detailed paths

    • Introduced through: - argo-cd-ui@1.0.0 - - redoc@2.0.0-rc.64 + github.com/argoproj/argo-cd/v2@0.0.0 - @redocly/openapi-core@1.0.0-beta.82 + github.com/Azure/kubelogin/pkg/token@0.0.20 - minimatch@3.0.4 + gopkg.in/retry.v1@1.0.3 @@ -704,86 +612,17 @@

      Detailed paths


      -

      Overview

      -

      minimatch is a minimal matching utility.

      -

      Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) via the braceExpand function in minimatch.js.

      -

      Details

      -

      Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.

      -

      The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.

      -

      Let’s take the following regular expression as an example:

      -
      regex = /A(B|C+)+D/
      -        
      -

      This regular expression accomplishes the following:

      -
        -
      • A The string must start with the letter 'A'
      • -
      • (B|C+)+ The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the + matches one or more times). The + at the end of this section states that we can look for one or more matches of this section.
      • -
      • D Finally, we ensure this section of the string ends with a 'D'
      • -
      -

      The expression would match inputs such as ABBD, ABCCCCD, ABCBCCCD and ACCCCCD

      -

      It most cases, it doesn't take very long for a regex engine to find a match:

      -
      $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD")'
      -        0.04s user 0.01s system 95% cpu 0.052 total
      -        
      -        $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX")'
      -        1.79s user 0.02s system 99% cpu 1.812 total
      -        
      -

      The entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.

      -

      Most Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as catastrophic backtracking.

      -

      Let's look at how our expression runs into this problem, using a shorter string: "ACCCX". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:

      -
        -
      1. CCC
      2. -
      3. CC+C
      4. -
      5. C+CC
      6. -
      7. C+C+C.
      8. -
      -

      The engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use RegEx 101 debugger to see the engine has to take a total of 38 steps before it can determine the string doesn't match.

      -

      From there, the number of steps the engine must use to validate a string just continues to grow.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      StringNumber of C'sNumber of steps
      ACCCX338
      ACCCCX471
      ACCCCCX5136
      ACCCCCCCCCCCCCCX1465,553
      -

      By the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.

      -

      Remediation

      -

      Upgrade minimatch to version 3.0.5 or higher.

      -

      References

      - +

      LGPL-3.0 license


    -

    Denial of Service (DoS)

    +

    Infinite loop

    @@ -793,19 +632,22 @@

    Denial of Service (DoS)


      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • Package Manager: golang
    • Vulnerable module: - golang.org/x/net/http2 + google.golang.org/protobuf/internal/encoding/json
    • Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0, k8s.io/client-go/rest@0.24.2 and others + github.com/argoproj/argo-cd/v2@0.0.0, github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 and others
    @@ -819,20 +661,13 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/rest@0.24.2 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - golang.org/x/net/http2@#9d032be2e588 - - - - -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + github.com/golang/protobuf/jsonpb@1.4.2 - github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -841,20 +676,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/argoproj/pkg/grpc/http@#d56162821bd1 - golang.org/x/net/http2@#9d032be2e588 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - github.com/soheilhy/cmux@0.1.5 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -863,11 +693,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/cache@0.24.2 + google.golang.org/grpc@1.58.3 + + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -876,24 +710,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc@1.58.3 - golang.org/x/net/http2@#9d032be2e588 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/dynamic@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -902,24 +729,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/transport/spdy@0.24.2 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc@1.58.3 - golang.org/x/net/http2@#9d032be2e588 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + google.golang.org/grpc/internal/transport@1.58.3 - github.com/argoproj/pkg/kubeclientmetrics@#36c59d8fafe0 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -928,24 +748,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/testing@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc@1.58.3 - golang.org/x/net/http2@#9d032be2e588 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/kubernetes@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -954,24 +767,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/azure@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc@1.58.3 - golang.org/x/net/http2@#9d032be2e588 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/plugin/pkg/client/auth/gcp@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -980,24 +786,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/oidc@0.24.2 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc@1.58.3 - golang.org/x/net/http2@#9d032be2e588 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/tools/record@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1006,24 +805,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/watch@0.24.2 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 + google.golang.org/grpc@1.58.3 - golang.org/x/net/http2@#9d032be2e588 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/pretty@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1032,13 +824,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/openapi@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - k8s.io/client-go/discovery@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1047,13 +843,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/listers/core/v1@0.24.2 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 + + google.golang.org/grpc@1.58.3 + + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/tools/cache@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1062,13 +862,19 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#4d8552b0775f + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - k8s.io/client-go/tools/cache@0.24.2 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/transport@1.58.3 + + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1077,13 +883,19 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/core/v1@0.24.2 + google.golang.org/grpc/reflection@1.58.3 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.58.3 - k8s.io/client-go/tools/cache@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1092,13 +904,19 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers@0.24.2 + google.golang.org/grpc/health@1.58.3 + + google.golang.org/grpc/health/grpc_health_v1@1.58.3 + + google.golang.org/grpc@1.58.3 - k8s.io/client-go/tools/cache@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1107,43 +925,98 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/clientcmd@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 - k8s.io/client-go/tools/auth@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/client-go/rest@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - golang.org/x/net/http2@#9d032be2e588 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - github.com/argoproj/notifications-engine/pkg/controller@#4d8552b0775f + google.golang.org/grpc@1.58.3 - k8s.io/client-go/tools/cache@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0
  • + + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Stack-based Buffer Overflow

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery/fake@0.24.2 - - k8s.io/client-go/testing@0.24.2 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/client-go/rest@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1152,13 +1025,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/fake@0.24.2 + github.com/argoproj/pkg/grpc/http@#d56162821bd1 - k8s.io/client-go/testing@0.24.2 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/client-go/rest@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1167,13 +1040,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/remotecommand@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/transport/spdy@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1182,13 +1055,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/apimachinery/pkg/watch@0.24.2 + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.7.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1197,13 +1070,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/rest@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/client-go/transport@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1212,13 +1087,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 + + google.golang.org/grpc@1.58.3 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1227,13 +1104,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1242,13 +1121,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/retry@1.3.0 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 + + google.golang.org/grpc@1.58.3 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1259,11 +1140,13 @@

      Detailed paths

      github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1272,13 +1155,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/health/grpc_health_v1@1.45.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 + + google.golang.org/grpc@1.58.3 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1287,13 +1172,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.3.0 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1302,13 +1189,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 + + google.golang.org/grpc@1.58.3 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1317,13 +1206,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.31.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1332,13 +1223,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.6.3 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 + + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1347,15 +1242,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/cache@#98ccd3d43fd9 + google.golang.org/grpc/reflection@1.58.3 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.58.3 - k8s.io/kubectl/pkg/util/openapi@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/discovery@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1364,15 +1261,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync@#98ccd3d43fd9 + google.golang.org/grpc/health@1.58.3 - k8s.io/kubectl/pkg/util/openapi@0.24.2 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/client-go/discovery@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1381,15 +1280,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@#98ccd3d43fd9 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + + google.golang.org/grpc@1.58.3 - k8s.io/kubectl/pkg/util/openapi@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/discovery@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1398,15 +1299,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#4d8552b0775f + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/client-go/tools/clientcmd@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/tools/auth@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1415,15 +1318,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/term@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 + + google.golang.org/grpc@1.58.3 - k8s.io/client-go/tools/remotecommand@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/transport/spdy@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1432,15 +1337,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1449,15 +1356,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/api/rbac/v1@0.24.2 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1466,15 +1375,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1@0.24.2 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 + + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1483,15 +1394,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/api/core/v1@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1500,15 +1413,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/errors@0.24.2 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 + + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1517,15 +1432,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/equality@0.24.2 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 + + google.golang.org/grpc/health/grpc_health_v1@1.58.3 + + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1534,15 +1453,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery@0.24.2 + google.golang.org/grpc/reflection@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.58.3 - k8s.io/client-go/transport@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.58.3 + + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1551,15 +1474,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/dynamic@0.24.2 + google.golang.org/grpc/health@1.58.3 + + google.golang.org/grpc/health/grpc_health_v1@1.58.3 + + google.golang.org/grpc@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/transport@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1568,15 +1495,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/transport/spdy@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 - k8s.io/client-go/rest@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/client-go/transport@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.58.3 + + google.golang.org/grpc/internal/transport@1.58.3 + + google.golang.org/grpc/internal/pretty@1.58.3 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1585,49 +1518,95 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/pkg/kubeclientmetrics@#36c59d8fafe0 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 - k8s.io/client-go/rest@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/client-go/transport@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - golang.org/x/net/http2@#9d032be2e588 - - - -
    • -
    • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/testing@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/transport@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Stack-based Buffer Overflow when processing input that uses pathologically deep nesting.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes@0.24.2 - - k8s.io/client-go/rest@0.24.2 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/client-go/transport@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1636,15 +1615,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/azure@0.24.2 - - k8s.io/client-go/rest@0.24.2 + github.com/argoproj/pkg/grpc/http@#d56162821bd1 - k8s.io/client-go/transport@0.24.2 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1653,15 +1630,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/gcp@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/transport@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1670,15 +1645,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/oidc@0.24.2 - - k8s.io/client-go/rest@0.24.2 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/client-go/transport@0.24.2 + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.7.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1687,15 +1660,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/reflection@1.45.0 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.45.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1704,15 +1677,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/health@1.45.0 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - google.golang.org/grpc/health/grpc_health_v1@1.45.0 + google.golang.org/grpc@1.58.3 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1721,17 +1694,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/health@#98ccd3d43fd9 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@#98ccd3d43fd9 + google.golang.org/grpc@1.58.3 - k8s.io/kubectl/pkg/util/openapi@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/discovery@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1740,17 +1711,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/common@#98ccd3d43fd9 - - github.com/argoproj/gitops-engine/pkg/utils/kube@#98ccd3d43fd9 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 - k8s.io/kubectl/pkg/util/openapi@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/discovery@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1759,17 +1728,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - k8s.io/client-go/restmapper@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/discovery@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1778,17 +1745,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/envtest@0.11.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane@0.11.0 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/tools/clientcmd@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/client-go/tools/auth@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/client-go/rest@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1797,17 +1762,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/openapi@0.24.2 - - k8s.io/client-go/discovery@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/transport@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1816,17 +1779,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/util/managedfields@0.24.2 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1835,17 +1796,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/resource@#98ccd3d43fd9 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1854,17 +1813,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/common@#98ccd3d43fd9 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1873,17 +1832,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/hook@#98ccd3d43fd9 + google.golang.org/grpc/reflection@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1892,17 +1851,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/ignore@#98ccd3d43fd9 + google.golang.org/grpc/health@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1911,17 +1870,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/syncwaves@#98ccd3d43fd9 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1930,17 +1889,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/testing@#98ccd3d43fd9 + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1949,17 +1908,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/record@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - k8s.io/client-go/tools/reference@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1968,17 +1927,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/cache@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 - k8s.io/client-go/tools/pager@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1987,17 +1946,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/pkg/apis/clientauthentication/v1beta1@0.24.2 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - k8s.io/client-go/pkg/apis/clientauthentication@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2006,17 +1965,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime@0.11.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - sigs.k8s.io/controller-runtime/pkg/scheme@0.11.0 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2025,17 +1984,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/util/retry@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - k8s.io/apimachinery/pkg/api/errors@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2044,17 +2003,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/resource@0.24.2 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 - k8s.io/api/core/v1@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2063,17 +2022,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/health@#98ccd3d43fd9 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 + + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/kubectl/pkg/util/podutils@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2082,17 +2043,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/validation@0.24.2 + google.golang.org/grpc/reflection@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1/validation@0.24.2 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2101,17 +2064,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/portforward@0.24.2 + google.golang.org/grpc/health@1.58.3 + + google.golang.org/grpc/health/grpc_health_v1@1.58.3 - k8s.io/api/core/v1@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/watch@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2120,36 +2085,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery/fake@0.24.2 - - k8s.io/client-go/testing@0.24.2 - - k8s.io/client-go/rest@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 - k8s.io/client-go/transport@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 - - - -
    • -
    • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/client-go/kubernetes/fake@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - k8s.io/client-go/testing@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/client-go/rest@0.24.2 + google.golang.org/grpc@1.58.3 - k8s.io/client-go/transport@0.24.2 + google.golang.org/grpc/internal/transport@1.58.3 - k8s.io/apimachinery/pkg/util/net@0.24.2 + google.golang.org/grpc/internal/pretty@1.58.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2158,57 +2108,94 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/remotecommand@0.24.2 - - k8s.io/client-go/transport/spdy@0.24.2 - - k8s.io/client-go/rest@0.24.2 - - k8s.io/client-go/transport@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - golang.org/x/net/http2@#9d032be2e588 - - - -
    • -
    • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.3.0 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 + google.golang.org/grpc@1.58.3 - github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 + google.golang.org/grpc/internal/transport@1.58.3 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/pretty@1.58.3 - google.golang.org/grpc/internal/transport@1.45.0 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Authentication Bypass by Capture-replay

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/crypto/ssh +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@0.0.0 and golang.org/x/crypto/ssh@0.16.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - - k8s.io/client-go/restmapper@0.24.2 - - k8s.io/client-go/discovery@0.24.2 - - k8s.io/client-go/rest@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2217,19 +2204,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/cache@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 + golang.org/x/crypto/ssh/knownhosts@0.16.0 - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - - k8s.io/client-go/restmapper@0.24.2 - - k8s.io/client-go/discovery@0.24.2 - - k8s.io/client-go/rest@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2238,19 +2215,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/cache@#98ccd3d43fd9 - - k8s.io/kubectl/pkg/util/openapi@0.24.2 - - k8s.io/client-go/discovery@0.24.2 - - k8s.io/client-go/rest@0.24.2 - - k8s.io/client-go/transport@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2259,19 +2226,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync@#98ccd3d43fd9 - - k8s.io/kubectl/pkg/util/openapi@0.24.2 - - k8s.io/client-go/discovery@0.24.2 - - k8s.io/client-go/rest@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/transport@0.24.2 + github.com/skeema/knownhosts@1.2.1 - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2280,19 +2239,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@#98ccd3d43fd9 - - k8s.io/kubectl/pkg/util/openapi@0.24.2 - - k8s.io/client-go/discovery@0.24.2 - - k8s.io/client-go/rest@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/client-go/transport@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2301,19 +2252,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/runtime/serializer@0.24.2 - - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/watch@0.24.2 + github.com/skeema/knownhosts@1.2.1 - k8s.io/apimachinery/pkg/util/net@0.24.2 + golang.org/x/crypto/ssh/knownhosts@0.16.0 - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2322,19 +2267,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/listers/core/v1@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/client-go/tools/cache@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/tools/pager@0.24.2 + github.com/skeema/knownhosts@1.2.1 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - - k8s.io/apimachinery/pkg/watch@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2343,19 +2282,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#4d8552b0775f - - k8s.io/client-go/tools/cache@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/tools/pager@0.24.2 + github.com/xanzy/ssh-agent@0.3.3 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + golang.org/x/crypto/ssh/agent@0.16.0 - k8s.io/apimachinery/pkg/watch@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2364,19 +2297,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/core/v1@0.24.2 - - k8s.io/client-go/tools/cache@0.24.2 - - k8s.io/client-go/tools/pager@0.24.2 + github.com/go-git/go-git/v5@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/apimachinery/pkg/watch@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2385,19 +2312,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers@0.24.2 - - k8s.io/client-go/tools/cache@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/client-go/tools/pager@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + github.com/skeema/knownhosts@1.2.1 - k8s.io/apimachinery/pkg/watch@0.24.2 + golang.org/x/crypto/ssh/knownhosts@0.16.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2406,19 +2329,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#4d8552b0775f - - k8s.io/client-go/tools/cache@0.24.2 + github.com/go-git/go-git/v5@5.11.0 - k8s.io/client-go/tools/pager@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/watch@0.24.2 + github.com/skeema/knownhosts@1.2.1 - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2427,19 +2346,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/term@0.24.2 - - k8s.io/client-go/tools/remotecommand@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/client-go/transport/spdy@0.24.2 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/rest@0.24.2 + github.com/xanzy/ssh-agent@0.3.3 - k8s.io/client-go/transport@0.24.2 + golang.org/x/crypto/ssh/agent@0.16.0 - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2448,21 +2363,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/hook@#98ccd3d43fd9 - - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#98ccd3d43fd9 + github.com/go-git/go-git/v5@5.11.0 - github.com/argoproj/gitops-engine/pkg/sync/common@#98ccd3d43fd9 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@#98ccd3d43fd9 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/kubectl/pkg/util/openapi@0.24.2 + github.com/skeema/knownhosts@1.2.1 - k8s.io/client-go/discovery@0.24.2 + golang.org/x/crypto/ssh/knownhosts@0.16.0 - k8s.io/client-go/rest@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2471,286 +2382,232 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/syncwaves@#98ccd3d43fd9 - - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#98ccd3d43fd9 + github.com/go-git/go-git/v5@5.11.0 - github.com/argoproj/gitops-engine/pkg/sync/common@#98ccd3d43fd9 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@#98ccd3d43fd9 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/kubectl/pkg/util/openapi@0.24.2 + github.com/xanzy/ssh-agent@0.3.3 - k8s.io/client-go/discovery@0.24.2 + golang.org/x/crypto/ssh/agent@0.16.0 - k8s.io/client-go/rest@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/crypto/ssh is a SSH client and server

    +

    Affected versions of this package are vulnerable to Authentication Bypass by Capture-replay during the establishment of the secure channel. An attacker can manipulate handshake sequence numbers to delete messages sent immediately after the channel is established.

    +

    Note:

    +
      +
    1. Sequence numbers are only validated once the channel is established and arbitrary messages are allowed during the handshake, allowing them to manipulate the sequence numbers.

      +
    2. +
    3. The potential consequences of the general Terrapin attack are dependent on the messages exchanged after the handshake concludes. If you are using a custom SSH service and do not resort to the authentication protocol, you should check that dropping the first few messages of a connection does not yield security risks.

      +
    4. +
    +

    Impact:

    +

    While cryptographically novel, there is no discernable impact on the integrity of SSH traffic beyond giving the attacker the ability to delete the message that enables some features related to keystroke timing obfuscation. To successfully carry out the exploitation, the connection needs to be protected using either the ChaCha20-Poly1305 or CBC with Encrypt-then-MAC encryption methods. The attacker must also be able to intercept and modify the connection's traffic.

    +

    Workaround

    +

    Temporarily disable the affected chacha20-poly1305@openssh.com encryption and *-etm@openssh.com MAC algorithms in the affected configuration, and use unaffected algorithms like AES-GCM instead.

    +

    Remediation

    +

    Upgrade golang.org/x/crypto/ssh to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/r3labs/diff +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/r3labs/diff@1.1.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/manager@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/webhook@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/metrics@0.11.0 - - k8s.io/client-go/tools/cache@0.24.2 - - k8s.io/client-go/rest@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + github.com/r3labs/diff@1.1.0
    • -
    • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - - k8s.io/client-go/restmapper@0.24.2 - - k8s.io/client-go/discovery@0.24.2 - - k8s.io/client-go/rest@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 - - +
    - -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - k8s.io/client-go/tools/clientcmd@0.24.2 - - k8s.io/client-go/tools/clientcmd/api/latest@0.24.2 - - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - - k8s.io/apimachinery/pkg/watch@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 - - +
  • - -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - k8s.io/client-go/kubernetes/scheme@0.24.2 - - k8s.io/apimachinery/pkg/runtime/serializer@0.24.2 - - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - - k8s.io/apimachinery/pkg/watch@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 - - +
    + +

    MPL-2.0 license

    -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - - k8s.io/client-go/restmapper@0.24.2 - - k8s.io/client-go/discovery@0.24.2 - - k8s.io/client-go/rest@0.24.2 - - k8s.io/client-go/transport@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 - - +
    -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - github.com/argoproj/gitops-engine/pkg/sync/ignore@#98ccd3d43fd9 - - github.com/argoproj/gitops-engine/pkg/sync/hook@#98ccd3d43fd9 - - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@#98ccd3d43fd9 - - github.com/argoproj/gitops-engine/pkg/sync/common@#98ccd3d43fd9 - - github.com/argoproj/gitops-engine/pkg/utils/kube@#98ccd3d43fd9 - - k8s.io/kubectl/pkg/util/openapi@0.24.2 - - k8s.io/client-go/discovery@0.24.2 - - k8s.io/client-go/rest@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 - - + -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - sigs.k8s.io/controller-runtime/pkg/handler@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - - k8s.io/client-go/restmapper@0.24.2 - - k8s.io/client-go/discovery@0.24.2 - - k8s.io/client-go/rest@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 - - +
  • +
    +

    MPL-2.0 license

    +
    - -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - github.com/argoproj/gitops-engine/pkg/diff@#98ccd3d43fd9 - - k8s.io/client-go/kubernetes/scheme@0.24.2 - - k8s.io/apimachinery/pkg/runtime/serializer@0.24.2 - - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - - k8s.io/apimachinery/pkg/watch@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 - - +
    + medium severity +
    -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - sigs.k8s.io/controller-runtime/pkg/envtest@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/webhook/conversion@0.11.0 - - k8s.io/apimachinery/pkg/runtime/serializer@0.24.2 - - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - - k8s.io/apimachinery/pkg/watch@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 - - +
    -
  • +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-version +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, code.gitea.io/sdk/gitea@0.15.1 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#4d8552b0775f - - k8s.io/client-go/tools/clientcmd@0.24.2 - - k8s.io/client-go/tools/clientcmd/api/latest@0.24.2 + code.gitea.io/sdk/gitea@0.15.1 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - - k8s.io/apimachinery/pkg/watch@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + github.com/hashicorp/go-version@1.2.1
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-retryablehttp +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/hashicorp/go-retryablehttp@0.7.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/kube/scheme@#98ccd3d43fd9 - - k8s.io/kubernetes/pkg/apis/storage/install@1.24.2 - - k8s.io/kubernetes/pkg/apis/storage/v1beta1@1.24.2 - - k8s.io/kubernetes/pkg/apis/storage@1.24.2 - - k8s.io/kubernetes/pkg/apis/core@1.24.2 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.24.2 - - k8s.io/apimachinery/pkg/watch@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -2759,23 +2616,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - - k8s.io/client-go/restmapper@0.24.2 - - k8s.io/client-go/discovery@0.24.2 - - k8s.io/client-go/rest@0.24.2 + github.com/xanzy/go-gitlab@0.86.0 - k8s.io/client-go/transport@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -2784,23 +2627,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/cache@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - - k8s.io/client-go/restmapper@0.24.2 - - k8s.io/client-go/discovery@0.24.2 + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 - k8s.io/client-go/rest@0.24.2 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - k8s.io/client-go/transport@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -2809,25 +2640,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - - k8s.io/client-go/restmapper@0.24.2 + github.com/argoproj/notifications-engine/pkg/cmd@#3446d4ae8520 - k8s.io/client-go/discovery@0.24.2 + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 - k8s.io/client-go/rest@0.24.2 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - k8s.io/client-go/transport@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -2836,27 +2655,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/source@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/source/internal@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/predicate@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 + github.com/argoproj/notifications-engine/pkg/subscriptions@#3446d4ae8520 - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 - k8s.io/client-go/restmapper@0.24.2 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - k8s.io/client-go/discovery@0.24.2 - - k8s.io/client-go/rest@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -2865,27 +2670,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/handler@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 + github.com/argoproj/notifications-engine/pkg/api@#3446d4ae8520 - k8s.io/client-go/restmapper@0.24.2 + github.com/argoproj/notifications-engine/pkg/subscriptions@#3446d4ae8520 - k8s.io/client-go/discovery@0.24.2 + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 - k8s.io/client-go/rest@0.24.2 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - k8s.io/client-go/transport@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -2894,31 +2687,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/source@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/source/internal@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/predicate@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 + github.com/argoproj/notifications-engine/pkg/controller@#3446d4ae8520 - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 + github.com/argoproj/notifications-engine/pkg/subscriptions@#3446d4ae8520 - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 - k8s.io/client-go/restmapper@0.24.2 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - k8s.io/client-go/discovery@0.24.2 - - k8s.io/client-go/rest@0.24.2 - - k8s.io/client-go/transport@0.24.2 - - k8s.io/apimachinery/pkg/util/net@0.24.2 - - golang.org/x/net/http2@#9d032be2e588 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -2929,40 +2706,17 @@

      Detailed paths


      -

      Overview

      -

      golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

      -

      Affected versions of this package are vulnerable to Denial of Service (DoS) due to improper checks and limitations for the number of entries in the cache, which can allow an attacker to consume unbounded amounts of memory by sending a small number of very large keys.

      -

      Details

      -

      Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

      -

      Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

      -

      One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

      -

      When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

      -

      Two common types of DoS vulnerabilities:

      -
        -
      • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

        -
      • -
      • Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

        -
      • -
      -

      Remediation

      -

      Upgrade golang.org/x/net/http2 to version 0.4.0 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Improper Input Validation

    +

    MPL-2.0 license

    @@ -2972,19 +2726,22 @@

    Improper Input Validation


      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • Package Manager: golang
    • - Vulnerable module: + Module: - go.mongodb.org/mongo-driver/bson/bsonrw + github.com/hashicorp/go-cleanhttp
    • Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0, github.com/go-openapi/runtime/middleware@0.19.4 and others + github.com/argoproj/argo-cd/v2@0.0.0, github.com/hashicorp/go-retryablehttp@0.7.4 and others
    @@ -2998,15 +2755,20 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/go-openapi/runtime/middleware@0.19.4 - - github.com/go-openapi/validate@0.19.5 + github.com/hashicorp/go-retryablehttp@0.7.4 - github.com/go-openapi/strfmt@0.19.3 + github.com/hashicorp/go-cleanhttp@0.5.2 + + + + +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - go.mongodb.org/mongo-driver/bson@1.1.2 + github.com/xanzy/go-gitlab@0.86.0 - go.mongodb.org/mongo-driver/bson/bsonrw@1.1.2 + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3015,86 +2777,43 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/go-openapi/runtime/middleware@0.19.4 + github.com/xanzy/go-gitlab@0.86.0 + + github.com/hashicorp/go-retryablehttp@0.7.4 - github.com/go-openapi/validate@0.19.5 + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - github.com/go-openapi/strfmt@0.19.3 + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 - go.mongodb.org/mongo-driver/bson@1.1.2 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - go.mongodb.org/mongo-driver/bson/bsoncodec@1.1.2 + github.com/hashicorp/go-retryablehttp@0.7.4 - go.mongodb.org/mongo-driver/bson/bsonrw@1.1.2 + github.com/hashicorp/go-cleanhttp@0.5.2
  • - - -
    - -
    - -

    Overview

    -

    go.mongodb.org/mongo-driver/bson/bsonrw is a The MongoDB supported driver for Go.

    -

    Affected versions of this package are vulnerable to Improper Input Validation. Specific cstrings input may not be properly validated in the MongoDB Go Driver when marshalling Go objects into BSON. A malicious user could use a Go object with specific string to potentially inject additional fields into marshalled documents.

    -

    Remediation

    -

    Upgrade go.mongodb.org/mongo-driver/bson/bsonrw to version 1.5.1 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Insecure Randomness

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: golang -
    • -
    • - Vulnerable module: - - github.com/Masterminds/goutils -
    • - -
    • Introduced through: - - - github.com/argoproj/argo-cd/v2@0.0.0, github.com/Masterminds/sprig@2.22.0 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/Masterminds/sprig@2.22.0 + github.com/argoproj/notifications-engine/pkg/cmd@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3103,13 +2822,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#4d8552b0775f + github.com/argoproj/notifications-engine/pkg/subscriptions@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 - github.com/argoproj/notifications-engine/pkg/templates@#4d8552b0775f + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/sprig@2.22.0 + github.com/hashicorp/go-retryablehttp@0.7.4 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3118,15 +2839,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#4d8552b0775f + github.com/argoproj/notifications-engine/pkg/api@#3446d4ae8520 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#3446d4ae8520 - github.com/argoproj/notifications-engine/pkg/api@#4d8552b0775f + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 - github.com/argoproj/notifications-engine/pkg/templates@#4d8552b0775f + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/sprig@2.22.0 + github.com/hashicorp/go-retryablehttp@0.7.4 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3135,15 +2858,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#4d8552b0775f + github.com/argoproj/notifications-engine/pkg/controller@#3446d4ae8520 - github.com/argoproj/notifications-engine/pkg/api@#4d8552b0775f + github.com/argoproj/notifications-engine/pkg/subscriptions@#3446d4ae8520 - github.com/argoproj/notifications-engine/pkg/templates@#4d8552b0775f + github.com/argoproj/notifications-engine/pkg/services@#3446d4ae8520 - github.com/Masterminds/sprig@2.22.0 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3154,25 +2879,17 @@

      Detailed paths


      -

      Overview

      -

      github.com/masterminds/goutils is a provides users with utility functions to manipulate strings in various ways.

      -

      Affected versions of this package are vulnerable to Insecure Randomness via the RandomAlphaNumeric(int) and CryptoRandomAlphaNumeric(int) functions. Small values of int in the functions above will return a smaller subset of results than they should. For example, RandomAlphaNumeric(1) would always return a digit in the 0-9 range, while RandomAlphaNumeric(4) return around ~7 million of the ~13M possible permutations.

      -

      Remediation

      -

      Upgrade github.com/masterminds/goutils to version 1.1.1 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Insecure Randomness

    +

    MPL-2.0 license

    @@ -3182,19 +2899,22 @@

    Insecure Randomness


      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/Masterminds/goutils + github.com/gosimple/slug
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/gosimple/slug@1.13.1 - github.com/argoproj/argo-cd/v2@0.0.0, github.com/Masterminds/sprig@2.22.0 and others
    @@ -3208,58 +2928,7 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/Masterminds/sprig@2.22.0 - - github.com/Masterminds/goutils@1.1.0 - - - - -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - github.com/argoproj/notifications-engine/pkg/api@#4d8552b0775f - - github.com/argoproj/notifications-engine/pkg/templates@#4d8552b0775f - - github.com/Masterminds/sprig@2.22.0 - - github.com/Masterminds/goutils@1.1.0 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - github.com/argoproj/notifications-engine/pkg/cmd@#4d8552b0775f - - github.com/argoproj/notifications-engine/pkg/api@#4d8552b0775f - - github.com/argoproj/notifications-engine/pkg/templates@#4d8552b0775f - - github.com/Masterminds/sprig@2.22.0 - - github.com/Masterminds/goutils@1.1.0 - - - -
  • -
  • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - github.com/argoproj/notifications-engine/pkg/controller@#4d8552b0775f - - github.com/argoproj/notifications-engine/pkg/api@#4d8552b0775f - - github.com/argoproj/notifications-engine/pkg/templates@#4d8552b0775f - - github.com/Masterminds/sprig@2.22.0 - - github.com/Masterminds/goutils@1.1.0 + github.com/gosimple/slug@1.13.1 @@ -3270,25 +2939,17 @@

    Detailed paths


    -

    Overview

    -

    github.com/masterminds/goutils is a provides users with utility functions to manipulate strings in various ways.

    -

    Affected versions of this package are vulnerable to Insecure Randomness when randomly-generated alphanumeric strings contain significantly less entropy than expected, the RandomAlphaNumeric and CryptoRandomAlphaNumeric functions always return strings containing at least one digit from 0 to 9. This significantly reduces the amount of entropy in short strings generated by these functions.

    -

    Remediation

    -

    Upgrade github.com/masterminds/goutils to version 1.1.1 or higher.

    -

    References

    - +

    MPL-2.0 license


  • -

    Regular Expression Denial of Service (ReDoS)

    +

    Improper Handling of Highly Compressed Data (Data Amplification)

    @@ -3299,18 +2960,21 @@

    Regular Expression Denial of Service (ReDoS)

    • - Package Manager: npm + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang
    • Vulnerable module: - cookiejar + github.com/go-jose/go-jose/v3
    • Introduced through: - argo-cd-ui@1.0.0, superagent@7.1.6 and others + github.com/argoproj/argo-cd/v2@0.0.0, github.com/coreos/go-oidc/v3/oidc@3.6.0 and others
    @@ -3322,11 +2986,11 @@

    Detailed paths

    • Introduced through: - argo-cd-ui@1.0.0 + github.com/argoproj/argo-cd/v2@0.0.0 - superagent@7.1.6 + github.com/coreos/go-oidc/v3/oidc@3.6.0 - cookiejar@2.1.3 + github.com/go-jose/go-jose/v3@3.0.0 @@ -3338,91 +3002,20 @@

      Detailed paths


      Overview

      -

      Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) via the Cookie.parse function, which uses an insecure regular expression.

      -

      PoC

      -
      const { CookieJar } = require("cookiejar");
      -        
      -        const jar = new CookieJar();
      -        
      -        const start = performance.now();
      -        const attack = "a" + "t".repeat(50_000);
      -        jar.setCookie(attack);
      -        console.log(`CookieJar.setCookie(): ${performance.now() - start}`);
      -        
      -

      Details

      -

      Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.

      -

      The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.

      -

      Let’s take the following regular expression as an example:

      -
      regex = /A(B|C+)+D/
      -        
      -

      This regular expression accomplishes the following:

      -
        -
      • A The string must start with the letter 'A'
      • -
      • (B|C+)+ The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the + matches one or more times). The + at the end of this section states that we can look for one or more matches of this section.
      • -
      • D Finally, we ensure this section of the string ends with a 'D'
      • -
      -

      The expression would match inputs such as ABBD, ABCCCCD, ABCBCCCD and ACCCCCD

      -

      It most cases, it doesn't take very long for a regex engine to find a match:

      -
      $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD")'
      -        0.04s user 0.01s system 95% cpu 0.052 total
      -        
      -        $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX")'
      -        1.79s user 0.02s system 99% cpu 1.812 total
      -        
      -

      The entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.

      -

      Most Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as catastrophic backtracking.

      -

      Let's look at how our expression runs into this problem, using a shorter string: "ACCCX". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:

      -
        -
      1. CCC
      2. -
      3. CC+C
      4. -
      5. C+CC
      6. -
      7. C+C+C.
      8. -
      -

      The engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use RegEx 101 debugger to see the engine has to take a total of 38 steps before it can determine the string doesn't match.

      -

      From there, the number of steps the engine must use to validate a string just continues to grow.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      StringNumber of C'sNumber of steps
      ACCCX338
      ACCCCX471
      ACCCCCX5136
      ACCCCCCCCCCCCCCX1465,553
      -

      By the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.

      +

      Affected versions of this package are vulnerable to Improper Handling of Highly Compressed Data (Data Amplification). An attacker could send a JWE containing compressed data that, when decompressed by Decrypt or DecryptMulti, would use large amounts of memory and CPU.

      Remediation

      -

      Upgrade cookiejar to version 2.1.4 or higher.

      +

      Upgrade github.com/go-jose/go-jose/v3 to version 3.0.3 or higher.

      References


    diff --git a/docs/snyk/v2.8.13/ghcr.io_dexidp_dex_v2.37.0.html b/docs/snyk/v2.8.13/ghcr.io_dexidp_dex_v2.37.0.html new file mode 100644 index 0000000000000..24a737a6ba12f --- /dev/null +++ b/docs/snyk/v2.8.13/ghcr.io_dexidp_dex_v2.37.0.html @@ -0,0 +1,4337 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    March 24th 2024, 12:19:56 am (UTC+00:00)

    +
    +
    + Scanned the following paths: +
      +
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (apk)
    • +
    • ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3//usr/local/bin/gomplate (gomodules)
    • +
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex//usr/local/bin/docker-entrypoint (gomodules)
    • +
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex//usr/local/bin/dex (gomodules)
    • +
    +
    + +
    +
    42 known vulnerabilities
    +
    121 vulnerable dependency paths
    +
    786 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    Path Traversal

    +
    + +
    + critical severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-git/go-git/v5 +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/go-git/go-git/v5@v5.4.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/go-git/go-git/v5@v5.4.2 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Path Traversal via malicious server replies. An attacker can create and amend files across the filesystem and potentially achieve remote code execution by sending crafted responses to the client.

    +

    Notes:

    +
      +
    1. This is only exploitable if the client is using ChrootOS, which is the default for certain functions such as PlainClone.

      +
    2. +
    3. Applications using BoundOS or in-memory filesystems are not affected by this issue.

      +
    4. +
    5. Users running versions of go-git from v4 and above are recommended to upgrade to v5.11 in order to mitigate this vulnerability.

      +
    6. +
    +

    Workaround

    +

    This vulnerability can be mitigated by limiting the client's use to trustworthy Git servers.

    +

    Remediation

    +

    Upgrade github.com/go-git/go-git/v5 to version 5.11.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + critical severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + busybox/busybox +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and busybox/busybox@1.36.1-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/busybox@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + alpine-baselayout/alpine-baselayout@3.4.3-r1 + + busybox/busybox-binsh@1.36.1-r0 + + busybox/busybox@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/busybox-binsh@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + alpine-baselayout/alpine-baselayout@3.4.3-r1 + + busybox/busybox-binsh@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream busybox package and not the busybox package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    There is a stack overflow vulnerability in ash.c:6030 in busybox before 1.35. In the environment of Internet of Vehicles, this vulnerability can be executed from command to arbitrary code execution.

    +

    Remediation

    +

    Upgrade Alpine:3.18 busybox to version 1.36.1-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-5363

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: A bug has been identified in the processing of key and + initialisation vector (IV) lengths. This can lead to potential truncation + or overruns during the initialisation of some symmetric ciphers.

    +

    Impact summary: A truncation in the IV can result in non-uniqueness, + which could result in loss of confidentiality for some cipher modes.

    +

    When calling EVP_EncryptInit_ex2(), EVP_DecryptInit_ex2() or + EVP_CipherInit_ex2() the provided OSSL_PARAM array is processed after + the key and IV have been established. Any alterations to the key length, + via the "keylen" parameter or the IV length, via the "ivlen" parameter, + within the OSSL_PARAM array will not take effect as intended, potentially + causing truncation or overreading of these values. The following ciphers + and cipher modes are impacted: RC2, RC4, RC5, CCM, GCM and OCB.

    +

    For the CCM, GCM and OCB cipher modes, truncation of the IV can result in + loss of confidentiality. For example, when following NIST's SP 800-38D + section 8.2.1 guidance for constructing a deterministic IV for AES in + GCM mode, truncation of the counter portion could lead to IV reuse.

    +

    Both truncations and overruns of the key and overruns of the IV will + produce incorrect results and could, in some cases, trigger a memory + exception. However, these issues are not currently assessed as security + critical.

    +

    Changing the key and/or IV lengths is not considered to be a common operation + and the vulnerable API was recently introduced. Furthermore it is likely that + application developers will have spotted this problem during testing since + decryption would fail unless both peers in the communication were similarly + vulnerable. For these reasons we expect the probability of an application being + vulnerable to this to be quite low. However if an application is vulnerable then + this issue is considered very serious. For these reasons we have assessed this + issue as Moderate severity overall.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this because + the issue lies outside of the FIPS provider boundary.

    +

    OpenSSL 3.1 and 3.0 are vulnerable to this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/grpc +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/grpc@v1.46.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/grpc@v1.46.2 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/grpc@v1.56.1 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    google.golang.org/grpc is a Go implementation of gRPC

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) in the implementation of the HTTP/2 protocol. An attacker can cause a denial of service (including via DDoS) by rapidly resetting many streams through request cancellation.

    +

    Remediation

    +

    Upgrade google.golang.org/grpc to version 1.56.3, 1.57.1, 1.58.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and golang.org/x/net/http2@v0.7.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + golang.org/x/net/http2@v0.7.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + golang.org/x/net/http2@v0.11.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) in the implementation of the HTTP/2 protocol. An attacker can cause a denial of service (including via DDoS) by rapidly resetting many streams through request cancellation.

    +

    Remediation

    +

    Upgrade golang.org/x/net/http2 to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Heap-based Buffer Overflow

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/mattn/go-sqlite3 +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/mattn/go-sqlite3@v1.14.17 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/mattn/go-sqlite3@v1.14.17 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Heap-based Buffer Overflow via the sessionReadRecord function in the ext/session/sqlite3session.c file. An attacker can cause a program crash or execute arbitrary code by manipulating the input to trigger a heap-based buffer overflow.

    +

    Remediation

    +

    Upgrade github.com/mattn/go-sqlite3 to version 1.14.18 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-jose/go-jose/v3 +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/go-jose/go-jose/v3@v3.0.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/go-jose/go-jose/v3@v3.0.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) when decrypting JWE inputs. An attacker can cause a denial-of-service by providing a PBES2 encrypted JWE blob with a very large p2c value.

    +

    Details

    +

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

    +

    Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

    +

    One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

    +

    When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

    +

    Two common types of DoS vulnerabilities:

    +
      +
    • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

      +
    • +
    • Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

      +
    • +
    +

    Remediation

    +

    Upgrade github.com/go-jose/go-jose/v3 to version 3.0.1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Authentication

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The AES-SIV cipher implementation contains a bug that causes + it to ignore empty associated data entries which are unauthenticated as + a consequence.

    +

    Impact summary: Applications that use the AES-SIV algorithm and want to + authenticate empty data entries as associated data can be mislead by removing + adding or reordering such empty entries as these are ignored by the OpenSSL + implementation. We are currently unaware of any such applications.

    +

    The AES-SIV algorithm allows for authentication of multiple associated + data entries along with the encryption. To authenticate empty data the + application has to call EVP_EncryptUpdate() (or EVP_CipherUpdate()) with + NULL pointer as the output buffer and 0 as the input buffer length. + The AES-SIV implementation in OpenSSL just returns success for such a call + instead of performing the associated data authentication operation. + The empty data thus will not be authenticated.

    +

    As this issue does not affect non-empty associated data authentication and + we expect it to be rare for an application to use empty associated data + entries this is qualified as Low severity issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.1-r2 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Inefficient Regular Expression Complexity

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Checking excessively long DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_check(), DH_check_ex() + or EVP_PKEY_param_check() to check a DH key or DH parameters may experience long + delays. Where the key or parameters that are being checked have been obtained + from an untrusted source this may lead to a Denial of Service.

    +

    The function DH_check() performs various checks on DH parameters. One of those + checks confirms that the modulus ('p' parameter) is not too large. Trying to use + a very large modulus is slow and OpenSSL will not normally use a modulus which + is over 10,000 bits in length.

    +

    However the DH_check() function checks numerous aspects of the key or parameters + that have been supplied. Some of those checks use the supplied modulus value + even if it has already been found to be too large.

    +

    An application that calls DH_check() and supplies a key or parameters obtained + from an untrusted source could be vulernable to a Denial of Service attack.

    +

    The function DH_check() is itself called by a number of other OpenSSL functions. + An application calling any of those other functions may similarly be affected. + The other functions affected by this are DH_check_ex() and + EVP_PKEY_param_check().

    +

    Also vulnerable are the OpenSSL dhparam and pkeyparam command line applications + when using the '-check' option.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue. + The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.1-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Excessive Iteration

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Checking excessively long DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_check(), DH_check_ex() + or EVP_PKEY_param_check() to check a DH key or DH parameters may experience long + delays. Where the key or parameters that are being checked have been obtained + from an untrusted source this may lead to a Denial of Service.

    +

    The function DH_check() performs various checks on DH parameters. After fixing + CVE-2023-3446 it was discovered that a large q parameter value can also trigger + an overly long computation during some of these checks. A correct q value, + if present, cannot be larger than the modulus p parameter, thus it is + unnecessary to perform these checks if q is larger than p.

    +

    An application that calls DH_check() and supplies a key or parameters obtained + from an untrusted source could be vulnerable to a Denial of Service attack.

    +

    The function DH_check() is itself called by a number of other OpenSSL functions. + An application calling any of those other functions may similarly be affected. + The other functions affected by this are DH_check_ex() and + EVP_PKEY_param_check().

    +

    Also vulnerable are the OpenSSL dhparam and pkeyparam command line applications + when using the "-check" option.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.2-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Check for Unusual or Exceptional Conditions

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Generating excessively long X9.42 DH keys or checking + excessively long X9.42 DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_generate_key() to + generate an X9.42 DH key may experience long delays. Likewise, applications + that use DH_check_pub_key(), DH_check_pub_key_ex() or EVP_PKEY_public_check() + to check an X9.42 DH key or X9.42 DH parameters may experience long delays. + Where the key or parameters that are being checked have been obtained from + an untrusted source this may lead to a Denial of Service.

    +

    While DH_check() performs all the necessary checks (as of CVE-2023-3817), + DH_check_pub_key() doesn't make any of these checks, and is therefore + vulnerable for excessively large P and Q parameters.

    +

    Likewise, while DH_generate_key() performs a check for an excessively large + P, it doesn't check for an excessively large Q.

    +

    An application that calls DH_generate_key() or DH_check_pub_key() and + supplies a key or parameters obtained from an untrusted source could be + vulnerable to a Denial of Service attack.

    +

    DH_generate_key() and DH_check_pub_key() are also called by a number of + other OpenSSL functions. An application calling any of those other + functions may similarly be affected. The other functions affected by this + are DH_check_pub_key_ex(), EVP_PKEY_public_check(), and EVP_PKEY_generate().

    +

    Also vulnerable are the OpenSSL pkey command line application when using the + "-pubcheck" option, as well as the OpenSSL genpkey command line application.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

    +

    Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

    +

    The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

    +

    The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

    +

    The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-0727

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

    +

    Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

    +

    A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

    +

    OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

    +

    We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

    +

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r5 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/internal/encoding/json +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/internal/encoding/json@v1.28.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/protobuf/internal/encoding/json@v1.28.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/protobuf/internal/encoding/json@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Stack-based Buffer Overflow

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/encoding/protojson@v1.28.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/protobuf/encoding/protojson@v1.28.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Stack-based Buffer Overflow when processing input that uses pathologically deep nesting.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/encoding/protojson@v1.28.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/protobuf/encoding/protojson@v1.28.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Allocation of Resources Without Limits or Throttling

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and golang.org/x/net/http2@v0.7.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + golang.org/x/net/http2@v0.7.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + golang.org/x/net/http2@v0.11.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

    +

    Affected versions of this package are vulnerable to Allocation of Resources Without Limits or Throttling when MaxConcurrentStreams handler goroutines running. A a handler is started until one of the existing handlers exits.

    +

    Note:

    +

    This issue is related to CVE-2023-44487

    +

    Remediation

    +

    Upgrade golang.org/x/net/http2 to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Cross-site Scripting (XSS)

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/html +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and golang.org/x/net/html@v0.11.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + golang.org/x/net/html@v0.11.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/net/html is a package that implements an HTML5-compliant tokenizer and parser.

    +

    Affected versions of this package are vulnerable to Cross-site Scripting (XSS) in the render1() function in render.go. Text nodes not in the HTML namespace are incorrectly literally rendered, causing text which should be escaped to not be.

    +

    Details

    +

    A cross-site scripting attack occurs when the attacker tricks a legitimate web-based application or site to accept a request as originating from a trusted source.

    +

    This is done by escaping the context of the web application; the web application then delivers that data to its users along with other trusted dynamic content, without validating it. The browser unknowingly executes malicious script on the client side (through client-side languages; usually JavaScript or HTML) in order to perform actions that are otherwise typically blocked by the browser’s Same Origin Policy.

    +

    Injecting malicious code is the most prevalent manner by which XSS is exploited; for this reason, escaping characters in order to prevent this manipulation is the top method for securing code against this vulnerability.

    +

    Escaping means that the application is coded to mark key characters, and particularly key characters included in user input, to prevent those characters from being interpreted in a dangerous context. For example, in HTML, < can be coded as &lt; and > can be coded as &gt; in order to be interpreted and displayed as themselves in text, while within the code itself, they are used for HTML tags. If malicious content is injected into an application that escapes special characters and that malicious content uses < and > as HTML tags, those characters are nonetheless not interpreted as HTML tags by the browser if they’ve been correctly escaped in the application code and in this way the attempted attack is diverted.

    +

    The most prominent use of XSS is to steal cookies (source: OWASP HttpOnly) and hijack user sessions, but XSS exploits have been used to expose sensitive information, enable access to privileged services and functionality and deliver malware.

    +

    Types of attacks

    +

    There are a few methods by which XSS can be manipulated:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeOriginDescription
    StoredServerThe malicious code is inserted in the application (usually as a link) by the attacker. The code is activated every time a user clicks the link.
    ReflectedServerThe attacker delivers a malicious link externally from the vulnerable web site application to a user. When clicked, malicious code is sent to the vulnerable web site, which reflects the attack back to the user’s browser.
    DOM-basedClientThe attacker forces the user’s browser to render a malicious page. The data in the page itself delivers the cross-site scripting data.
    MutatedThe attacker injects code that appears safe, but is then rewritten and modified by the browser, while parsing the markup. An example is rebalancing unclosed quotation marks or even adding quotation marks to unquoted parameters.
    +

    Affected environments

    +

    The following environments are susceptible to an XSS attack:

    +
      +
    • Web servers
    • +
    • Application servers
    • +
    • Web application environments
    • +
    +

    How to prevent

    +

    This section describes the top best practices designed to specifically protect your code:

    +
      +
    • Sanitize data input in an HTTP request before reflecting it back, ensuring all data is validated, filtered or escaped before echoing anything back to the user, such as the values of query parameters during searches.
    • +
    • Convert special characters such as ?, &, /, <, > and spaces to their respective HTML or URL encoded equivalents.
    • +
    • Give users the option to disable client-side scripts.
    • +
    • Redirect invalid requests.
    • +
    • Detect simultaneous logins, including those from two separate IP addresses, and invalidate those sessions.
    • +
    • Use and enforce a Content Security Policy (source: Wikipedia) to disable any features that might be manipulated for an XSS attack.
    • +
    • Read the documentation for any of the libraries referenced in your code to understand which elements allow for embedded HTML.
    • +
    +

    Remediation

    +

    Upgrade golang.org/x/net/html to version 0.13.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Authentication Bypass by Capture-replay

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/crypto/ssh +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and golang.org/x/crypto/ssh@v0.0.0-20220525230936-793ad666bf5e + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + golang.org/x/crypto/ssh@v0.0.0-20220525230936-793ad666bf5e + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/crypto/ssh is a SSH client and server

    +

    Affected versions of this package are vulnerable to Authentication Bypass by Capture-replay during the establishment of the secure channel. An attacker can manipulate handshake sequence numbers to delete messages sent immediately after the channel is established.

    +

    Note:

    +
      +
    1. Sequence numbers are only validated once the channel is established and arbitrary messages are allowed during the handshake, allowing them to manipulate the sequence numbers.

      +
    2. +
    3. The potential consequences of the general Terrapin attack are dependent on the messages exchanged after the handshake concludes. If you are using a custom SSH service and do not resort to the authentication protocol, you should check that dropping the first few messages of a connection does not yield security risks.

      +
    4. +
    +

    Impact:

    +

    While cryptographically novel, there is no discernable impact on the integrity of SSH traffic beyond giving the attacker the ability to delete the message that enables some features related to keystroke timing obfuscation. To successfully carry out the exploitation, the connection needs to be protected using either the ChaCha20-Poly1305 or CBC with Encrypt-then-MAC encryption methods. The attacker must also be able to intercept and modify the connection's traffic.

    +

    Workaround

    +

    Temporarily disable the affected chacha20-poly1305@openssh.com encryption and *-etm@openssh.com MAC algorithms in the affected configuration, and use unaffected algorithms like AES-GCM instead.

    +

    Remediation

    +

    Upgrade golang.org/x/crypto/ssh to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/vault/sdk/helper/certutil +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/vault/sdk/helper/certutil@v0.5.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/certutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/compressutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/consts@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/jsonutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/pluginutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/strutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/logical@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/physical@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/physical/inmem@v0.5.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/vault/api +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/vault/api@v1.6.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/api@v1.6.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/serf/coordinate +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/serf/coordinate@v0.9.7 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/serf/coordinate@v0.9.7 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/hcl/v2 +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/hashicorp/hcl/v2@v2.13.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/ext/customdecode@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/ext/tryfunc@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/gohcl@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/hclparse@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/hclsyntax@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/hclwrite@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/json@v2.13.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/hcl +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/hcl@v1.0.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl@v1.0.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/hcl/parser@v1.0.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/hcl/strconv@v1.0.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/hcl/token@v1.0.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/json/parser@v1.0.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/golang-lru/simplelru +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/golang-lru/simplelru@v0.5.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/golang-lru/simplelru@v0.5.4 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-version +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-version@v1.5.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-version@v1.5.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-sockaddr +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-sockaddr@v1.0.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-sockaddr@v1.0.2 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-sockaddr/template@v1.0.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-secure-stdlib/strutil +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-secure-stdlib/strutil@v0.1.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-secure-stdlib/strutil@v0.1.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-secure-stdlib/parseutil +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-secure-stdlib/parseutil@v0.1.5 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-secure-stdlib/parseutil@v0.1.5 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-secure-stdlib/mlock +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-secure-stdlib/mlock@v0.1.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-secure-stdlib/mlock@v0.1.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-rootcerts +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-rootcerts@v1.0.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-rootcerts@v1.0.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-retryablehttp +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-retryablehttp@v0.7.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-retryablehttp@v0.7.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-plugin +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-plugin@v1.4.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-plugin@v1.4.4 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-plugin/internal/plugin@v1.4.4 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-immutable-radix +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-immutable-radix@v1.3.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-immutable-radix@v1.3.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-cleanhttp +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-cleanhttp@v0.5.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-cleanhttp@v0.5.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/errwrap +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/errwrap@v1.1.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/errwrap@v1.1.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/consul/api +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/consul/api@v1.13.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/consul/api@v1.13.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/gosimple/slug +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/gosimple/slug@v1.12.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/gosimple/slug@v1.12.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/go-sql-driver/mysql +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/go-sql-driver/mysql@v1.7.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/go-sql-driver/mysql@v1.7.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    Improper Handling of Highly Compressed Data (Data Amplification)

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-jose/go-jose/v3 +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/go-jose/go-jose/v3@v3.0.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/go-jose/go-jose/v3@v3.0.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Improper Handling of Highly Compressed Data (Data Amplification). An attacker could send a JWE containing compressed data that, when decompressed by Decrypt or DecryptMulti, would use large amounts of memory and CPU.

    +

    Remediation

    +

    Upgrade github.com/go-jose/go-jose/v3 to version 3.0.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Uncontrolled Resource Consumption ('Resource Exhaustion')

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-git/go-git/v5/plumbing +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/go-git/go-git/v5/plumbing@v5.4.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/go-git/go-git/v5/plumbing@v5.4.2 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    github.com/go-git/go-git/v5/plumbing is a highly extensible git implementation library written in pure Go.

    +

    Affected versions of this package are vulnerable to Uncontrolled Resource Consumption ('Resource Exhaustion') via specially crafted responses from a Git server, which triggers resource exhaustion in clients.

    +

    Note + This is only exploitable if the client is not using the in-memory filesystem supported by the library.

    +

    Workaround

    +

    In cases where a bump to the latest version of go-git is not possible, we recommend limiting its use to only trust-worthy Git servers.

    +

    Details

    +

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

    +

    Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

    +

    One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

    +

    When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

    +

    Two common types of DoS vulnerabilities:

    +
      +
    • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

      +
    • +
    • Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

      +
    • +
    +

    Remediation

    +

    Upgrade github.com/go-git/go-git/v5/plumbing to version 5.11.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-6237

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    This vulnerability has not been analyzed by NVD yet.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r4 or higher.

    + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.8.13/haproxy_2.6.14-alpine.html b/docs/snyk/v2.8.13/haproxy_2.6.14-alpine.html new file mode 100644 index 0000000000000..b2b3a76ed356e --- /dev/null +++ b/docs/snyk/v2.8.13/haproxy_2.6.14-alpine.html @@ -0,0 +1,1376 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    March 24th 2024, 12:20:01 am (UTC+00:00)

    +
    +
    + Scanned the following path: +
      +
    • haproxy:2.6.14-alpine (apk)
    • +
    +
    + +
    +
    5 known vulnerabilities
    +
    45 vulnerable dependency paths
    +
    18 dependencies
    +
    +
    +
    +
    +
    + + + + + + + +
    Project docker-image|haproxy
    Path haproxy:2.6.14-alpine
    Package Manager apk
    +
    +
    +
    +
    +

    CVE-2023-5363

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: A bug has been identified in the processing of key and + initialisation vector (IV) lengths. This can lead to potential truncation + or overruns during the initialisation of some symmetric ciphers.

    +

    Impact summary: A truncation in the IV can result in non-uniqueness, + which could result in loss of confidentiality for some cipher modes.

    +

    When calling EVP_EncryptInit_ex2(), EVP_DecryptInit_ex2() or + EVP_CipherInit_ex2() the provided OSSL_PARAM array is processed after + the key and IV have been established. Any alterations to the key length, + via the "keylen" parameter or the IV length, via the "ivlen" parameter, + within the OSSL_PARAM array will not take effect as intended, potentially + causing truncation or overreading of these values. The following ciphers + and cipher modes are impacted: RC2, RC4, RC5, CCM, GCM and OCB.

    +

    For the CCM, GCM and OCB cipher modes, truncation of the IV can result in + loss of confidentiality. For example, when following NIST's SP 800-38D + section 8.2.1 guidance for constructing a deterministic IV for AES in + GCM mode, truncation of the counter portion could lead to IV reuse.

    +

    Both truncations and overruns of the key and overruns of the IV will + produce incorrect results and could, in some cases, trigger a memory + exception. However, these issues are not currently assessed as security + critical.

    +

    Changing the key and/or IV lengths is not considered to be a common operation + and the vulnerable API was recently introduced. Furthermore it is likely that + application developers will have spotted this problem during testing since + decryption would fail unless both peers in the communication were similarly + vulnerable. For these reasons we expect the probability of an application being + vulnerable to this to be quite low. However if an application is vulnerable then + this issue is considered very serious. For these reasons we have assessed this + issue as Moderate severity overall.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this because + the issue lies outside of the FIPS provider boundary.

    +

    OpenSSL 3.1 and 3.0 are vulnerable to this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Check for Unusual or Exceptional Conditions

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Generating excessively long X9.42 DH keys or checking + excessively long X9.42 DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_generate_key() to + generate an X9.42 DH key may experience long delays. Likewise, applications + that use DH_check_pub_key(), DH_check_pub_key_ex() or EVP_PKEY_public_check() + to check an X9.42 DH key or X9.42 DH parameters may experience long delays. + Where the key or parameters that are being checked have been obtained from + an untrusted source this may lead to a Denial of Service.

    +

    While DH_check() performs all the necessary checks (as of CVE-2023-3817), + DH_check_pub_key() doesn't make any of these checks, and is therefore + vulnerable for excessively large P and Q parameters.

    +

    Likewise, while DH_generate_key() performs a check for an excessively large + P, it doesn't check for an excessively large Q.

    +

    An application that calls DH_generate_key() or DH_check_pub_key() and + supplies a key or parameters obtained from an untrusted source could be + vulnerable to a Denial of Service attack.

    +

    DH_generate_key() and DH_check_pub_key() are also called by a number of + other OpenSSL functions. An application calling any of those other + functions may similarly be affected. The other functions affected by this + are DH_check_pub_key_ex(), EVP_PKEY_public_check(), and EVP_PKEY_generate().

    +

    Also vulnerable are the OpenSSL pkey command line application when using the + "-pubcheck" option, as well as the OpenSSL genpkey command line application.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

    +

    Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

    +

    The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

    +

    The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

    +

    The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-0727

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

    +

    Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

    +

    A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

    +

    OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

    +

    We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

    +

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r5 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-6237

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    This vulnerability has not been analyzed by NVD yet.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r4 or higher.

    + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.8.13/quay.io_argoproj_argocd_v2.8.13.html b/docs/snyk/v2.8.13/quay.io_argoproj_argocd_v2.8.13.html new file mode 100644 index 0000000000000..01078e7e7a861 --- /dev/null +++ b/docs/snyk/v2.8.13/quay.io_argoproj_argocd_v2.8.13.html @@ -0,0 +1,5054 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    March 24th 2024, 12:20:18 am (UTC+00:00)

    +
    +
    + Scanned the following paths: +
      +
    • quay.io/argoproj/argocd:v2.8.13/argoproj/argocd/Dockerfile (deb)
    • +
    • quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2//usr/local/bin/argocd (gomodules)
    • +
    • quay.io/argoproj/argocd:v2.8.13/kustomize/kustomize/v5//usr/local/bin/kustomize (gomodules)
    • +
    • quay.io/argoproj/argocd:v2.8.13/helm/v3//usr/local/bin/helm (gomodules)
    • +
    • quay.io/argoproj/argocd:v2.8.13/git-lfs/git-lfs//usr/bin/git-lfs (gomodules)
    • +
    +
    + +
    +
    39 known vulnerabilities
    +
    182 vulnerable dependency paths
    +
    2120 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/helm/v3 /usr/local/bin/helm +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + helm.sh/helm/v3@* and golang.org/x/net/http2@v0.8.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + helm.sh/helm/v3@* + + golang.org/x/net/http2@v0.8.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) in the implementation of the HTTP/2 protocol. An attacker can cause a denial of service (including via DDoS) by rapidly resetting many streams through request cancellation.

    +

    Remediation

    +

    Upgrade golang.org/x/net/http2 to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-jose/go-jose/v3 +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/go-jose/go-jose/v3@v3.0.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/go-jose/go-jose/v3@v3.0.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) when decrypting JWE inputs. An attacker can cause a denial-of-service by providing a PBES2 encrypted JWE blob with a very large p2c value.

    +

    Details

    +

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

    +

    Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

    +

    One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

    +

    When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

    +

    Two common types of DoS vulnerabilities:

    +
      +
    • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

      +
    • +
    • Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

      +
    • +
    +

    Remediation

    +

    Upgrade github.com/go-jose/go-jose/v3 to version 3.0.1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Directory Traversal

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/helm/v3 /usr/local/bin/helm +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/cyphar/filepath-securejoin +
    • + +
    • Introduced through: + + helm.sh/helm/v3@* and github.com/cyphar/filepath-securejoin@v0.2.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + helm.sh/helm/v3@* + + github.com/cyphar/filepath-securejoin@v0.2.3 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Directory Traversal via the filepath.FromSlash() function, allwoing attackers to generate paths that were outside of the provided rootfs.

    +

    Note: + This vulnerability is only exploitable on Windows OS.

    +

    Details

    +

    A Directory Traversal attack (also known as path traversal) aims to access files and directories that are stored outside the intended folder. By manipulating files with "dot-dot-slash (../)" sequences and its variations, or by using absolute file paths, it may be possible to access arbitrary files and directories stored on file system, including application source code, configuration, and other critical system files.

    +

    Directory Traversal vulnerabilities can be generally divided into two types:

    +
      +
    • Information Disclosure: Allows the attacker to gain information about the folder structure or read the contents of sensitive files on the system.
    • +
    +

    st is a module for serving static files on web pages, and contains a vulnerability of this type. In our example, we will serve files from the public route.

    +

    If an attacker requests the following URL from our server, it will in turn leak the sensitive private key of the root user.

    +
    curl http://localhost:8080/public/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/root/.ssh/id_rsa
    +        
    +

    Note %2e is the URL encoded version of . (dot).

    +
      +
    • Writing arbitrary files: Allows the attacker to create or replace existing files. This type of vulnerability is also known as Zip-Slip.
    • +
    +

    One way to achieve this is by using a malicious zip archive that holds path traversal filenames. When each filename in the zip archive gets concatenated to the target extraction folder, without validation, the final path ends up outside of the target folder. If an executable or a configuration file is overwritten with a file containing malicious code, the problem can turn into an arbitrary code execution issue quite easily.

    +

    The following is an example of a zip archive with one benign file and one malicious file. Extracting the malicious file will result in traversing out of the target folder, ending up in /root/.ssh/ overwriting the authorized_keys file:

    +
    2018-04-15 22:04:29 .....           19           19  good.txt
    +        2018-04-15 22:04:42 .....           20           20  ../../../../../../root/.ssh/authorized_keys
    +        
    +

    Remediation

    +

    Upgrade github.com/cyphar/filepath-securejoin to version 0.2.4 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2020-22916

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + xz-utils/liblzma5 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and xz-utils/liblzma5@5.2.5-2ubuntu1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + xz-utils/liblzma5@5.2.5-2ubuntu1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream xz-utils package and not the xz-utils package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    An issue discovered in XZ 5.2.5 allows attackers to cause a denial of service via decompression of a crafted file. NOTE: the vendor disputes the claims of "endless output" and "denial of service" because decompression of the 17,486 bytes always results in 114,881,179 bytes, which is often a reasonable size increase.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 xz-utils.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-51767

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + openssh/openssh-client +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and openssh/openssh-client@1:8.9p1-3ubuntu0.6 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssh package and not the openssh package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    OpenSSH through 9.6, when common types of DRAM are used, might allow row hammer attacks (for authentication bypass) because the integer value of authenticated in mm_answer_authpassword does not resist flips of a single bit. NOTE: this is applicable to a certain threat model of attacker-victim co-location in which the attacker has user privileges.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 openssh.

    +

    References

    + + +
    + + + +
    +
    +

    Information Exposure

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + libgcrypt20 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and libgcrypt20@1.9.4-3ubuntu3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + apt@2.4.11 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream libgcrypt20 package and not the libgcrypt20 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A timing-based side-channel flaw was found in libgcrypt's RSA implementation. This issue may allow a remote attacker to initiate a Bleichenbacher-style attack, which can lead to the decryption of RSA ciphertexts.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 libgcrypt20.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-26461

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libkrb5support0@1.19.2-2ubuntu0.3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    Kerberos 5 (aka krb5) 1.21.2 contains a memory leak vulnerability in /krb5/src/lib/gssapi/krb5/k5sealv3.c.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-26462

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libkrb5support0@1.19.2-2ubuntu0.3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    Kerberos 5 (aka krb5) 1.21.2 contains a memory leak vulnerability in /krb5/src/kdc/ndr.c.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-26458

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libkrb5support0@1.19.2-2ubuntu0.3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    Kerberos 5 (aka krb5) 1.21.2 contains a memory leak in /krb5/src/lib/rpc/pmap_rmt.c.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    References

    + + +
    + + + +
    +
    +

    LGPL-3.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + gopkg.in/retry.v1 +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and gopkg.in/retry.v1@v1.0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + gopkg.in/retry.v1@v1.0.3 + + + +
    • +
    + +
    + +
    + +

    LGPL-3.0 license

    + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/internal/encoding/json +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/protobuf/internal/encoding/json@v1.31.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + google.golang.org/protobuf/internal/encoding/json@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Stack-based Buffer Overflow

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Stack-based Buffer Overflow when processing input that uses pathologically deep nesting.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Allocation of Resources Without Limits or Throttling

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/helm/v3 /usr/local/bin/helm +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + helm.sh/helm/v3@* and golang.org/x/net/http2@v0.8.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + helm.sh/helm/v3@* + + golang.org/x/net/http2@v0.8.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

    +

    Affected versions of this package are vulnerable to Allocation of Resources Without Limits or Throttling when MaxConcurrentStreams handler goroutines running. A a handler is started until one of the existing handlers exits.

    +

    Note:

    +

    This issue is related to CVE-2023-44487

    +

    Remediation

    +

    Upgrade golang.org/x/net/http2 to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Authentication Bypass by Capture-replay

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/crypto/ssh +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and golang.org/x/crypto/ssh@v0.16.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + golang.org/x/crypto/ssh@v0.16.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/crypto/ssh is a SSH client and server

    +

    Affected versions of this package are vulnerable to Authentication Bypass by Capture-replay during the establishment of the secure channel. An attacker can manipulate handshake sequence numbers to delete messages sent immediately after the channel is established.

    +

    Note:

    +
      +
    1. Sequence numbers are only validated once the channel is established and arbitrary messages are allowed during the handshake, allowing them to manipulate the sequence numbers.

      +
    2. +
    3. The potential consequences of the general Terrapin attack are dependent on the messages exchanged after the handshake concludes. If you are using a custom SSH service and do not resort to the authentication protocol, you should check that dropping the first few messages of a connection does not yield security risks.

      +
    4. +
    +

    Impact:

    +

    While cryptographically novel, there is no discernable impact on the integrity of SSH traffic beyond giving the attacker the ability to delete the message that enables some features related to keystroke timing obfuscation. To successfully carry out the exploitation, the connection needs to be protected using either the ChaCha20-Poly1305 or CBC with Encrypt-then-MAC encryption methods. The attacker must also be able to intercept and modify the connection's traffic.

    +

    Workaround

    +

    Temporarily disable the affected chacha20-poly1305@openssh.com encryption and *-etm@openssh.com MAC algorithms in the affected configuration, and use unaffected algorithms like AES-GCM instead.

    +

    Remediation

    +

    Upgrade golang.org/x/crypto/ssh to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Information Exposure

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gnutls28/libgnutls30 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + apt@2.4.11 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + openldap/libldap-2.5-0@2.5.17+dfsg-0ubuntu0.22.04.1 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + rtmpdump/librtmp1@2.4+20151223.gitfa8646d.1-2build4 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream gnutls28 package and not the gnutls28 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A flaw was found in GnuTLS. The Minerva attack is a cryptographic vulnerability that exploits deterministic behavior in systems like GnuTLS, leading to side-channel leaks. In specific scenarios, such as when using the GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE flag, it can result in a noticeable step in nonce size from 513 to 512 bits, exposing a potential timing side-channel.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 gnutls28.

    +

    References

    + + +
    + + + +
    +
    +

    Uncaught Exception

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gnutls28/libgnutls30 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + apt@2.4.11 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + openldap/libldap-2.5-0@2.5.17+dfsg-0ubuntu0.22.04.1 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + rtmpdump/librtmp1@2.4+20151223.gitfa8646d.1-2build4 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream gnutls28 package and not the gnutls28 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A flaw has been discovered in GnuTLS where an application crash can be induced when attempting to verify a specially crafted .pem bundle using the "certtool --verify-chain" command.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 gnutls28.

    +

    References

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/r3labs/diff +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/r3labs/diff@v1.1.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/r3labs/diff@v1.1.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-version +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-version@v1.2.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/hashicorp/go-version@v1.2.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-retryablehttp +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-retryablehttp@v0.7.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/hashicorp/go-retryablehttp@v0.7.4 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/helm/v3 /usr/local/bin/helm +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-multierror +
    • + +
    • Introduced through: + + helm.sh/helm/v3@* and github.com/hashicorp/go-multierror@v1.1.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + helm.sh/helm/v3@* + + github.com/hashicorp/go-multierror@v1.1.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-cleanhttp +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-cleanhttp@v0.5.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/hashicorp/go-cleanhttp@v0.5.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/gosimple/slug +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/gosimple/slug@v1.13.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/gosimple/slug@v1.13.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    Improper Handling of Highly Compressed Data (Data Amplification)

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-jose/go-jose/v3 +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/go-jose/go-jose/v3@v3.0.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/go-jose/go-jose/v3@v3.0.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Improper Handling of Highly Compressed Data (Data Amplification). An attacker could send a JWE containing compressed data that, when decompressed by Decrypt or DecryptMulti, would use large amounts of memory and CPU.

    +

    Remediation

    +

    Upgrade github.com/go-jose/go-jose/v3 to version 3.0.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + bash +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and bash@5.1-6ubuntu1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + bash@5.1-6ubuntu1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream bash package and not the bash package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A flaw was found in the bash package, where a heap-buffer overflow can occur in valid parameter_transform. This issue may lead to memory problems.

    +

    Remediation

    +

    Upgrade Ubuntu:22.04 bash to version 5.1-6ubuntu1.1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-7008

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + systemd/libsystemd0 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and systemd/libsystemd0@249.11-0ubuntu3.12 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + apt@2.4.11 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + procps/libprocps8@2:3.3.17-6ubuntu2.1 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + util-linux@2.37.2-4ubuntu3 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + util-linux/bsdutils@1:2.37.2-4ubuntu3 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + systemd/libudev1@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + libfido2/libfido2-1@1.10.0-1 + + systemd/libudev1@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + util-linux@2.37.2-4ubuntu3 + + systemd/libudev1@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + systemd/libudev1@249.11-0ubuntu3.12 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream systemd package and not the systemd package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A vulnerability was found in systemd-resolved. This issue may allow systemd-resolved to accept records of DNSSEC-signed domains even when they have no signature, allowing man-in-the-middles (or the upstream DNS resolver) to manipulate records.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 systemd.

    +

    References

    + + +
    + + + +
    +
    +

    Arbitrary Code Injection

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + shadow/passwd +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and shadow/passwd@1:4.8.1-2ubuntu2.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + shadow/login@1:4.8.1-2ubuntu2.2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream shadow package and not the shadow package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    In Shadow 4.13, it is possible to inject control characters into fields provided to the SUID program chfn (change finger). Although it is not possible to exploit this directly (e.g., adding a new user fails because \n is in the block list), it is possible to misrepresent the /etc/passwd file when viewed. Use of \r manipulations and Unicode characters to work around blocking of the : character make it possible to give the impression that a new user has been added. In other words, an adversary may be able to convince a system administrator to take the system offline (an indirect, social-engineered denial of service) by demonstrating that "cat /etc/passwd" shows a rogue user account.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 shadow.

    +

    References

    + + +
    + + + +
    +
    +

    Uncontrolled Recursion

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + pcre3/libpcre3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + grep@3.7-1build1 + + pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream pcre3 package and not the pcre3 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    In PCRE 8.41, the OP_KETRMAX feature in the match function in pcre_exec.c allows stack exhaustion (uncontrolled recursion) when processing a crafted regular expression.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 pcre3.

    +

    References

    + + +
    + + + +
    +
    +

    Release of Invalid Pointer or Reference

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + patch +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and patch@2.7.6-7build2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + patch@2.7.6-7build2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream patch package and not the patch package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    An Invalid Pointer vulnerability exists in GNU patch 2.7 via the another_hunk function, which causes a Denial of Service.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 patch.

    +

    References

    + + +
    + + + +
    +
    +

    Double Free

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + patch +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and patch@2.7.6-7build2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + patch@2.7.6-7build2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream patch package and not the patch package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A double free exists in the another_hunk function in pch.c in GNU patch through 2.7.6.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 patch.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-50495

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + ncurses/libtinfo6 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and ncurses/libtinfo6@6.3-2ubuntu0.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + bash@5.1-6ubuntu1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + less@590-1ubuntu0.22.04.2 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + libedit/libedit2@3.1-20210910-1build1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/ncurses-bin@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + util-linux@2.37.2-4ubuntu3 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + readline/libreadline8@8.1.2-1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + pinentry/pinentry-curses@1.1.1-1build2 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + pinentry/pinentry-curses@1.1.1-1build2 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/ncurses-base@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/ncurses-bin@6.3-2ubuntu0.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream ncurses package and not the ncurses package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    NCurse v6.4-20230418 was discovered to contain a segmentation fault via the component _nc_wrap_entry().

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 ncurses.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-45918

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + ncurses/libtinfo6 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and ncurses/libtinfo6@6.3-2ubuntu0.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + bash@5.1-6ubuntu1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + less@590-1ubuntu0.22.04.2 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + libedit/libedit2@3.1-20210910-1build1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/ncurses-bin@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + util-linux@2.37.2-4ubuntu3 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + readline/libreadline8@8.1.2-1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + pinentry/pinentry-curses@1.1.1-1build2 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + pinentry/pinentry-curses@1.1.1-1build2 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/ncurses-base@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + ncurses/ncurses-bin@6.3-2ubuntu0.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream ncurses package and not the ncurses package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    ncurses 6.4-20230610 has a NULL pointer dereference in tgetstr in tinfo/lib_termcap.c.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 ncurses.

    +

    References

    + + +
    + + + +
    +
    +

    Resource Exhaustion

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + libzstd/libzstd1 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and libzstd/libzstd1@1.4.8+dfsg-3build1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + libzstd/libzstd1@1.4.8+dfsg-3build1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream libzstd package and not the libzstd package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A vulnerability was found in zstd v1.4.10, where an attacker can supply empty string as an argument to the command line tool to cause buffer overrun.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 libzstd.

    +

    References

    + + +
    + + + +
    +
    +

    Integer Overflow or Wraparound

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + krb5/libkrb5support0@1.19.2-2ubuntu0.3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    An issue was discovered in MIT Kerberos 5 (aka krb5) through 1.16. There is a variable "dbentry->n_key_data" in kadmin/dbutil/dump.c that can store 16-bit data but unknowingly the developer has assigned a "u4" variable to it, which is for 32-bit data. An attacker can use this vulnerability to affect other artifacts of the database as we know that a Kerberos database dump file contains trusted data.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gnupg2/gpgv +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and gnupg2/gpgv@2.2.27-3ubuntu2.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + apt@2.4.11 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream gnupg2 package and not the gnupg2 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    GnuPG can be made to spin on a relatively small input by (for example) crafting a public key with thousands of signatures attached, compressed down to just a few KB.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 gnupg2.

    +

    References

    + + +
    + + + +
    +
    +

    Allocation of Resources Without Limits or Throttling

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + glibc/libc-bin +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and glibc/libc-bin@2.35-0ubuntu3.6 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + glibc/libc-bin@2.35-0ubuntu3.6 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + glibc/libc6@2.35-0ubuntu3.6 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream glibc package and not the glibc package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    sha256crypt and sha512crypt through 0.6 allow attackers to cause a denial of service (CPU consumption) because the algorithm's runtime is proportional to the square of the length of the password.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 glibc.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Input Validation

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + git/git-man +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.8.13, git@1:2.34.1-1ubuntu1.10 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + git/git-man@1:2.34.1-1ubuntu1.10 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git@1:2.34.1-1ubuntu1.10 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + git-lfs@3.0.2-1ubuntu0.2 + + git@1:2.34.1-1ubuntu1.10 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream git package and not the git package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    GIT version 2.15.1 and earlier contains a Input Validation Error vulnerability in Client that can result in problems including messing up terminal configuration to RCE. This attack appear to be exploitable via The user must interact with a malicious git server, (or have their traffic modified in a MITM attack).

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 git.

    +

    References

    + + +
    + + + +
    +
    +

    Uncontrolled Recursion

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gcc-12/libstdc++6 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + apt@2.4.11 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gcc-12/gcc-12-base@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + gcc-12/libgcc-s1@12.3.0-1ubuntu1~22.04 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream gcc-12 package and not the gcc-12 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    libiberty/rust-demangle.c in GNU GCC 11.2 allows stack consumption in demangle_const, as demonstrated by nm-new.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 gcc-12.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Input Validation

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.8.13/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + coreutils +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.8.13 and coreutils@8.32-4.1ubuntu1.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.8.13 + + coreutils@8.32-4.1ubuntu1.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream coreutils package and not the coreutils package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 coreutils.

    +

    References

    + + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.8.13/redis_7.0.11-alpine.html b/docs/snyk/v2.8.13/redis_7.0.11-alpine.html new file mode 100644 index 0000000000000..9df9ec7f93123 --- /dev/null +++ b/docs/snyk/v2.8.13/redis_7.0.11-alpine.html @@ -0,0 +1,2032 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    March 24th 2024, 12:20:22 am (UTC+00:00)

    +
    +
    + Scanned the following path: +
      +
    • redis:7.0.11-alpine (apk)
    • +
    +
    + +
    +
    9 known vulnerabilities
    +
    77 vulnerable dependency paths
    +
    18 dependencies
    +
    +
    +
    +
    +
    + + + + + + + +
    Project docker-image|redis
    Path redis:7.0.11-alpine
    Package Manager apk
    +
    +
    +
    +
    +

    Out-of-bounds Write

    +
    + +
    + critical severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + busybox/busybox +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and busybox/busybox@1.36.1-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/busybox@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + alpine-baselayout/alpine-baselayout@3.4.3-r1 + + busybox/busybox-binsh@1.36.1-r0 + + busybox/busybox@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/busybox-binsh@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + alpine-baselayout/alpine-baselayout@3.4.3-r1 + + busybox/busybox-binsh@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream busybox package and not the busybox package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    There is a stack overflow vulnerability in ash.c:6030 in busybox before 1.35. In the environment of Internet of Vehicles, this vulnerability can be executed from command to arbitrary code execution.

    +

    Remediation

    +

    Upgrade Alpine:3.18 busybox to version 1.36.1-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-5363

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: A bug has been identified in the processing of key and + initialisation vector (IV) lengths. This can lead to potential truncation + or overruns during the initialisation of some symmetric ciphers.

    +

    Impact summary: A truncation in the IV can result in non-uniqueness, + which could result in loss of confidentiality for some cipher modes.

    +

    When calling EVP_EncryptInit_ex2(), EVP_DecryptInit_ex2() or + EVP_CipherInit_ex2() the provided OSSL_PARAM array is processed after + the key and IV have been established. Any alterations to the key length, + via the "keylen" parameter or the IV length, via the "ivlen" parameter, + within the OSSL_PARAM array will not take effect as intended, potentially + causing truncation or overreading of these values. The following ciphers + and cipher modes are impacted: RC2, RC4, RC5, CCM, GCM and OCB.

    +

    For the CCM, GCM and OCB cipher modes, truncation of the IV can result in + loss of confidentiality. For example, when following NIST's SP 800-38D + section 8.2.1 guidance for constructing a deterministic IV for AES in + GCM mode, truncation of the counter portion could lead to IV reuse.

    +

    Both truncations and overruns of the key and overruns of the IV will + produce incorrect results and could, in some cases, trigger a memory + exception. However, these issues are not currently assessed as security + critical.

    +

    Changing the key and/or IV lengths is not considered to be a common operation + and the vulnerable API was recently introduced. Furthermore it is likely that + application developers will have spotted this problem during testing since + decryption would fail unless both peers in the communication were similarly + vulnerable. For these reasons we expect the probability of an application being + vulnerable to this to be quite low. However if an application is vulnerable then + this issue is considered very serious. For these reasons we have assessed this + issue as Moderate severity overall.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this because + the issue lies outside of the FIPS provider boundary.

    +

    OpenSSL 3.1 and 3.0 are vulnerable to this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Authentication

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The AES-SIV cipher implementation contains a bug that causes + it to ignore empty associated data entries which are unauthenticated as + a consequence.

    +

    Impact summary: Applications that use the AES-SIV algorithm and want to + authenticate empty data entries as associated data can be mislead by removing + adding or reordering such empty entries as these are ignored by the OpenSSL + implementation. We are currently unaware of any such applications.

    +

    The AES-SIV algorithm allows for authentication of multiple associated + data entries along with the encryption. To authenticate empty data the + application has to call EVP_EncryptUpdate() (or EVP_CipherUpdate()) with + NULL pointer as the output buffer and 0 as the input buffer length. + The AES-SIV implementation in OpenSSL just returns success for such a call + instead of performing the associated data authentication operation. + The empty data thus will not be authenticated.

    +

    As this issue does not affect non-empty associated data authentication and + we expect it to be rare for an application to use empty associated data + entries this is qualified as Low severity issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.1-r2 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Inefficient Regular Expression Complexity

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Checking excessively long DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_check(), DH_check_ex() + or EVP_PKEY_param_check() to check a DH key or DH parameters may experience long + delays. Where the key or parameters that are being checked have been obtained + from an untrusted source this may lead to a Denial of Service.

    +

    The function DH_check() performs various checks on DH parameters. One of those + checks confirms that the modulus ('p' parameter) is not too large. Trying to use + a very large modulus is slow and OpenSSL will not normally use a modulus which + is over 10,000 bits in length.

    +

    However the DH_check() function checks numerous aspects of the key or parameters + that have been supplied. Some of those checks use the supplied modulus value + even if it has already been found to be too large.

    +

    An application that calls DH_check() and supplies a key or parameters obtained + from an untrusted source could be vulernable to a Denial of Service attack.

    +

    The function DH_check() is itself called by a number of other OpenSSL functions. + An application calling any of those other functions may similarly be affected. + The other functions affected by this are DH_check_ex() and + EVP_PKEY_param_check().

    +

    Also vulnerable are the OpenSSL dhparam and pkeyparam command line applications + when using the '-check' option.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue. + The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.1-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Excessive Iteration

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Checking excessively long DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_check(), DH_check_ex() + or EVP_PKEY_param_check() to check a DH key or DH parameters may experience long + delays. Where the key or parameters that are being checked have been obtained + from an untrusted source this may lead to a Denial of Service.

    +

    The function DH_check() performs various checks on DH parameters. After fixing + CVE-2023-3446 it was discovered that a large q parameter value can also trigger + an overly long computation during some of these checks. A correct q value, + if present, cannot be larger than the modulus p parameter, thus it is + unnecessary to perform these checks if q is larger than p.

    +

    An application that calls DH_check() and supplies a key or parameters obtained + from an untrusted source could be vulnerable to a Denial of Service attack.

    +

    The function DH_check() is itself called by a number of other OpenSSL functions. + An application calling any of those other functions may similarly be affected. + The other functions affected by this are DH_check_ex() and + EVP_PKEY_param_check().

    +

    Also vulnerable are the OpenSSL dhparam and pkeyparam command line applications + when using the "-check" option.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.2-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Check for Unusual or Exceptional Conditions

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Generating excessively long X9.42 DH keys or checking + excessively long X9.42 DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_generate_key() to + generate an X9.42 DH key may experience long delays. Likewise, applications + that use DH_check_pub_key(), DH_check_pub_key_ex() or EVP_PKEY_public_check() + to check an X9.42 DH key or X9.42 DH parameters may experience long delays. + Where the key or parameters that are being checked have been obtained from + an untrusted source this may lead to a Denial of Service.

    +

    While DH_check() performs all the necessary checks (as of CVE-2023-3817), + DH_check_pub_key() doesn't make any of these checks, and is therefore + vulnerable for excessively large P and Q parameters.

    +

    Likewise, while DH_generate_key() performs a check for an excessively large + P, it doesn't check for an excessively large Q.

    +

    An application that calls DH_generate_key() or DH_check_pub_key() and + supplies a key or parameters obtained from an untrusted source could be + vulnerable to a Denial of Service attack.

    +

    DH_generate_key() and DH_check_pub_key() are also called by a number of + other OpenSSL functions. An application calling any of those other + functions may similarly be affected. The other functions affected by this + are DH_check_pub_key_ex(), EVP_PKEY_public_check(), and EVP_PKEY_generate().

    +

    Also vulnerable are the OpenSSL pkey command line application when using the + "-pubcheck" option, as well as the OpenSSL genpkey command line application.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

    +

    Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

    +

    The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

    +

    The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

    +

    The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-0727

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

    +

    Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

    +

    A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

    +

    OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

    +

    We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

    +

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r5 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-6237

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    This vulnerability has not been analyzed by NVD yet.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r4 or higher.

    + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.6.0-rc4/argocd-iac-install.html b/docs/snyk/v2.9.9/argocd-iac-install.html similarity index 73% rename from docs/snyk/v2.6.0-rc4/argocd-iac-install.html rename to docs/snyk/v2.9.9/argocd-iac-install.html index c40c5f73ed37f..e25fc886459cb 100644 --- a/docs/snyk/v2.6.0-rc4/argocd-iac-install.html +++ b/docs/snyk/v2.9.9/argocd-iac-install.html @@ -456,7 +456,7 @@

    Snyk test report

    -

    January 22nd 2023, 12:19:28 am

    +

    March 24th 2024, 12:19:27 am (UTC+00:00)

    Scanned the following path: @@ -466,7 +466,7 @@

    Snyk test report

    -
    32 total issues
    +
    38 total issues
    @@ -483,7 +483,7 @@

    Snyk test report

    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -494,7 +494,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -507,29 +507,29 @@

      Role with dangerous permissions

    • - Line number: 15177 + Line number: 20316

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -540,7 +540,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -553,29 +553,29 @@

      Role with dangerous permissions

    • - Line number: 15254 + Line number: 20401

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -586,7 +586,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -599,29 +599,29 @@

      Role with dangerous permissions

    • - Line number: 15282 + Line number: 20429

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -632,42 +632,42 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: [DocId: 13] - rules[3] + rules[1] resources
    • - Line number: 15326 + Line number: 20459

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -678,42 +678,42 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: [DocId: 13] - rules[1] + rules[3] resources
    • - Line number: 15308 + Line number: 20477

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -724,7 +724,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -737,24 +737,24 @@

      Role with dangerous permissions

    • - Line number: 15342 + Line number: 20493

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    @@ -770,11 +770,11 @@

    Container could be running with outdated image

    • - Public ID: SNYK-CC-K8S-42 + Public ID: SNYK-CC-K8S-42
    • Introduced through: - [DocId: 46] + [DocId: 45] spec @@ -789,7 +789,7 @@

      Container could be running with outdated image

    • - Line number: 16346 + Line number: 21633
    @@ -806,7 +806,7 @@

    Remediation

    @@ -822,11 +822,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 42] + [DocId: 41] input @@ -847,7 +847,7 @@

      Container has no CPU limit

    • - Line number: 15809 + Line number: 20978
    @@ -864,7 +864,7 @@

    Remediation

    @@ -880,11 +880,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 43] + [DocId: 42] input @@ -905,7 +905,7 @@

      Container has no CPU limit

    • - Line number: 15982 + Line number: 21223
    @@ -922,7 +922,7 @@

    Remediation

    @@ -938,11 +938,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 43] + [DocId: 42] input @@ -963,7 +963,7 @@

      Container has no CPU limit

    • - Line number: 15948 + Line number: 21189
    @@ -980,7 +980,7 @@

    Remediation

    @@ -996,11 +996,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 44] + [DocId: 43] input @@ -1021,7 +1021,7 @@

      Container has no CPU limit

    • - Line number: 16038 + Line number: 21283
    @@ -1038,7 +1038,7 @@

    Remediation

    @@ -1054,11 +1054,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 45] + [DocId: 44] input @@ -1079,7 +1079,7 @@

      Container has no CPU limit

    • - Line number: 16112 + Line number: 21376
    @@ -1096,7 +1096,7 @@

    Remediation

    @@ -1112,11 +1112,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 46] + [DocId: 45] input @@ -1137,7 +1137,7 @@

      Container has no CPU limit

    • - Line number: 16346 + Line number: 21633
    @@ -1154,7 +1154,7 @@

    Remediation

    @@ -1170,11 +1170,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 46] + [DocId: 45] input @@ -1195,7 +1195,7 @@

      Container has no CPU limit

    • - Line number: 16168 + Line number: 21433
    @@ -1212,7 +1212,7 @@

    Remediation

    @@ -1228,11 +1228,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 47] + [DocId: 46] input @@ -1253,7 +1253,7 @@

      Container has no CPU limit

    • - Line number: 16431 + Line number: 21718
    @@ -1270,7 +1270,7 @@

    Remediation

    @@ -1286,11 +1286,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 48] + [DocId: 47] input @@ -1311,7 +1311,7 @@

      Container has no CPU limit

    • - Line number: 16735 + Line number: 22040
    @@ -1328,7 +1328,7 @@

    Remediation

    @@ -1344,11 +1344,11 @@

    Container is running with multiple open ports

    • - Public ID: SNYK-CC-K8S-36 + Public ID: SNYK-CC-K8S-36
    • Introduced through: - [DocId: 43] + [DocId: 42] spec @@ -1363,7 +1363,7 @@

      Container is running with multiple open ports

    • - Line number: 15962 + Line number: 21203
    @@ -1380,12 +1380,12 @@

    Remediation

    -

    Container is running with writable root filesystem

    +

    Container is running without liveness probe

    @@ -1396,13 +1396,11 @@

    Container is running with writable root filesystem

    • - Public ID: SNYK-CC-K8S-8 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 45] - - input + [DocId: 41] spec @@ -1410,33 +1408,31 @@

      Container is running with writable root filesystem

      spec - containers[redis] - - securityContext + containers[argocd-applicationset-controller] - readOnlyRootFilesystem + livenessProbe
    • - Line number: 16122 + Line number: 20978

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    +

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    +

    Add `livenessProbe` attribute


    @@ -1452,7 +1448,7 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: @@ -1464,14 +1460,14 @@

      Container is running without liveness probe

      spec - containers[argocd-applicationset-controller] + containers[dex] livenessProbe
    • - Line number: 15809 + Line number: 21189
    @@ -1488,7 +1484,7 @@

    Remediation

    @@ -1504,11 +1500,11 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 43] + [DocId: 44] spec @@ -1516,14 +1512,14 @@

      Container is running without liveness probe

      spec - containers[dex] + containers[redis] livenessProbe
    • - Line number: 15948 + Line number: 21376
    @@ -1540,12 +1536,12 @@

    Remediation

    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1556,11 +1552,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 43] + [DocId: 41] + + input spec @@ -1568,36 +1566,40 @@

      Container is running without liveness probe

      spec - initContainers[copyutil] + containers[argocd-applicationset-controller] - livenessProbe + resources + + limits + + memory
    • - Line number: 15982 + Line number: 20978

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1608,11 +1610,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 45] + [DocId: 42] + + input spec @@ -1620,36 +1624,40 @@

      Container is running without liveness probe

      spec - containers[redis] + containers[dex] - livenessProbe + resources + + limits + + memory
    • - Line number: 16112 + Line number: 21189

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1660,11 +1668,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 46] + [DocId: 42] + + input spec @@ -1674,29 +1684,33 @@

      Container is running without liveness probe

      initContainers[copyutil] - livenessProbe + resources + + limits + + memory
    • - Line number: 16346 + Line number: 21223

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    @@ -1712,11 +1726,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 42] + [DocId: 43] input @@ -1726,7 +1740,7 @@

      Container is running without memory limit

      spec - containers[argocd-applicationset-controller] + containers[argocd-notifications-controller] resources @@ -1737,7 +1751,7 @@

      Container is running without memory limit

    • - Line number: 15809 + Line number: 21283
    @@ -1754,7 +1768,7 @@

    Remediation

    @@ -1770,11 +1784,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 43] + [DocId: 44] input @@ -1784,7 +1798,7 @@

      Container is running without memory limit

      spec - containers[dex] + containers[redis] resources @@ -1795,7 +1809,7 @@

      Container is running without memory limit

    • - Line number: 15948 + Line number: 21376
    @@ -1812,7 +1826,7 @@

    Remediation

    @@ -1828,11 +1842,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 43] + [DocId: 45] input @@ -1853,7 +1867,7 @@

      Container is running without memory limit

    • - Line number: 15982 + Line number: 21633
    @@ -1870,7 +1884,7 @@

    Remediation

    @@ -1886,11 +1900,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 44] + [DocId: 45] input @@ -1900,7 +1914,7 @@

      Container is running without memory limit

      spec - containers[argocd-notifications-controller] + containers[argocd-repo-server] resources @@ -1911,7 +1925,7 @@

      Container is running without memory limit

    • - Line number: 16038 + Line number: 21433
    @@ -1928,7 +1942,7 @@

    Remediation

    @@ -1944,11 +1958,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 45] + [DocId: 46] input @@ -1958,7 +1972,7 @@

      Container is running without memory limit

      spec - containers[redis] + containers[argocd-server] resources @@ -1969,7 +1983,7 @@

      Container is running without memory limit

    • - Line number: 16112 + Line number: 21718
    @@ -1986,7 +2000,7 @@

    Remediation

    @@ -2002,11 +2016,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 46] + [DocId: 47] input @@ -2016,7 +2030,7 @@

      Container is running without memory limit

      spec - initContainers[copyutil] + containers[argocd-application-controller] resources @@ -2027,7 +2041,7 @@

      Container is running without memory limit

    • - Line number: 16346 + Line number: 22040
    @@ -2044,12 +2058,12 @@

    Remediation

    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2060,11 +2074,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: - [DocId: 46] + [DocId: 41] input @@ -2074,40 +2088,94 @@

      Container is running without memory limit

      spec - containers[argocd-repo-server] + containers[argocd-applicationset-controller] - resources + securityContext - limits + runAsUser + +
    • + +
    • + Line number: 21113 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 42] - memory + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser
    • - Line number: 16168 + Line number: 21231

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2118,11 +2186,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: - [DocId: 47] + [DocId: 42] input @@ -2132,40 +2200,94 @@

      Container is running without memory limit

      spec - containers[argocd-server] + containers[dex] - resources + securityContext - limits + runAsUser + +
    • + +
    • + Line number: 21206 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 43] - memory + input + + spec + + template + + spec + + containers[argocd-notifications-controller] + + securityContext + + runAsUser
    • - Line number: 16431 + Line number: 21310

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2176,11 +2298,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: - [DocId: 48] + [DocId: 44] input @@ -2190,35 +2312,257 @@

      Container is running without memory limit

      spec - containers[argocd-application-controller] + containers[redis] - resources + securityContext - limits + runAsUser + +
    • + +
    • + Line number: 21386 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 45] - memory + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser
    • - Line number: 16735 + Line number: 21640

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 45] + + input + + spec + + template + + spec + + containers[argocd-repo-server] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 21606 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 46] + + input + + spec + + template + + spec + + containers[argocd-server] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 21950 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 47] + + input + + spec + + template + + spec + + containers[argocd-application-controller] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 22188 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    diff --git a/docs/snyk/v2.4.19/argocd-iac-namespace-install.html b/docs/snyk/v2.9.9/argocd-iac-namespace-install.html similarity index 73% rename from docs/snyk/v2.4.19/argocd-iac-namespace-install.html rename to docs/snyk/v2.9.9/argocd-iac-namespace-install.html index 389075c733ec2..5fd494538c87c 100644 --- a/docs/snyk/v2.4.19/argocd-iac-namespace-install.html +++ b/docs/snyk/v2.9.9/argocd-iac-namespace-install.html @@ -456,7 +456,7 @@

    Snyk test report

    -

    January 22nd 2023, 12:23:01 am

    +

    March 24th 2024, 12:19:35 am (UTC+00:00)

    Scanned the following path: @@ -466,7 +466,7 @@

    Snyk test report

    -
    32 total issues
    +
    38 total issues
    @@ -483,7 +483,7 @@

    Snyk test report

    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -494,7 +494,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -507,29 +507,29 @@

      Role with dangerous permissions

    • - Line number: 73 + Line number: 77

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -540,7 +540,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -553,29 +553,29 @@

      Role with dangerous permissions

    • - Line number: 150 + Line number: 162

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -586,7 +586,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -599,29 +599,29 @@

      Role with dangerous permissions

    • - Line number: 178 + Line number: 190

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -632,42 +632,42 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: [DocId: 10] - rules[3] + rules[1] resources
    • - Line number: 222 + Line number: 220

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -678,42 +678,42 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: [DocId: 10] - rules[1] + rules[3] resources
    • - Line number: 204 + Line number: 238

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    -

    Role with dangerous permissions

    +

    Role or ClusterRole with dangerous permissions

    @@ -724,7 +724,7 @@

    Role with dangerous permissions

    • - Public ID: SNYK-CC-K8S-47 + Public ID: SNYK-CC-K8S-47
    • Introduced through: @@ -737,24 +737,24 @@

      Role with dangerous permissions

    • - Line number: 238 + Line number: 254

    Impact

    -

    Using this role grants dangerous permissions

    +

    Using this role grants dangerous permissions. For a ClusterRole this would be considered high severity.

    Remediation

    -

    Consider removing this permissions

    +

    Consider removing these permissions


    @@ -770,11 +770,11 @@

    Container could be running with outdated image

    • - Public ID: SNYK-CC-K8S-42 + Public ID: SNYK-CC-K8S-42
    • Introduced through: - [DocId: 39] + [DocId: 38] spec @@ -789,7 +789,7 @@

      Container could be running with outdated image

    • - Line number: 1025 + Line number: 1288
    @@ -806,7 +806,7 @@

    Remediation

    @@ -822,11 +822,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 35] + [DocId: 34] input @@ -847,7 +847,7 @@

      Container has no CPU limit

    • - Line number: 611 + Line number: 633
    @@ -864,7 +864,7 @@

    Remediation

    @@ -880,11 +880,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 36] + [DocId: 35] input @@ -905,7 +905,7 @@

      Container has no CPU limit

    • - Line number: 711 + Line number: 878
    @@ -922,7 +922,7 @@

    Remediation

    @@ -938,11 +938,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 36] + [DocId: 35] input @@ -963,7 +963,7 @@

      Container has no CPU limit

    • - Line number: 688 + Line number: 844
    @@ -980,7 +980,7 @@

    Remediation

    @@ -996,11 +996,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 37] + [DocId: 36] input @@ -1021,7 +1021,7 @@

      Container has no CPU limit

    • - Line number: 754 + Line number: 938
    @@ -1038,7 +1038,7 @@

    Remediation

    @@ -1054,11 +1054,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 38] + [DocId: 37] input @@ -1079,7 +1079,7 @@

      Container has no CPU limit

    • - Line number: 826 + Line number: 1031
    @@ -1096,7 +1096,7 @@

    Remediation

    @@ -1112,11 +1112,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 39] + [DocId: 38] input @@ -1137,7 +1137,7 @@

      Container has no CPU limit

    • - Line number: 1025 + Line number: 1288
    @@ -1154,7 +1154,7 @@

    Remediation

    @@ -1170,11 +1170,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 39] + [DocId: 38] input @@ -1195,7 +1195,7 @@

      Container has no CPU limit

    • - Line number: 880 + Line number: 1088
    @@ -1212,7 +1212,7 @@

    Remediation

    @@ -1228,11 +1228,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 40] + [DocId: 39] input @@ -1253,7 +1253,7 @@

      Container has no CPU limit

    • - Line number: 1108 + Line number: 1373
    @@ -1270,7 +1270,7 @@

    Remediation

    @@ -1286,11 +1286,11 @@

    Container has no CPU limit

    • - Public ID: SNYK-CC-K8S-5 + Public ID: SNYK-CC-K8S-5
    • Introduced through: - [DocId: 41] + [DocId: 40] input @@ -1311,7 +1311,7 @@

      Container has no CPU limit

    • - Line number: 1368 + Line number: 1695
    @@ -1328,7 +1328,7 @@

    Remediation

    @@ -1344,11 +1344,11 @@

    Container is running with multiple open ports

    • - Public ID: SNYK-CC-K8S-36 + Public ID: SNYK-CC-K8S-36
    • Introduced through: - [DocId: 36] + [DocId: 35] spec @@ -1363,7 +1363,7 @@

      Container is running with multiple open ports

    • - Line number: 695 + Line number: 858
    @@ -1380,12 +1380,12 @@

    Remediation

    -

    Container is running with writable root filesystem

    +

    Container is running without liveness probe

    @@ -1396,13 +1396,11 @@

    Container is running with writable root filesystem

    • - Public ID: SNYK-CC-K8S-8 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 38] - - input + [DocId: 34] spec @@ -1410,33 +1408,31 @@

      Container is running with writable root filesystem

      spec - containers[redis] - - securityContext + containers[argocd-applicationset-controller] - readOnlyRootFilesystem + livenessProbe
    • - Line number: 836 + Line number: 633

    Impact

    -

    Compromised process could abuse writable root filesystem to elevate privileges

    +

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    Remediation

    -

    Set `securityContext.readOnlyRootFilesystem` to `true`

    +

    Add `livenessProbe` attribute


    @@ -1452,7 +1448,7 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: @@ -1464,14 +1460,14 @@

      Container is running without liveness probe

      spec - containers[argocd-applicationset-controller] + containers[dex] livenessProbe
    • - Line number: 611 + Line number: 844
    @@ -1488,7 +1484,7 @@

    Remediation

    @@ -1504,11 +1500,11 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-41
    • Introduced through: - [DocId: 36] + [DocId: 37] spec @@ -1516,14 +1512,14 @@

      Container is running without liveness probe

      spec - containers[dex] + containers[redis] livenessProbe
    • - Line number: 688 + Line number: 1031
    @@ -1540,12 +1536,12 @@

    Remediation

    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1556,11 +1552,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 36] + [DocId: 34] + + input spec @@ -1568,36 +1566,40 @@

      Container is running without liveness probe

      spec - initContainers[copyutil] + containers[argocd-applicationset-controller] - livenessProbe + resources + + limits + + memory
    • - Line number: 711 + Line number: 633

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1608,11 +1610,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 38] + [DocId: 35] + + input spec @@ -1620,36 +1624,40 @@

      Container is running without liveness probe

      spec - containers[redis] + containers[dex] - livenessProbe + resources + + limits + + memory
    • - Line number: 826 + Line number: 844

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    -

    Container is running without liveness probe

    +

    Container is running without memory limit

    @@ -1660,11 +1668,13 @@

    Container is running without liveness probe

    • - Public ID: SNYK-CC-K8S-41 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 39] + [DocId: 35] + + input spec @@ -1674,29 +1684,33 @@

      Container is running without liveness probe

      initContainers[copyutil] - livenessProbe + resources + + limits + + memory
    • - Line number: 1025 + Line number: 878

    Impact

    -

    Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods

    +

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    Remediation

    -

    Add `livenessProbe` attribute

    +

    Set `resources.limits.memory` value


    @@ -1712,11 +1726,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 35] + [DocId: 36] input @@ -1726,7 +1740,7 @@

      Container is running without memory limit

      spec - containers[argocd-applicationset-controller] + containers[argocd-notifications-controller] resources @@ -1737,7 +1751,7 @@

      Container is running without memory limit

    • - Line number: 611 + Line number: 938
    @@ -1754,7 +1768,7 @@

    Remediation

    @@ -1770,11 +1784,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 36] + [DocId: 37] input @@ -1784,7 +1798,7 @@

      Container is running without memory limit

      spec - containers[dex] + containers[redis] resources @@ -1795,7 +1809,7 @@

      Container is running without memory limit

    • - Line number: 688 + Line number: 1031
    @@ -1812,7 +1826,7 @@

    Remediation

    @@ -1828,11 +1842,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 36] + [DocId: 38] input @@ -1853,7 +1867,7 @@

      Container is running without memory limit

    • - Line number: 711 + Line number: 1288
    @@ -1870,7 +1884,7 @@

    Remediation

    @@ -1886,11 +1900,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 37] + [DocId: 38] input @@ -1900,7 +1914,7 @@

      Container is running without memory limit

      spec - containers[argocd-notifications-controller] + containers[argocd-repo-server] resources @@ -1911,7 +1925,7 @@

      Container is running without memory limit

    • - Line number: 754 + Line number: 1088
    @@ -1928,7 +1942,7 @@

    Remediation

    @@ -1944,11 +1958,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 38] + [DocId: 39] input @@ -1958,7 +1972,7 @@

      Container is running without memory limit

      spec - containers[redis] + containers[argocd-server] resources @@ -1969,7 +1983,7 @@

      Container is running without memory limit

    • - Line number: 826 + Line number: 1373
    @@ -1986,7 +2000,7 @@

    Remediation

    @@ -2002,11 +2016,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-4
    • Introduced through: - [DocId: 39] + [DocId: 40] input @@ -2016,7 +2030,7 @@

      Container is running without memory limit

      spec - initContainers[copyutil] + containers[argocd-application-controller] resources @@ -2027,7 +2041,7 @@

      Container is running without memory limit

    • - Line number: 1025 + Line number: 1695
    @@ -2044,12 +2058,12 @@

    Remediation

    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2060,11 +2074,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: - [DocId: 39] + [DocId: 34] input @@ -2074,40 +2088,94 @@

      Container is running without memory limit

      spec - containers[argocd-repo-server] + containers[argocd-applicationset-controller] - resources + securityContext - limits + runAsUser + +
    • + +
    • + Line number: 768 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 35] - memory + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser
    • - Line number: 880 + Line number: 886

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2118,11 +2186,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: - [DocId: 40] + [DocId: 35] input @@ -2132,40 +2200,94 @@

      Container is running without memory limit

      spec - containers[argocd-server] + containers[dex] - resources + securityContext - limits + runAsUser + +
    • + +
    • + Line number: 861 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 36] - memory + input + + spec + + template + + spec + + containers[argocd-notifications-controller] + + securityContext + + runAsUser
    • - Line number: 1108 + Line number: 965

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    -

    Container is running without memory limit

    +

    Container's or Pod's UID could clash with host's UID

    @@ -2176,11 +2298,11 @@

    Container is running without memory limit

    • - Public ID: SNYK-CC-K8S-4 + Public ID: SNYK-CC-K8S-11
    • Introduced through: - [DocId: 41] + [DocId: 37] input @@ -2190,35 +2312,257 @@

      Container is running without memory limit

      spec - containers[argocd-application-controller] + containers[redis] - resources + securityContext - limits + runAsUser + +
    • + +
    • + Line number: 1041 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 38] - memory + input + + spec + + template + + spec + + initContainers[copyutil] + + securityContext + + runAsUser
    • - Line number: 1368 + Line number: 1295

    Impact

    -

    Containers without memory limits are more likely to be terminated when the node runs out of memory

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    Remediation

    -

    Set `resources.limits.memory` value

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 38] + + input + + spec + + template + + spec + + containers[argocd-repo-server] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 1261 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 39] + + input + + spec + + template + + spec + + containers[argocd-server] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 1605 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence

    + + +
    +
    + + + +
    +
    +

    Container's or Pod's UID could clash with host's UID

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Public ID: SNYK-CC-K8S-11 +
    • + +
    • Introduced through: + [DocId: 40] + + input + + spec + + template + + spec + + containers[argocd-application-controller] + + securityContext + + runAsUser + +
    • + +
    • + Line number: 1843 +
    • +
    + +
    + +

    Impact

    +

    UID of the container processes could clash with host's UIDs and lead to unintentional authorization bypass

    + +

    Remediation

    +

    Set `securityContext.runAsUser` value to greater or equal than 10'000. SecurityContext can be set on both `pod` and `container` level. If both are set, then the container level takes precedence


    diff --git a/docs/snyk/v2.4.19/argocd-test.html b/docs/snyk/v2.9.9/argocd-test.html similarity index 61% rename from docs/snyk/v2.4.19/argocd-test.html rename to docs/snyk/v2.9.9/argocd-test.html index 4a113207d1bf2..c4894f56b168a 100644 --- a/docs/snyk/v2.4.19/argocd-test.html +++ b/docs/snyk/v2.9.9/argocd-test.html @@ -7,7 +7,7 @@ Snyk test report - + @@ -456,19 +456,20 @@

    Snyk test report

    -

    January 22nd 2023, 12:21:39 am

    +

    March 24th 2024, 12:17:43 am (UTC+00:00)

    Scanned the following paths:
      -
    • /argo-cd/argoproj/argo-cd/v2 (gomodules)
    • /argo-cd (yarn)
    • +
    • /argo-cd/argoproj/argo-cd/v2/go.mod (gomodules)
    • +
    • /argo-cd/ui/yarn.lock (yarn)
    -
    8 known vulnerabilities
    -
    128 vulnerable dependency paths
    -
    1656 dependencies
    +
    12 known vulnerabilities
    +
    133 vulnerable dependency paths
    +
    1917 dependencies
    @@ -476,206 +477,33 @@

    Snyk test report

    -
    -

    Server-side Request Forgery (SSRF)

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: npm -
    • -
    • - Vulnerable module: - - parse-url -
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, git-url-parse@11.6.0 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - git-url-parse@11.6.0 - - git-up@4.0.5 - - parse-url@6.0.5 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    parse-url is an An advanced url parser supporting git urls too.

    -

    Affected versions of this package are vulnerable to Server-side Request Forgery (SSRF) due to improper detection of protocol, resource, and pathname fields. Exploiting this vulnerability results in bypassing protocol verification.

    -

    PoC:

    -
    import parseUrl from "parse-url";
    -        import fetch from 'node-fetch';
    -        var parsed=parseUrl("http://nnnn@localhost:808:/?id=xss")
    -        if(parsed.resource=="localhost"){
    -        console.log("internal network access is blocked")
    -        }
    -        else{
    -           const response = await fetch('http://'+parsed.resource+parsed.pathname);
    -                console.log(response)
    -         }
    -        
    -

    Remediation

    -

    Upgrade parse-url to version 8.1.0 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Improper Input Validation

    +
    +

    Denial of Service (DoS)

    -
    - medium severity +
    + high severity

    • - Package Manager: npm + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod
    • - Vulnerable module: - - parse-url -
    • - -
    • Introduced through: - - - argo-cd-ui@1.0.0, git-url-parse@11.6.0 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
      -
    • - Introduced through: - argo-cd-ui@1.0.0 - - git-url-parse@11.6.0 - - git-up@4.0.5 - - parse-url@6.0.5 - - - -
    • -
    - -
    - -
    - -

    Overview

    -

    parse-url is an An advanced url parser supporting git urls too.

    -

    Affected versions of this package are vulnerable to Improper Input Validation due to incorrect parsing of URLs. This allows the attacker to craft a malformed URL which can lead to a phishing attack.

    -
    
    -        const parseUrl = require("parse-url");
    -        const Url = require("url");
    -        
    -        const express = require('express');
    -        const app = express();
    -        
    -        var url = "https://www.google.com:x@fakesite.com:x";
    -        parsed = parseUrl(url);
    -        console.log("[*]`parse-url` output: ")
    -        console.log(parsed);
    -        
    -        parsed2 = Url.parse(url);
    -        console.log("[*]`url` output: ")
    -        console.log(parsed2)
    -        
    -        app.get('/', (req, res) => {
    -            if (parsed.host == "www.google.com") {
    -                res.send("<a href=\'" + parsed2.href + "\'>CLICK ME!</a>")
    -            }
    -        })
    -        
    -        app.listen(8888,"0.0.0.0");
    -        
    -

    Remediation

    -

    Upgrade parse-url to version 8.1.0 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Regular Expression Denial of Service (ReDoS)

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: npm + Package Manager: golang
    • Vulnerable module: - minimatch + google.golang.org/grpc
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and google.golang.org/grpc@1.56.2 - argo-cd-ui@1.0.0, redoc@2.0.0-rc.64 and others
    @@ -687,141 +515,20 @@

    Detailed paths

    • Introduced through: - argo-cd-ui@1.0.0 - - redoc@2.0.0-rc.64 - - @redocly/openapi-core@1.0.0-beta.82 + github.com/argoproj/argo-cd/v2@0.0.0 - minimatch@3.0.4 + google.golang.org/grpc@1.56.2
    • -
    - -
    - -
    - -

    Overview

    -

    minimatch is a minimal matching utility.

    -

    Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) via the braceExpand function in minimatch.js.

    -

    Details

    -

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.

    -

    The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.

    -

    Let’s take the following regular expression as an example:

    -
    regex = /A(B|C+)+D/
    -        
    -

    This regular expression accomplishes the following:

    -
      -
    • A The string must start with the letter 'A'
    • -
    • (B|C+)+ The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the + matches one or more times). The + at the end of this section states that we can look for one or more matches of this section.
    • -
    • D Finally, we ensure this section of the string ends with a 'D'
    • -
    -

    The expression would match inputs such as ABBD, ABCCCCD, ABCBCCCD and ACCCCCD

    -

    It most cases, it doesn't take very long for a regex engine to find a match:

    -
    $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD")'
    -        0.04s user 0.01s system 95% cpu 0.052 total
    -        
    -        $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX")'
    -        1.79s user 0.02s system 99% cpu 1.812 total
    -        
    -

    The entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.

    -

    Most Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as catastrophic backtracking.

    -

    Let's look at how our expression runs into this problem, using a shorter string: "ACCCX". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:

    -
      -
    1. CCC
    2. -
    3. CC+C
    4. -
    5. C+CC
    6. -
    7. C+C+C.
    8. -
    -

    The engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use RegEx 101 debugger to see the engine has to take a total of 38 steps before it can determine the string doesn't match.

    -

    From there, the number of steps the engine must use to validate a string just continues to grow.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    StringNumber of C'sNumber of steps
    ACCCX338
    ACCCCX471
    ACCCCCX5136
    ACCCCCCCCCCCCCCX1465,553
    -

    By the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.

    -

    Remediation

    -

    Upgrade minimatch to version 3.0.5 or higher.

    -

    References

    - - -
    - - - -
    -
    -

    Denial of Service (DoS)

    -
    - -
    - medium severity -
    - -
    - -
      -
    • - Package Manager: golang -
    • -
    • - Vulnerable module: - - golang.org/x/net/http2 -
    • - -
    • Introduced through: - - - github.com/argoproj/argo-cd/v2@0.0.0, k8s.io/client-go/rest@0.23.1 and others -
    • -
    - -
    - - -

    Detailed paths

    - -
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -830,9 +537,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -841,9 +548,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -852,9 +559,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/soheilhy/cmux@0.1.5 + google.golang.org/grpc/health@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -863,11 +570,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/cache@0.23.1 - - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/reflection@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -876,11 +581,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -889,11 +592,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/dynamic@0.23.1 - - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -902,11 +603,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/transport/spdy@0.23.1 - - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -915,11 +614,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/pkg/kubeclientmetrics@#36c59d8fafe0 - - k8s.io/client-go/rest@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -928,11 +625,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/testing@0.23.1 - - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -941,11 +636,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes@0.23.1 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -954,11 +647,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/azure@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -967,11 +660,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/gcp@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/client-go/rest@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig@1.16.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -980,11 +673,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/oidc@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/client-go/rest@0.23.1 + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -993,11 +686,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/record@0.23.1 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -1006,11 +699,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/reflection@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -1019,11 +712,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/health@1.56.2 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -1032,13 +725,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - k8s.io/client-go/discovery@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -1047,13 +740,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/listers/core/v1@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 - k8s.io/client-go/tools/cache@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -1062,13 +755,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#567361917320 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/client-go/tools/cache@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -1077,13 +772,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/core/v1@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 + + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - k8s.io/client-go/tools/cache@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.11.3 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc@1.56.2 @@ -1092,43 +789,164 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 - k8s.io/client-go/tools/cache@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - golang.org/x/net/http2@#9d032be2e588 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 + + google.golang.org/grpc@1.56.2
    • +
    + +
    + +
    + +

    Overview

    +

    google.golang.org/grpc is a Go implementation of gRPC

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) in the implementation of the HTTP/2 protocol. An attacker can cause a denial of service (including via DDoS) by rapidly resetting many streams through request cancellation.

    +

    Remediation

    +

    Upgrade google.golang.org/grpc to version 1.56.3, 1.57.1, 1.58.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    LGPL-3.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + gopkg.in/retry.v1 +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/Azure/kubelogin/pkg/token@0.0.20 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/clientcmd@0.23.1 - - k8s.io/client-go/tools/auth@0.23.1 + github.com/Azure/kubelogin/pkg/token@0.0.20 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + gopkg.in/retry.v1@1.0.3
    • +
    + +
    + +
    + +

    LGPL-3.0 license

    + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/internal/encoding/json +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#567361917320 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/client-go/tools/cache@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1137,13 +955,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery/fake@0.23.1 + github.com/argoproj/pkg/grpc/http@#d56162821bd1 + + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/client-go/testing@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1152,13 +972,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/fake@0.23.1 + google.golang.org/grpc@1.56.2 + + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/testing@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1167,13 +989,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/remotecommand@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/client-go/transport/spdy@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.56.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1182,13 +1008,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 + + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1197,13 +1027,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 + + google.golang.org/grpc@1.56.2 + + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1212,13 +1046,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware@1.3.0 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc@1.56.2 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.56.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1227,13 +1065,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/auth@1.3.0 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + + google.golang.org/grpc@1.56.2 + + google.golang.org/grpc/internal/transport@1.56.2 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/pretty@1.56.2 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1242,13 +1084,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/retry@1.3.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 + + google.golang.org/grpc@1.56.2 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/transport@1.56.2 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1257,13 +1103,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 + + google.golang.org/grpc@1.56.2 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/transport@1.56.2 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1272,13 +1122,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/health/grpc_health_v1@1.45.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 + + google.golang.org/grpc@1.56.2 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/transport@1.56.2 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1287,28 +1141,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.3.0 - - google.golang.org/grpc@1.45.0 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - golang.org/x/net/http2@#9d032be2e588 - - - -
    • -
    • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + google.golang.org/grpc@1.56.2 - github.com/improbable-eng/grpc-web/go/grpcweb@#16092bd1d58a + google.golang.org/grpc/internal/transport@1.56.2 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/pretty@1.56.2 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1317,28 +1162,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.31.0 + google.golang.org/grpc/reflection@1.56.2 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.56.2 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc@1.56.2 - golang.org/x/net/http2@#9d032be2e588 - - - -
    • -
    • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.6.3 + google.golang.org/grpc/internal/transport@1.56.2 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/pretty@1.56.2 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/protobuf/encoding/protojson@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1347,15 +1183,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/cache@0.7.3 + google.golang.org/grpc/health@1.56.2 + + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 + + google.golang.org/protobuf/internal/encoding/json@1.31.0 @@ -1364,83 +1204,98 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync@0.7.3 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/client-go/discovery@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - k8s.io/client-go/rest@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - golang.org/x/net/http2@#9d032be2e588 - - - -
    • -
    • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 + google.golang.org/grpc@1.56.2 - github.com/argoproj/gitops-engine/pkg/utils/kube@0.7.3 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/protobuf/encoding/protojson@1.31.0 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/internal/encoding/json@1.31.0 -
    • -
    • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - github.com/argoproj/notifications-engine/pkg/cmd@#567361917320 - - k8s.io/client-go/tools/clientcmd@0.23.1 - - k8s.io/client-go/tools/auth@0.23.1 - - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 - - +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Stack-based Buffer Overflow

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + -
    • -
    • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - k8s.io/kubectl/pkg/util/term@0.23.1 - - k8s.io/client-go/tools/remotecommand@0.23.1 - - k8s.io/client-go/transport/spdy@0.23.1 - - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 - - + github.com/argoproj/argo-cd/v2@0.0.0, github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 and others +
    • +
    - +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/apimachinery/pkg/watch@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1449,15 +1304,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/api/rbac/v1@0.23.1 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/argoproj/pkg/grpc/http@#d56162821bd1 - k8s.io/apimachinery/pkg/watch@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1466,15 +1319,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1483,15 +1334,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/scheme@0.11.0 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/apimachinery/pkg/watch@0.23.1 + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.11.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1500,15 +1349,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/api/core/v1@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1517,15 +1366,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/errors@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1534,15 +1383,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/equality@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1551,15 +1400,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1568,15 +1417,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/dynamic@0.23.1 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1585,15 +1434,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/transport/spdy@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1602,15 +1451,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/pkg/kubeclientmetrics@#36c59d8fafe0 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1619,15 +1468,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/testing@0.23.1 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1636,15 +1485,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1653,15 +1502,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/azure@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/client-go/rest@0.23.1 + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - k8s.io/client-go/transport@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.11.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/protobuf/types/known/structpb@1.31.0 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1670,15 +1519,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/gcp@0.23.1 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 + + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1687,15 +1538,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/plugin/pkg/client/auth/oidc@0.23.1 + google.golang.org/grpc/reflection@1.56.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.56.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1704,15 +1557,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/reflection@1.45.0 + google.golang.org/grpc/health@1.56.2 + + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.45.0 + google.golang.org/grpc@1.56.2 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/transport@1.56.2 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1721,15 +1576,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - google.golang.org/grpc/health@1.45.0 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - google.golang.org/grpc/health/grpc_health_v1@1.45.0 + google.golang.org/grpc@1.56.2 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/transport@1.56.2 - google.golang.org/grpc/internal/transport@1.45.0 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1738,17 +1595,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/health@0.7.3 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - github.com/argoproj/gitops-engine/pkg/utils/kube@0.7.3 + google.golang.org/grpc@1.56.2 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/client-go/rest@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1757,17 +1614,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/common@0.7.3 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@0.7.3 + google.golang.org/grpc@1.56.2 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/client-go/rest@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1776,17 +1633,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.11.0 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/restmapper@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/client-go/rest@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1795,17 +1652,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/envtest@0.11.0 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane@0.11.0 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/tools/clientcmd@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/tools/auth@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/client-go/rest@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1814,17 +1671,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1833,17 +1690,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/util/managedfields@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1852,17 +1709,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/common@0.7.3 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1871,17 +1728,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/hook@0.7.3 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1890,17 +1749,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/resource@0.7.3 + google.golang.org/grpc/reflection@1.56.2 + + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1909,17 +1770,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/ignore@0.7.3 + google.golang.org/grpc/health@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + github.com/golang/protobuf/jsonpb@1.4.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1928,17 +1791,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/syncwaves@0.7.3 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/grpc/internal/pretty@1.56.2 + + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -1947,55 +1814,95 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/testing@0.7.3 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/apimachinery/pkg/watch@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - golang.org/x/net/http2@#9d032be2e588 - - - -
    • -
    • - Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0 - - k8s.io/client-go/tools/record@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/tools/reference@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Stack-based Buffer Overflow when processing input that uses pathologically deep nesting.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/cache@0.23.1 - - k8s.io/client-go/tools/pager@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/apimachinery/pkg/watch@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2004,17 +1911,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/pkg/apis/clientauthentication/v1beta1@0.23.1 - - k8s.io/client-go/pkg/apis/clientauthentication@0.23.1 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + github.com/argoproj/pkg/grpc/http@#d56162821bd1 - k8s.io/apimachinery/pkg/watch@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/runtime@1.16.0 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2023,17 +1926,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime@0.11.0 + google.golang.org/grpc@1.56.2 - sigs.k8s.io/controller-runtime/pkg/scheme@0.11.0 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2042,17 +1941,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/util/retry@0.23.1 - - k8s.io/apimachinery/pkg/api/errors@0.23.1 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/apimachinery/pkg/watch@0.23.1 + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.11.3 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2061,17 +1956,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/resource@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/api/core/v1@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2080,17 +1973,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/health@0.7.3 - - k8s.io/kubectl/pkg/util/podutils@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2099,17 +1990,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/api/validation@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1/validation@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2118,17 +2007,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/portforward@0.23.1 - - k8s.io/api/core/v1@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2137,17 +2024,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery/fake@0.23.1 - - k8s.io/client-go/testing@0.23.1 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2156,17 +2041,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/fake@0.23.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/client-go/testing@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2175,17 +2058,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/remotecommand@0.23.1 - - k8s.io/client-go/transport/spdy@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2194,17 +2075,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.3.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 - github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.3.0 + google.golang.org/grpc@1.56.2 - github.com/grpc-ecosystem/go-grpc-middleware/tags@1.3.0 + google.golang.org/grpc/internal/transport@1.56.2 - google.golang.org/grpc@1.45.0 + google.golang.org/grpc/internal/pretty@1.56.2 - google.golang.org/grpc/internal/transport@1.45.0 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2213,19 +2092,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 + google.golang.org/grpc@1.56.2 - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/restmapper@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/client-go/discovery@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2234,19 +2109,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/cache@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 + go.opentelemetry.io/proto/otlp/collector/trace/v1@0.19.0 - k8s.io/client-go/restmapper@0.23.1 + github.com/grpc-ecosystem/grpc-gateway/v2/runtime@2.11.3 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/protobuf/types/known/structpb@1.31.0 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2255,19 +2126,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/cache@0.7.3 - - k8s.io/kubectl/pkg/util/openapi@0.23.1 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2276,19 +2145,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync@0.7.3 + google.golang.org/grpc/reflection@1.56.2 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.56.2 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2297,19 +2164,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/utils/kube@0.7.3 - - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc/health@1.56.2 - k8s.io/client-go/discovery@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2318,19 +2183,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/apimachinery/pkg/runtime/serializer@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2339,19 +2202,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/listers/core/v1@0.23.1 - - k8s.io/client-go/tools/cache@0.23.1 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - k8s.io/client-go/tools/pager@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2360,19 +2221,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#567361917320 + github.com/grpc-ecosystem/go-grpc-middleware/auth@1.4.0 - k8s.io/client-go/tools/cache@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/tools/pager@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2381,19 +2240,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers/core/v1@0.23.1 - - k8s.io/client-go/tools/cache@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/retry@1.4.0 - k8s.io/client-go/tools/pager@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2402,19 +2259,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/informers@0.23.1 - - k8s.io/client-go/tools/cache@0.23.1 + github.com/grpc-ecosystem/go-grpc-prometheus@1.2.0 - k8s.io/client-go/tools/pager@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2423,19 +2278,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#567361917320 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.16.0 - k8s.io/client-go/tools/cache@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/tools/pager@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2444,19 +2297,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/kubectl/pkg/util/term@0.23.1 - - k8s.io/client-go/tools/remotecommand@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus@1.4.0 - k8s.io/client-go/transport/spdy@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/client-go/rest@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/transport@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2465,21 +2316,17 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/hook@0.7.3 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@0.42.0 - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@0.7.3 + google.golang.org/grpc@1.56.2 - github.com/argoproj/gitops-engine/pkg/sync/common@0.7.3 + google.golang.org/grpc/internal/transport@1.56.2 - github.com/argoproj/gitops-engine/pkg/utils/kube@0.7.3 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/client-go/discovery@0.23.1 - - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2488,21 +2335,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/syncwaves@0.7.3 + github.com/improbable-eng/grpc-web/go/grpcweb@0.15.0 - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@0.7.3 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - github.com/argoproj/gitops-engine/pkg/sync/common@0.7.3 + google.golang.org/grpc@1.56.2 - github.com/argoproj/gitops-engine/pkg/utils/kube@0.7.3 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/kubectl/pkg/util/openapi@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/client-go/discovery@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2511,21 +2356,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/manager@0.11.0 + google.golang.org/grpc/reflection@1.56.2 - sigs.k8s.io/controller-runtime/pkg/webhook@0.11.0 + google.golang.org/grpc/reflection/grpc_reflection_v1alpha@1.56.2 - sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics@0.11.0 + google.golang.org/grpc@1.56.2 - sigs.k8s.io/controller-runtime/pkg/metrics@0.11.0 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/tools/cache@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/client-go/rest@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2534,21 +2377,19 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/event@0.11.0 + google.golang.org/grpc/health@1.56.2 - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 + google.golang.org/grpc/health/grpc_health_v1@1.56.2 - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 + google.golang.org/grpc@1.56.2 - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/client-go/restmapper@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/client-go/discovery@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2557,21 +2398,21 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/kubernetes/scheme@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 - k8s.io/apimachinery/pkg/runtime/serializer@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0 @@ -2580,44 +2421,94 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/tools/clientcmd@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus@1.4.0 + + github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus@1.4.0 - k8s.io/client-go/tools/clientcmd/api/latest@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware/tags@1.4.0 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.23.1 + github.com/grpc-ecosystem/go-grpc-middleware@1.4.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + google.golang.org/grpc@1.56.2 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + google.golang.org/grpc/internal/transport@1.56.2 - k8s.io/apimachinery/pkg/watch@0.23.1 + google.golang.org/grpc/internal/pretty@1.56.2 - k8s.io/apimachinery/pkg/util/net@0.23.1 + github.com/golang/protobuf/jsonpb@1.4.2 - golang.org/x/net/http2@#9d032be2e588 + google.golang.org/protobuf/encoding/protojson@1.31.0
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Authentication Bypass by Capture-replay

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/crypto/ssh +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@0.0.0 and golang.org/x/crypto/ssh@0.16.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/controller/controllerutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - - k8s.io/client-go/restmapper@0.23.1 - - k8s.io/client-go/discovery@0.23.1 - - k8s.io/client-go/rest@0.23.1 - - k8s.io/client-go/transport@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2626,23 +2517,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/sync/ignore@0.7.3 - - github.com/argoproj/gitops-engine/pkg/sync/hook@0.7.3 - - github.com/argoproj/gitops-engine/pkg/sync/hook/helm@0.7.3 - - github.com/argoproj/gitops-engine/pkg/sync/common@0.7.3 - - github.com/argoproj/gitops-engine/pkg/utils/kube@0.7.3 - - k8s.io/kubectl/pkg/util/openapi@0.23.1 + golang.org/x/crypto/ssh/knownhosts@0.16.0 - k8s.io/client-go/discovery@0.23.1 - - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2651,23 +2528,9 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/handler@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - - k8s.io/client-go/restmapper@0.23.1 - - k8s.io/client-go/discovery@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/rest@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2676,23 +2539,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/gitops-engine/pkg/diff@0.7.3 - - k8s.io/client-go/kubernetes/scheme@0.23.1 - - k8s.io/apimachinery/pkg/runtime/serializer@0.23.1 - - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + github.com/skeema/knownhosts@1.2.1 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 - - k8s.io/apimachinery/pkg/watch@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2701,23 +2552,11 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/envtest@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/webhook/conversion@0.11.0 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/apimachinery/pkg/runtime/serializer@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.23.1 - - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 - - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 - - k8s.io/apimachinery/pkg/watch@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2726,23 +2565,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#567361917320 - - k8s.io/client-go/tools/clientcmd@0.23.1 - - k8s.io/client-go/tools/clientcmd/api/latest@0.23.1 - - k8s.io/apimachinery/pkg/runtime/serializer/versioning@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/apimachinery/pkg/apis/meta/v1/unstructured@0.23.1 + github.com/skeema/knownhosts@1.2.1 - k8s.io/apimachinery/pkg/apis/meta/v1@0.23.1 + golang.org/x/crypto/ssh/knownhosts@0.16.0 - k8s.io/apimachinery/pkg/watch@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2751,23 +2580,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/client-go/restmapper@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/discovery@0.23.1 + github.com/skeema/knownhosts@1.2.1 - k8s.io/client-go/rest@0.23.1 - - k8s.io/client-go/transport@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2776,23 +2595,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/cache@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 + github.com/xanzy/ssh-agent@0.3.3 - k8s.io/client-go/restmapper@0.23.1 + golang.org/x/crypto/ssh/agent@0.16.0 - k8s.io/client-go/discovery@0.23.1 - - k8s.io/client-go/rest@0.23.1 - - k8s.io/client-go/transport@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2801,25 +2610,13 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - - k8s.io/client-go/restmapper@0.23.1 + github.com/go-git/go-git/v5@5.11.0 - k8s.io/client-go/discovery@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/client-go/rest@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/transport@0.23.1 - - k8s.io/apimachinery/pkg/util/net@0.23.1 - - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2828,27 +2625,15 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/source@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/source/internal@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/predicate@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/event@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/client-go/restmapper@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/discovery@0.23.1 + github.com/skeema/knownhosts@1.2.1 - k8s.io/client-go/rest@0.23.1 + golang.org/x/crypto/ssh/knownhosts@0.16.0 - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2857,27 +2642,32 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/handler@0.11.0 + github.com/go-git/go-git/v5@5.11.0 - sigs.k8s.io/controller-runtime/pkg/runtime/inject@0.11.0 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 + github.com/skeema/knownhosts@1.2.1 - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 - - k8s.io/client-go/restmapper@0.23.1 + golang.org/x/crypto/ssh@0.16.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/discovery@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/client-go/rest@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/transport@0.23.1 + github.com/xanzy/ssh-agent@0.3.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + golang.org/x/crypto/ssh/agent@0.16.0 - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2886,31 +2676,36 @@

      Detailed paths

      Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - sigs.k8s.io/controller-runtime/pkg/source@0.11.0 + github.com/go-git/go-git/v5@5.11.0 - sigs.k8s.io/controller-runtime/pkg/source/internal@0.11.0 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - sigs.k8s.io/controller-runtime/pkg/predicate@0.11.0 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - sigs.k8s.io/controller-runtime/pkg/event@0.11.0 + github.com/skeema/knownhosts@1.2.1 - sigs.k8s.io/controller-runtime/pkg/client@0.11.0 + golang.org/x/crypto/ssh/knownhosts@0.16.0 - sigs.k8s.io/controller-runtime/pkg/internal/objectutil@0.11.0 - - sigs.k8s.io/controller-runtime/pkg/client/apiutil@0.11.0 + golang.org/x/crypto/ssh@0.16.0 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - k8s.io/client-go/restmapper@0.23.1 + github.com/go-git/go-git/v5@5.11.0 - k8s.io/client-go/discovery@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/client@5.11.0 - k8s.io/client-go/rest@0.23.1 + github.com/go-git/go-git/v5/plumbing/transport/ssh@5.11.0 - k8s.io/client-go/transport@0.23.1 + github.com/xanzy/ssh-agent@0.3.3 - k8s.io/apimachinery/pkg/util/net@0.23.1 + golang.org/x/crypto/ssh/agent@0.16.0 - golang.org/x/net/http2@#9d032be2e588 + golang.org/x/crypto/ssh@0.16.0 @@ -2922,39 +2717,49 @@

      Detailed paths


      Overview

      -

      golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

      -

      Affected versions of this package are vulnerable to Denial of Service (DoS) due to improper checks and limitations for the number of entries in the cache, which can allow an attacker to consume unbounded amounts of memory by sending a small number of very large keys.

      -

      Details

      -

      Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

      -

      Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

      -

      One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

      -

      When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

      -

      Two common types of DoS vulnerabilities:

      -
        -
      • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

        +

        golang.org/x/crypto/ssh is a SSH client and server

        +

        Affected versions of this package are vulnerable to Authentication Bypass by Capture-replay during the establishment of the secure channel. An attacker can manipulate handshake sequence numbers to delete messages sent immediately after the channel is established.

        +

        Note:

        +
          +
        1. Sequence numbers are only validated once the channel is established and arbitrary messages are allowed during the handshake, allowing them to manipulate the sequence numbers.

        2. -
        3. Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

          +
        4. The potential consequences of the general Terrapin attack are dependent on the messages exchanged after the handshake concludes. If you are using a custom SSH service and do not resort to the authentication protocol, you should check that dropping the first few messages of a connection does not yield security risks.

        5. -
      + +

      Impact:

      +

      While cryptographically novel, there is no discernable impact on the integrity of SSH traffic beyond giving the attacker the ability to delete the message that enables some features related to keystroke timing obfuscation. To successfully carry out the exploitation, the connection needs to be protected using either the ChaCha20-Poly1305 or CBC with Encrypt-then-MAC encryption methods. The attacker must also be able to intercept and modify the connection's traffic.

      +

      Workaround

      +

      Temporarily disable the affected chacha20-poly1305@openssh.com encryption and *-etm@openssh.com MAC algorithms in the affected configuration, and use unaffected algorithms like AES-GCM instead.

      Remediation

      -

      Upgrade golang.org/x/net/http2 to version 0.4.0 or higher.

      +

      Upgrade golang.org/x/crypto/ssh to version 0.17.0 or higher.

      References


    -

    Improper Input Validation

    +

    MPL-2.0 license

    @@ -2964,19 +2769,22 @@

    Improper Input Validation


      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • Package Manager: golang
    • - Vulnerable module: + Module: - go.mongodb.org/mongo-driver/bson/bsonrw + github.com/r3labs/diff
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/r3labs/diff@1.1.0 - github.com/argoproj/argo-cd/v2@0.0.0, github.com/go-openapi/runtime/middleware@0.19.4 and others
    @@ -2990,34 +2798,69 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/go-openapi/runtime/middleware@0.19.4 - - github.com/go-openapi/validate@0.19.5 - - github.com/go-openapi/strfmt@0.19.3 - - go.mongodb.org/mongo-driver/bson@1.1.2 - - go.mongodb.org/mongo-driver/bson/bsonrw@1.1.2 + github.com/r3labs/diff@1.1.0 + + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-version +
    • + +
    • Introduced through: + + + github.com/argoproj/argo-cd/v2@0.0.0, code.gitea.io/sdk/gitea@0.15.1 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
    • Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/go-openapi/runtime/middleware@0.19.4 - - github.com/go-openapi/validate@0.19.5 + code.gitea.io/sdk/gitea@0.15.1 - github.com/go-openapi/strfmt@0.19.3 - - go.mongodb.org/mongo-driver/bson@1.1.2 - - go.mongodb.org/mongo-driver/bson/bsoncodec@1.1.2 - - go.mongodb.org/mongo-driver/bson/bsonrw@1.1.2 + github.com/hashicorp/go-version@1.2.1 @@ -3028,27 +2871,17 @@

      Detailed paths


      -

      Overview

      -

      go.mongodb.org/mongo-driver/bson/bsonrw is a The MongoDB supported driver for Go.

      -

      Affected versions of this package are vulnerable to Improper Input Validation. Specific cstrings input may not be properly validated in the MongoDB Go Driver when marshalling Go objects into BSON. A malicious user could use a Go object with specific string to potentially inject additional fields into marshalled documents.

      -

      Remediation

      -

      Upgrade go.mongodb.org/mongo-driver/bson/bsonrw to version 1.5.1 or higher.

      -

      References

      - +

      MPL-2.0 license


    -

    Insecure Randomness

    +

    MPL-2.0 license

    @@ -3058,19 +2891,22 @@

    Insecure Randomness


      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/Masterminds/goutils + github.com/hashicorp/go-retryablehttp
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/hashicorp/go-retryablehttp@0.7.4 - github.com/argoproj/argo-cd/v2@0.0.0, github.com/argoproj/notifications-engine/pkg/api@#567361917320 and others
    @@ -3084,13 +2920,61 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#567361917320 + github.com/hashicorp/go-retryablehttp@0.7.4 + + + + +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/xanzy/go-gitlab@0.91.1 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#9dcecdc3eebf + + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/templates@#567361917320 + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf - github.com/Masterminds/sprig@2.22.0 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -3099,15 +2983,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#567361917320 + github.com/argoproj/notifications-engine/pkg/api@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/api@#567361917320 + github.com/argoproj/notifications-engine/pkg/subscriptions@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/templates@#567361917320 + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf - github.com/Masterminds/sprig@2.22.0 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -3116,15 +3000,15 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#567361917320 + github.com/argoproj/notifications-engine/pkg/controller@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/api@#567361917320 + github.com/argoproj/notifications-engine/pkg/subscriptions@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/templates@#567361917320 + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf - github.com/Masterminds/sprig@2.22.0 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-retryablehttp@0.7.4 @@ -3135,25 +3019,17 @@

    Detailed paths


    -

    Overview

    -

    github.com/masterminds/goutils is a provides users with utility functions to manipulate strings in various ways.

    -

    Affected versions of this package are vulnerable to Insecure Randomness via the RandomAlphaNumeric(int) and CryptoRandomAlphaNumeric(int) functions. Small values of int in the functions above will return a smaller subset of results than they should. For example, RandomAlphaNumeric(1) would always return a digit in the 0-9 range, while RandomAlphaNumeric(4) return around ~7 million of the ~13M possible permutations.

    -

    Remediation

    -

    Upgrade github.com/masterminds/goutils to version 1.1.1 or higher.

    -

    References

    - +

    MPL-2.0 license


  • -

    Insecure Randomness

    +

    MPL-2.0 license

    @@ -3163,19 +3039,22 @@

    Insecure Randomness


      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • Package Manager: golang
    • - Vulnerable module: + Module: - github.com/Masterminds/goutils + github.com/hashicorp/go-cleanhttp
    • Introduced through: - github.com/argoproj/argo-cd/v2@0.0.0, github.com/argoproj/notifications-engine/pkg/api@#567361917320 and others + github.com/argoproj/argo-cd/v2@0.0.0, github.com/hashicorp/go-retryablehttp@0.7.4 and others
    @@ -3189,13 +3068,82 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/api@#567361917320 + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + + +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/xanzy/go-gitlab@0.91.1 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/xanzy/go-gitlab@0.91.1 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/subscriptions@#9dcecdc3eebf + + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf + + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 + + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 + + + +
  • +
  • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/argoproj/notifications-engine/pkg/cmd@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/templates@#567361917320 + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf - github.com/Masterminds/sprig@2.22.0 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3204,15 +3152,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/cmd@#567361917320 + github.com/argoproj/notifications-engine/pkg/api@#9dcecdc3eebf + + github.com/argoproj/notifications-engine/pkg/subscriptions@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/api@#567361917320 + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/templates@#567361917320 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/sprig@2.22.0 + github.com/hashicorp/go-retryablehttp@0.7.4 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3221,15 +3171,17 @@

    Detailed paths

    Introduced through: github.com/argoproj/argo-cd/v2@0.0.0 - github.com/argoproj/notifications-engine/pkg/controller@#567361917320 + github.com/argoproj/notifications-engine/pkg/controller@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/api@#567361917320 + github.com/argoproj/notifications-engine/pkg/subscriptions@#9dcecdc3eebf - github.com/argoproj/notifications-engine/pkg/templates@#567361917320 + github.com/argoproj/notifications-engine/pkg/services@#9dcecdc3eebf - github.com/Masterminds/sprig@2.22.0 + github.com/opsgenie/opsgenie-go-sdk-v2/client@1.0.5 - github.com/Masterminds/goutils@1.1.0 + github.com/hashicorp/go-retryablehttp@0.7.4 + + github.com/hashicorp/go-cleanhttp@0.5.2 @@ -3240,25 +3192,77 @@

    Detailed paths


    -

    Overview

    -

    github.com/masterminds/goutils is a provides users with utility functions to manipulate strings in various ways.

    -

    Affected versions of this package are vulnerable to Insecure Randomness when randomly-generated alphanumeric strings contain significantly less entropy than expected, the RandomAlphaNumeric and CryptoRandomAlphaNumeric functions always return strings containing at least one digit from 0 to 9. This significantly reduces the amount of entropy in short strings generated by these functions.

    -

    Remediation

    -

    Upgrade github.com/masterminds/goutils to version 1.1.1 or higher.

    -

    References

    - +

    MPL-2.0 license

    + +
    + + + +
  • +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/gosimple/slug +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/gosimple/slug@1.13.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/gosimple/slug@1.13.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license


    -

    Regular Expression Denial of Service (ReDoS)

    +

    Improper Handling of Highly Compressed Data (Data Amplification)

    @@ -3269,18 +3273,21 @@

    Regular Expression Denial of Service (ReDoS)

    • - Package Manager: npm + Manifest file: /argo-cd/argoproj/argo-cd/v2 go.mod +
    • +
    • + Package Manager: golang
    • Vulnerable module: - cookiejar + github.com/go-jose/go-jose/v3
    • Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 and github.com/go-jose/go-jose/v3@3.0.1 - argo-cd-ui@1.0.0, superagent@7.1.3 and others
    @@ -3292,11 +3299,20 @@

    Detailed paths

    • Introduced through: - argo-cd-ui@1.0.0 + github.com/argoproj/argo-cd/v2@0.0.0 + + github.com/go-jose/go-jose/v3@3.0.1 + + + +
    • +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@0.0.0 - superagent@7.1.3 + github.com/coreos/go-oidc/v3/oidc@3.6.0 - cookiejar@2.1.3 + github.com/go-jose/go-jose/v3@3.0.1 @@ -3308,91 +3324,20 @@

      Detailed paths


      Overview

      -

      Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS) via the Cookie.parse function, which uses an insecure regular expression.

      -

      PoC

      -
      const { CookieJar } = require("cookiejar");
      -        
      -        const jar = new CookieJar();
      -        
      -        const start = performance.now();
      -        const attack = "a" + "t".repeat(50_000);
      -        jar.setCookie(attack);
      -        console.log(`CookieJar.setCookie(): ${performance.now() - start}`);
      -        
      -

      Details

      -

      Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.

      -

      The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.

      -

      Let’s take the following regular expression as an example:

      -
      regex = /A(B|C+)+D/
      -        
      -

      This regular expression accomplishes the following:

      -
        -
      • A The string must start with the letter 'A'
      • -
      • (B|C+)+ The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the + matches one or more times). The + at the end of this section states that we can look for one or more matches of this section.
      • -
      • D Finally, we ensure this section of the string ends with a 'D'
      • -
      -

      The expression would match inputs such as ABBD, ABCCCCD, ABCBCCCD and ACCCCCD

      -

      It most cases, it doesn't take very long for a regex engine to find a match:

      -
      $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD")'
      -        0.04s user 0.01s system 95% cpu 0.052 total
      -        
      -        $ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX")'
      -        1.79s user 0.02s system 99% cpu 1.812 total
      -        
      -

      The entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.

      -

      Most Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as catastrophic backtracking.

      -

      Let's look at how our expression runs into this problem, using a shorter string: "ACCCX". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:

      -
        -
      1. CCC
      2. -
      3. CC+C
      4. -
      5. C+CC
      6. -
      7. C+C+C.
      8. -
      -

      The engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use RegEx 101 debugger to see the engine has to take a total of 38 steps before it can determine the string doesn't match.

      -

      From there, the number of steps the engine must use to validate a string just continues to grow.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      StringNumber of C'sNumber of steps
      ACCCX338
      ACCCCX471
      ACCCCCX5136
      ACCCCCCCCCCCCCCX1465,553
      -

      By the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.

      +

      Affected versions of this package are vulnerable to Improper Handling of Highly Compressed Data (Data Amplification). An attacker could send a JWE containing compressed data that, when decompressed by Decrypt or DecryptMulti, would use large amounts of memory and CPU.

      Remediation

      -

      Upgrade cookiejar to version 2.1.4 or higher.

      +

      Upgrade github.com/go-jose/go-jose/v3 to version 3.0.3 or higher.

      References


    diff --git a/docs/snyk/v2.9.9/ghcr.io_dexidp_dex_v2.37.0.html b/docs/snyk/v2.9.9/ghcr.io_dexidp_dex_v2.37.0.html new file mode 100644 index 0000000000000..ca1fb70c0e4b2 --- /dev/null +++ b/docs/snyk/v2.9.9/ghcr.io_dexidp_dex_v2.37.0.html @@ -0,0 +1,4337 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    March 24th 2024, 12:17:49 am (UTC+00:00)

    +
    +
    + Scanned the following paths: +
      +
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex (apk)
    • +
    • ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3//usr/local/bin/gomplate (gomodules)
    • +
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex//usr/local/bin/docker-entrypoint (gomodules)
    • +
    • ghcr.io/dexidp/dex:v2.37.0/dexidp/dex//usr/local/bin/dex (gomodules)
    • +
    +
    + +
    +
    42 known vulnerabilities
    +
    121 vulnerable dependency paths
    +
    786 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    Path Traversal

    +
    + +
    + critical severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-git/go-git/v5 +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/go-git/go-git/v5@v5.4.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/go-git/go-git/v5@v5.4.2 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Path Traversal via malicious server replies. An attacker can create and amend files across the filesystem and potentially achieve remote code execution by sending crafted responses to the client.

    +

    Notes:

    +
      +
    1. This is only exploitable if the client is using ChrootOS, which is the default for certain functions such as PlainClone.

      +
    2. +
    3. Applications using BoundOS or in-memory filesystems are not affected by this issue.

      +
    4. +
    5. Users running versions of go-git from v4 and above are recommended to upgrade to v5.11 in order to mitigate this vulnerability.

      +
    6. +
    +

    Workaround

    +

    This vulnerability can be mitigated by limiting the client's use to trustworthy Git servers.

    +

    Remediation

    +

    Upgrade github.com/go-git/go-git/v5 to version 5.11.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + critical severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + busybox/busybox +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and busybox/busybox@1.36.1-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/busybox@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + alpine-baselayout/alpine-baselayout@3.4.3-r1 + + busybox/busybox-binsh@1.36.1-r0 + + busybox/busybox@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/busybox-binsh@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + alpine-baselayout/alpine-baselayout@3.4.3-r1 + + busybox/busybox-binsh@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream busybox package and not the busybox package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    There is a stack overflow vulnerability in ash.c:6030 in busybox before 1.35. In the environment of Internet of Vehicles, this vulnerability can be executed from command to arbitrary code execution.

    +

    Remediation

    +

    Upgrade Alpine:3.18 busybox to version 1.36.1-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-5363

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: A bug has been identified in the processing of key and + initialisation vector (IV) lengths. This can lead to potential truncation + or overruns during the initialisation of some symmetric ciphers.

    +

    Impact summary: A truncation in the IV can result in non-uniqueness, + which could result in loss of confidentiality for some cipher modes.

    +

    When calling EVP_EncryptInit_ex2(), EVP_DecryptInit_ex2() or + EVP_CipherInit_ex2() the provided OSSL_PARAM array is processed after + the key and IV have been established. Any alterations to the key length, + via the "keylen" parameter or the IV length, via the "ivlen" parameter, + within the OSSL_PARAM array will not take effect as intended, potentially + causing truncation or overreading of these values. The following ciphers + and cipher modes are impacted: RC2, RC4, RC5, CCM, GCM and OCB.

    +

    For the CCM, GCM and OCB cipher modes, truncation of the IV can result in + loss of confidentiality. For example, when following NIST's SP 800-38D + section 8.2.1 guidance for constructing a deterministic IV for AES in + GCM mode, truncation of the counter portion could lead to IV reuse.

    +

    Both truncations and overruns of the key and overruns of the IV will + produce incorrect results and could, in some cases, trigger a memory + exception. However, these issues are not currently assessed as security + critical.

    +

    Changing the key and/or IV lengths is not considered to be a common operation + and the vulnerable API was recently introduced. Furthermore it is likely that + application developers will have spotted this problem during testing since + decryption would fail unless both peers in the communication were similarly + vulnerable. For these reasons we expect the probability of an application being + vulnerable to this to be quite low. However if an application is vulnerable then + this issue is considered very serious. For these reasons we have assessed this + issue as Moderate severity overall.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this because + the issue lies outside of the FIPS provider boundary.

    +

    OpenSSL 3.1 and 3.0 are vulnerable to this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/grpc +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/grpc@v1.46.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/grpc@v1.46.2 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/grpc@v1.56.1 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    google.golang.org/grpc is a Go implementation of gRPC

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) in the implementation of the HTTP/2 protocol. An attacker can cause a denial of service (including via DDoS) by rapidly resetting many streams through request cancellation.

    +

    Remediation

    +

    Upgrade google.golang.org/grpc to version 1.56.3, 1.57.1, 1.58.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and golang.org/x/net/http2@v0.7.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + golang.org/x/net/http2@v0.7.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + golang.org/x/net/http2@v0.11.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) in the implementation of the HTTP/2 protocol. An attacker can cause a denial of service (including via DDoS) by rapidly resetting many streams through request cancellation.

    +

    Remediation

    +

    Upgrade golang.org/x/net/http2 to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Heap-based Buffer Overflow

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/mattn/go-sqlite3 +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/mattn/go-sqlite3@v1.14.17 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/mattn/go-sqlite3@v1.14.17 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Heap-based Buffer Overflow via the sessionReadRecord function in the ext/session/sqlite3session.c file. An attacker can cause a program crash or execute arbitrary code by manipulating the input to trigger a heap-based buffer overflow.

    +

    Remediation

    +

    Upgrade github.com/mattn/go-sqlite3 to version 1.14.18 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-jose/go-jose/v3 +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/go-jose/go-jose/v3@v3.0.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/go-jose/go-jose/v3@v3.0.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) when decrypting JWE inputs. An attacker can cause a denial-of-service by providing a PBES2 encrypted JWE blob with a very large p2c value.

    +

    Details

    +

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

    +

    Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

    +

    One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

    +

    When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

    +

    Two common types of DoS vulnerabilities:

    +
      +
    • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

      +
    • +
    • Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

      +
    • +
    +

    Remediation

    +

    Upgrade github.com/go-jose/go-jose/v3 to version 3.0.1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Authentication

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The AES-SIV cipher implementation contains a bug that causes + it to ignore empty associated data entries which are unauthenticated as + a consequence.

    +

    Impact summary: Applications that use the AES-SIV algorithm and want to + authenticate empty data entries as associated data can be mislead by removing + adding or reordering such empty entries as these are ignored by the OpenSSL + implementation. We are currently unaware of any such applications.

    +

    The AES-SIV algorithm allows for authentication of multiple associated + data entries along with the encryption. To authenticate empty data the + application has to call EVP_EncryptUpdate() (or EVP_CipherUpdate()) with + NULL pointer as the output buffer and 0 as the input buffer length. + The AES-SIV implementation in OpenSSL just returns success for such a call + instead of performing the associated data authentication operation. + The empty data thus will not be authenticated.

    +

    As this issue does not affect non-empty associated data authentication and + we expect it to be rare for an application to use empty associated data + entries this is qualified as Low severity issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.1-r2 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Inefficient Regular Expression Complexity

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Checking excessively long DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_check(), DH_check_ex() + or EVP_PKEY_param_check() to check a DH key or DH parameters may experience long + delays. Where the key or parameters that are being checked have been obtained + from an untrusted source this may lead to a Denial of Service.

    +

    The function DH_check() performs various checks on DH parameters. One of those + checks confirms that the modulus ('p' parameter) is not too large. Trying to use + a very large modulus is slow and OpenSSL will not normally use a modulus which + is over 10,000 bits in length.

    +

    However the DH_check() function checks numerous aspects of the key or parameters + that have been supplied. Some of those checks use the supplied modulus value + even if it has already been found to be too large.

    +

    An application that calls DH_check() and supplies a key or parameters obtained + from an untrusted source could be vulernable to a Denial of Service attack.

    +

    The function DH_check() is itself called by a number of other OpenSSL functions. + An application calling any of those other functions may similarly be affected. + The other functions affected by this are DH_check_ex() and + EVP_PKEY_param_check().

    +

    Also vulnerable are the OpenSSL dhparam and pkeyparam command line applications + when using the '-check' option.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue. + The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.1-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Excessive Iteration

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Checking excessively long DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_check(), DH_check_ex() + or EVP_PKEY_param_check() to check a DH key or DH parameters may experience long + delays. Where the key or parameters that are being checked have been obtained + from an untrusted source this may lead to a Denial of Service.

    +

    The function DH_check() performs various checks on DH parameters. After fixing + CVE-2023-3446 it was discovered that a large q parameter value can also trigger + an overly long computation during some of these checks. A correct q value, + if present, cannot be larger than the modulus p parameter, thus it is + unnecessary to perform these checks if q is larger than p.

    +

    An application that calls DH_check() and supplies a key or parameters obtained + from an untrusted source could be vulnerable to a Denial of Service attack.

    +

    The function DH_check() is itself called by a number of other OpenSSL functions. + An application calling any of those other functions may similarly be affected. + The other functions affected by this are DH_check_ex() and + EVP_PKEY_param_check().

    +

    Also vulnerable are the OpenSSL dhparam and pkeyparam command line applications + when using the "-check" option.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.2-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Check for Unusual or Exceptional Conditions

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Generating excessively long X9.42 DH keys or checking + excessively long X9.42 DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_generate_key() to + generate an X9.42 DH key may experience long delays. Likewise, applications + that use DH_check_pub_key(), DH_check_pub_key_ex() or EVP_PKEY_public_check() + to check an X9.42 DH key or X9.42 DH parameters may experience long delays. + Where the key or parameters that are being checked have been obtained from + an untrusted source this may lead to a Denial of Service.

    +

    While DH_check() performs all the necessary checks (as of CVE-2023-3817), + DH_check_pub_key() doesn't make any of these checks, and is therefore + vulnerable for excessively large P and Q parameters.

    +

    Likewise, while DH_generate_key() performs a check for an excessively large + P, it doesn't check for an excessively large Q.

    +

    An application that calls DH_generate_key() or DH_check_pub_key() and + supplies a key or parameters obtained from an untrusted source could be + vulnerable to a Denial of Service attack.

    +

    DH_generate_key() and DH_check_pub_key() are also called by a number of + other OpenSSL functions. An application calling any of those other + functions may similarly be affected. The other functions affected by this + are DH_check_pub_key_ex(), EVP_PKEY_public_check(), and EVP_PKEY_generate().

    +

    Also vulnerable are the OpenSSL pkey command line application when using the + "-pubcheck" option, as well as the OpenSSL genpkey command line application.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

    +

    Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

    +

    The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

    +

    The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

    +

    The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-0727

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

    +

    Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

    +

    A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

    +

    OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

    +

    We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

    +

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r5 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/internal/encoding/json +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/internal/encoding/json@v1.28.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/protobuf/internal/encoding/json@v1.28.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/protobuf/internal/encoding/json@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Stack-based Buffer Overflow

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/encoding/protojson@v1.28.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/protobuf/encoding/protojson@v1.28.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Stack-based Buffer Overflow when processing input that uses pathologically deep nesting.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and google.golang.org/protobuf/encoding/protojson@v1.28.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + google.golang.org/protobuf/encoding/protojson@v1.28.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Allocation of Resources Without Limits or Throttling

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/http2 +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and golang.org/x/net/http2@v0.7.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + golang.org/x/net/http2@v0.7.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + golang.org/x/net/http2@v0.11.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/net/http2 is a work-in-progress HTTP/2 implementation for Go.

    +

    Affected versions of this package are vulnerable to Allocation of Resources Without Limits or Throttling when MaxConcurrentStreams handler goroutines running. A a handler is started until one of the existing handlers exits.

    +

    Note:

    +

    This issue is related to CVE-2023-44487

    +

    Remediation

    +

    Upgrade golang.org/x/net/http2 to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Cross-site Scripting (XSS)

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/net/html +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and golang.org/x/net/html@v0.11.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + golang.org/x/net/html@v0.11.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/net/html is a package that implements an HTML5-compliant tokenizer and parser.

    +

    Affected versions of this package are vulnerable to Cross-site Scripting (XSS) in the render1() function in render.go. Text nodes not in the HTML namespace are incorrectly literally rendered, causing text which should be escaped to not be.

    +

    Details

    +

    A cross-site scripting attack occurs when the attacker tricks a legitimate web-based application or site to accept a request as originating from a trusted source.

    +

    This is done by escaping the context of the web application; the web application then delivers that data to its users along with other trusted dynamic content, without validating it. The browser unknowingly executes malicious script on the client side (through client-side languages; usually JavaScript or HTML) in order to perform actions that are otherwise typically blocked by the browser’s Same Origin Policy.

    +

    Injecting malicious code is the most prevalent manner by which XSS is exploited; for this reason, escaping characters in order to prevent this manipulation is the top method for securing code against this vulnerability.

    +

    Escaping means that the application is coded to mark key characters, and particularly key characters included in user input, to prevent those characters from being interpreted in a dangerous context. For example, in HTML, < can be coded as &lt; and > can be coded as &gt; in order to be interpreted and displayed as themselves in text, while within the code itself, they are used for HTML tags. If malicious content is injected into an application that escapes special characters and that malicious content uses < and > as HTML tags, those characters are nonetheless not interpreted as HTML tags by the browser if they’ve been correctly escaped in the application code and in this way the attempted attack is diverted.

    +

    The most prominent use of XSS is to steal cookies (source: OWASP HttpOnly) and hijack user sessions, but XSS exploits have been used to expose sensitive information, enable access to privileged services and functionality and deliver malware.

    +

    Types of attacks

    +

    There are a few methods by which XSS can be manipulated:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeOriginDescription
    StoredServerThe malicious code is inserted in the application (usually as a link) by the attacker. The code is activated every time a user clicks the link.
    ReflectedServerThe attacker delivers a malicious link externally from the vulnerable web site application to a user. When clicked, malicious code is sent to the vulnerable web site, which reflects the attack back to the user’s browser.
    DOM-basedClientThe attacker forces the user’s browser to render a malicious page. The data in the page itself delivers the cross-site scripting data.
    MutatedThe attacker injects code that appears safe, but is then rewritten and modified by the browser, while parsing the markup. An example is rebalancing unclosed quotation marks or even adding quotation marks to unquoted parameters.
    +

    Affected environments

    +

    The following environments are susceptible to an XSS attack:

    +
      +
    • Web servers
    • +
    • Application servers
    • +
    • Web application environments
    • +
    +

    How to prevent

    +

    This section describes the top best practices designed to specifically protect your code:

    +
      +
    • Sanitize data input in an HTTP request before reflecting it back, ensuring all data is validated, filtered or escaped before echoing anything back to the user, such as the values of query parameters during searches.
    • +
    • Convert special characters such as ?, &, /, <, > and spaces to their respective HTML or URL encoded equivalents.
    • +
    • Give users the option to disable client-side scripts.
    • +
    • Redirect invalid requests.
    • +
    • Detect simultaneous logins, including those from two separate IP addresses, and invalidate those sessions.
    • +
    • Use and enforce a Content Security Policy (source: Wikipedia) to disable any features that might be manipulated for an XSS attack.
    • +
    • Read the documentation for any of the libraries referenced in your code to understand which elements allow for embedded HTML.
    • +
    +

    Remediation

    +

    Upgrade golang.org/x/net/html to version 0.13.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Authentication Bypass by Capture-replay

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/crypto/ssh +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and golang.org/x/crypto/ssh@v0.0.0-20220525230936-793ad666bf5e + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + golang.org/x/crypto/ssh@v0.0.0-20220525230936-793ad666bf5e + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/crypto/ssh is a SSH client and server

    +

    Affected versions of this package are vulnerable to Authentication Bypass by Capture-replay during the establishment of the secure channel. An attacker can manipulate handshake sequence numbers to delete messages sent immediately after the channel is established.

    +

    Note:

    +
      +
    1. Sequence numbers are only validated once the channel is established and arbitrary messages are allowed during the handshake, allowing them to manipulate the sequence numbers.

      +
    2. +
    3. The potential consequences of the general Terrapin attack are dependent on the messages exchanged after the handshake concludes. If you are using a custom SSH service and do not resort to the authentication protocol, you should check that dropping the first few messages of a connection does not yield security risks.

      +
    4. +
    +

    Impact:

    +

    While cryptographically novel, there is no discernable impact on the integrity of SSH traffic beyond giving the attacker the ability to delete the message that enables some features related to keystroke timing obfuscation. To successfully carry out the exploitation, the connection needs to be protected using either the ChaCha20-Poly1305 or CBC with Encrypt-then-MAC encryption methods. The attacker must also be able to intercept and modify the connection's traffic.

    +

    Workaround

    +

    Temporarily disable the affected chacha20-poly1305@openssh.com encryption and *-etm@openssh.com MAC algorithms in the affected configuration, and use unaffected algorithms like AES-GCM instead.

    +

    Remediation

    +

    Upgrade golang.org/x/crypto/ssh to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/vault/sdk/helper/certutil +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/vault/sdk/helper/certutil@v0.5.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/certutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/compressutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/consts@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/jsonutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/pluginutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/helper/strutil@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/logical@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/physical@v0.5.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/sdk/physical/inmem@v0.5.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/vault/api +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/vault/api@v1.6.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/vault/api@v1.6.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/serf/coordinate +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/serf/coordinate@v0.9.7 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/serf/coordinate@v0.9.7 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/hcl/v2 +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/hashicorp/hcl/v2@v2.13.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/ext/customdecode@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/ext/tryfunc@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/gohcl@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/hclparse@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/hclsyntax@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/hclwrite@v2.13.0 + + + +
    • +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/hashicorp/hcl/v2/json@v2.13.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/hcl +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/hcl@v1.0.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl@v1.0.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/hcl/parser@v1.0.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/hcl/strconv@v1.0.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/hcl/token@v1.0.0 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/hcl/json/parser@v1.0.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/golang-lru/simplelru +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/golang-lru/simplelru@v0.5.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/golang-lru/simplelru@v0.5.4 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-version +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-version@v1.5.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-version@v1.5.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-sockaddr +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-sockaddr@v1.0.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-sockaddr@v1.0.2 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-sockaddr/template@v1.0.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-secure-stdlib/strutil +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-secure-stdlib/strutil@v0.1.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-secure-stdlib/strutil@v0.1.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-secure-stdlib/parseutil +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-secure-stdlib/parseutil@v0.1.5 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-secure-stdlib/parseutil@v0.1.5 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-secure-stdlib/mlock +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-secure-stdlib/mlock@v0.1.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-secure-stdlib/mlock@v0.1.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-rootcerts +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-rootcerts@v1.0.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-rootcerts@v1.0.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-retryablehttp +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-retryablehttp@v0.7.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-retryablehttp@v0.7.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-plugin +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-plugin@v1.4.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-plugin@v1.4.4 + + + +
    • +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-plugin/internal/plugin@v1.4.4 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-immutable-radix +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-immutable-radix@v1.3.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-immutable-radix@v1.3.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-cleanhttp +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/go-cleanhttp@v0.5.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/go-cleanhttp@v0.5.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/errwrap +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/errwrap@v1.1.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/errwrap@v1.1.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/consul/api +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/hashicorp/consul/api@v1.13.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/hashicorp/consul/api@v1.13.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/gosimple/slug +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/gosimple/slug@v1.12.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/gosimple/slug@v1.12.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/go-sql-driver/mysql +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/go-sql-driver/mysql@v1.7.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/go-sql-driver/mysql@v1.7.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    Improper Handling of Highly Compressed Data (Data Amplification)

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/dexidp/dex /usr/local/bin/dex +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-jose/go-jose/v3 +
    • + +
    • Introduced through: + + github.com/dexidp/dex@* and github.com/go-jose/go-jose/v3@v3.0.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/dexidp/dex@* + + github.com/go-jose/go-jose/v3@v3.0.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Improper Handling of Highly Compressed Data (Data Amplification). An attacker could send a JWE containing compressed data that, when decompressed by Decrypt or DecryptMulti, would use large amounts of memory and CPU.

    +

    Remediation

    +

    Upgrade github.com/go-jose/go-jose/v3 to version 3.0.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Uncontrolled Resource Consumption ('Resource Exhaustion')

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: ghcr.io/dexidp/dex:v2.37.0/hairyhenderson/gomplate/v3 /usr/local/bin/gomplate +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-git/go-git/v5/plumbing +
    • + +
    • Introduced through: + + github.com/hairyhenderson/gomplate/v3@* and github.com/go-git/go-git/v5/plumbing@v5.4.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/hairyhenderson/gomplate/v3@* + + github.com/go-git/go-git/v5/plumbing@v5.4.2 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    github.com/go-git/go-git/v5/plumbing is a highly extensible git implementation library written in pure Go.

    +

    Affected versions of this package are vulnerable to Uncontrolled Resource Consumption ('Resource Exhaustion') via specially crafted responses from a Git server, which triggers resource exhaustion in clients.

    +

    Note + This is only exploitable if the client is not using the in-memory filesystem supported by the library.

    +

    Workaround

    +

    In cases where a bump to the latest version of go-git is not possible, we recommend limiting its use to only trust-worthy Git servers.

    +

    Details

    +

    Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its intended and legitimate users.

    +

    Unlike other vulnerabilities, DoS attacks usually do not aim at breaching security. Rather, they are focused on making websites and services unavailable to genuine users resulting in downtime.

    +

    One popular Denial of Service vulnerability is DDoS (a Distributed Denial of Service), an attack that attempts to clog network pipes to the system by generating a large volume of traffic from many machines.

    +

    When it comes to open source libraries, DoS vulnerabilities allow attackers to trigger such a crash or crippling of the service by using a flaw either in the application code or from the use of open source libraries.

    +

    Two common types of DoS vulnerabilities:

    +
      +
    • High CPU/Memory Consumption- An attacker sending crafted requests that could cause the system to take a disproportionate amount of time to process. For example, commons-fileupload:commons-fileupload.

      +
    • +
    • Crash - An attacker sending crafted requests that could cause the system to crash. For Example, npm ws package

      +
    • +
    +

    Remediation

    +

    Upgrade github.com/go-git/go-git/v5/plumbing to version 5.11.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-6237

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|ghcr.io/dexidp/dex@v2.37.0 and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|ghcr.io/dexidp/dex@v2.37.0 + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    This vulnerability has not been analyzed by NVD yet.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r4 or higher.

    + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.9.9/haproxy_2.6.14-alpine.html b/docs/snyk/v2.9.9/haproxy_2.6.14-alpine.html new file mode 100644 index 0000000000000..22d46e565dc6f --- /dev/null +++ b/docs/snyk/v2.9.9/haproxy_2.6.14-alpine.html @@ -0,0 +1,1376 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    March 24th 2024, 12:17:53 am (UTC+00:00)

    +
    +
    + Scanned the following path: +
      +
    • haproxy:2.6.14-alpine (apk)
    • +
    +
    + +
    +
    5 known vulnerabilities
    +
    45 vulnerable dependency paths
    +
    18 dependencies
    +
    +
    +
    +
    +
    + + + + + + + +
    Project docker-image|haproxy
    Path haproxy:2.6.14-alpine
    Package Manager apk
    +
    +
    +
    +
    +

    CVE-2023-5363

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: A bug has been identified in the processing of key and + initialisation vector (IV) lengths. This can lead to potential truncation + or overruns during the initialisation of some symmetric ciphers.

    +

    Impact summary: A truncation in the IV can result in non-uniqueness, + which could result in loss of confidentiality for some cipher modes.

    +

    When calling EVP_EncryptInit_ex2(), EVP_DecryptInit_ex2() or + EVP_CipherInit_ex2() the provided OSSL_PARAM array is processed after + the key and IV have been established. Any alterations to the key length, + via the "keylen" parameter or the IV length, via the "ivlen" parameter, + within the OSSL_PARAM array will not take effect as intended, potentially + causing truncation or overreading of these values. The following ciphers + and cipher modes are impacted: RC2, RC4, RC5, CCM, GCM and OCB.

    +

    For the CCM, GCM and OCB cipher modes, truncation of the IV can result in + loss of confidentiality. For example, when following NIST's SP 800-38D + section 8.2.1 guidance for constructing a deterministic IV for AES in + GCM mode, truncation of the counter portion could lead to IV reuse.

    +

    Both truncations and overruns of the key and overruns of the IV will + produce incorrect results and could, in some cases, trigger a memory + exception. However, these issues are not currently assessed as security + critical.

    +

    Changing the key and/or IV lengths is not considered to be a common operation + and the vulnerable API was recently introduced. Furthermore it is likely that + application developers will have spotted this problem during testing since + decryption would fail unless both peers in the communication were similarly + vulnerable. For these reasons we expect the probability of an application being + vulnerable to this to be quite low. However if an application is vulnerable then + this issue is considered very serious. For these reasons we have assessed this + issue as Moderate severity overall.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this because + the issue lies outside of the FIPS provider boundary.

    +

    OpenSSL 3.1 and 3.0 are vulnerable to this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Check for Unusual or Exceptional Conditions

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Generating excessively long X9.42 DH keys or checking + excessively long X9.42 DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_generate_key() to + generate an X9.42 DH key may experience long delays. Likewise, applications + that use DH_check_pub_key(), DH_check_pub_key_ex() or EVP_PKEY_public_check() + to check an X9.42 DH key or X9.42 DH parameters may experience long delays. + Where the key or parameters that are being checked have been obtained from + an untrusted source this may lead to a Denial of Service.

    +

    While DH_check() performs all the necessary checks (as of CVE-2023-3817), + DH_check_pub_key() doesn't make any of these checks, and is therefore + vulnerable for excessively large P and Q parameters.

    +

    Likewise, while DH_generate_key() performs a check for an excessively large + P, it doesn't check for an excessively large Q.

    +

    An application that calls DH_generate_key() or DH_check_pub_key() and + supplies a key or parameters obtained from an untrusted source could be + vulnerable to a Denial of Service attack.

    +

    DH_generate_key() and DH_check_pub_key() are also called by a number of + other OpenSSL functions. An application calling any of those other + functions may similarly be affected. The other functions affected by this + are DH_check_pub_key_ex(), EVP_PKEY_public_check(), and EVP_PKEY_generate().

    +

    Also vulnerable are the OpenSSL pkey command line application when using the + "-pubcheck" option, as well as the OpenSSL genpkey command line application.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

    +

    Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

    +

    The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

    +

    The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

    +

    The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-0727

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

    +

    Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

    +

    A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

    +

    OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

    +

    We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

    +

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r5 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-6237

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|haproxy@2.6.14-alpine and openssl/libcrypto3@3.1.2-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + openssl/libcrypto3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + .haproxy-rundeps@20230809.001942 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    • + Introduced through: + docker-image|haproxy@2.6.14-alpine + + busybox/ssl_client@1.36.1-r2 + + openssl/libssl3@3.1.2-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    This vulnerability has not been analyzed by NVD yet.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r4 or higher.

    + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.9.9/quay.io_argoproj_argocd_v2.9.9.html b/docs/snyk/v2.9.9/quay.io_argoproj_argocd_v2.9.9.html new file mode 100644 index 0000000000000..704d480d51ff7 --- /dev/null +++ b/docs/snyk/v2.9.9/quay.io_argoproj_argocd_v2.9.9.html @@ -0,0 +1,4812 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    March 24th 2024, 12:18:09 am (UTC+00:00)

    +
    +
    + Scanned the following paths: +
      +
    • quay.io/argoproj/argocd:v2.9.9/argoproj/argocd/Dockerfile (deb)
    • +
    • quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2//usr/local/bin/argocd (gomodules)
    • +
    • quay.io/argoproj/argocd:v2.9.9//usr/local/bin/kustomize (gomodules)
    • +
    • quay.io/argoproj/argocd:v2.9.9/helm/v3//usr/local/bin/helm (gomodules)
    • +
    • quay.io/argoproj/argocd:v2.9.9/git-lfs/git-lfs//usr/bin/git-lfs (gomodules)
    • +
    +
    + +
    +
    36 known vulnerabilities
    +
    179 vulnerable dependency paths
    +
    2189 dependencies
    +
    +
    +
    +
    + +
    +
    +
    +

    Denial of Service (DoS)

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/grpc +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/grpc@v1.56.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + google.golang.org/grpc@v1.56.2 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    google.golang.org/grpc is a Go implementation of gRPC

    +

    Affected versions of this package are vulnerable to Denial of Service (DoS) in the implementation of the HTTP/2 protocol. An attacker can cause a denial of service (including via DDoS) by rapidly resetting many streams through request cancellation.

    +

    Remediation

    +

    Upgrade google.golang.org/grpc to version 1.56.3, 1.57.1, 1.58.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2020-22916

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + xz-utils/liblzma5 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and xz-utils/liblzma5@5.2.5-2ubuntu1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + xz-utils/liblzma5@5.2.5-2ubuntu1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream xz-utils package and not the xz-utils package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    An issue discovered in XZ 5.2.5 allows attackers to cause a denial of service via decompression of a crafted file. NOTE: the vendor disputes the claims of "endless output" and "denial of service" because decompression of the 17,486 bytes always results in 114,881,179 bytes, which is often a reasonable size increase.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 xz-utils.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-51767

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + openssh/openssh-client +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and openssh/openssh-client@1:8.9p1-3ubuntu0.6 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssh package and not the openssh package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    OpenSSH through 9.6, when common types of DRAM are used, might allow row hammer attacks (for authentication bypass) because the integer value of authenticated in mm_answer_authpassword does not resist flips of a single bit. NOTE: this is applicable to a certain threat model of attacker-victim co-location in which the attacker has user privileges.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 openssh.

    +

    References

    + + +
    + + + +
    +
    +

    Information Exposure

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + libgcrypt20 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and libgcrypt20@1.9.4-3ubuntu3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + apt@2.4.11 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + libgcrypt20@1.9.4-3ubuntu3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream libgcrypt20 package and not the libgcrypt20 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A timing-based side-channel flaw was found in libgcrypt's RSA implementation. This issue may allow a remote attacker to initiate a Bleichenbacher-style attack, which can lead to the decryption of RSA ciphertexts.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 libgcrypt20.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-26461

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libkrb5support0@1.19.2-2ubuntu0.3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    Kerberos 5 (aka krb5) 1.21.2 contains a memory leak vulnerability in /krb5/src/lib/gssapi/krb5/k5sealv3.c.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-26462

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libkrb5support0@1.19.2-2ubuntu0.3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    Kerberos 5 (aka krb5) 1.21.2 contains a memory leak vulnerability in /krb5/src/kdc/ndr.c.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-26458

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libkrb5support0@1.19.2-2ubuntu0.3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    Kerberos 5 (aka krb5) 1.21.2 contains a memory leak in /krb5/src/lib/rpc/pmap_rmt.c.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    References

    + + +
    + + + +
    +
    +

    LGPL-3.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + gopkg.in/retry.v1 +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and gopkg.in/retry.v1@v1.0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + gopkg.in/retry.v1@v1.0.3 + + + +
    • +
    + +
    + +
    + +

    LGPL-3.0 license

    + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/internal/encoding/json +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/protobuf/internal/encoding/json@v1.31.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + google.golang.org/protobuf/internal/encoding/json@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/internal/encoding/json to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Stack-based Buffer Overflow

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Stack-based Buffer Overflow when processing input that uses pathologically deep nesting.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.32.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Infinite loop

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + google.golang.org/protobuf/encoding/protojson +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and google.golang.org/protobuf/encoding/protojson@v1.31.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + google.golang.org/protobuf/encoding/protojson@v1.31.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Infinite loop via the protojson.Unmarshal function. An attacker can cause a denial of service condition by unmarshaling certain forms of invalid JSON.

    +

    Note:

    +

    This condition can occur when unmarshaling into a message which contains a google.protobuf.Any value, or when the UnmarshalOptions.DiscardUnknown option is set.

    +

    Remediation

    +

    Upgrade google.golang.org/protobuf/encoding/protojson to version 1.33.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Authentication Bypass by Capture-replay

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + golang.org/x/crypto/ssh +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and golang.org/x/crypto/ssh@v0.16.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + golang.org/x/crypto/ssh@v0.16.0 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    golang.org/x/crypto/ssh is a SSH client and server

    +

    Affected versions of this package are vulnerable to Authentication Bypass by Capture-replay during the establishment of the secure channel. An attacker can manipulate handshake sequence numbers to delete messages sent immediately after the channel is established.

    +

    Note:

    +
      +
    1. Sequence numbers are only validated once the channel is established and arbitrary messages are allowed during the handshake, allowing them to manipulate the sequence numbers.

      +
    2. +
    3. The potential consequences of the general Terrapin attack are dependent on the messages exchanged after the handshake concludes. If you are using a custom SSH service and do not resort to the authentication protocol, you should check that dropping the first few messages of a connection does not yield security risks.

      +
    4. +
    +

    Impact:

    +

    While cryptographically novel, there is no discernable impact on the integrity of SSH traffic beyond giving the attacker the ability to delete the message that enables some features related to keystroke timing obfuscation. To successfully carry out the exploitation, the connection needs to be protected using either the ChaCha20-Poly1305 or CBC with Encrypt-then-MAC encryption methods. The attacker must also be able to intercept and modify the connection's traffic.

    +

    Workaround

    +

    Temporarily disable the affected chacha20-poly1305@openssh.com encryption and *-etm@openssh.com MAC algorithms in the affected configuration, and use unaffected algorithms like AES-GCM instead.

    +

    Remediation

    +

    Upgrade golang.org/x/crypto/ssh to version 0.17.0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Information Exposure

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gnutls28/libgnutls30 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + apt@2.4.11 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + openldap/libldap-2.5-0@2.5.17+dfsg-0ubuntu0.22.04.1 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + rtmpdump/librtmp1@2.4+20151223.gitfa8646d.1-2build4 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream gnutls28 package and not the gnutls28 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A flaw was found in GnuTLS. The Minerva attack is a cryptographic vulnerability that exploits deterministic behavior in systems like GnuTLS, leading to side-channel leaks. In specific scenarios, such as when using the GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE flag, it can result in a noticeable step in nonce size from 513 to 512 bits, exposing a potential timing side-channel.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 gnutls28.

    +

    References

    + + +
    + + + +
    +
    +

    Uncaught Exception

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gnutls28/libgnutls30 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + apt@2.4.11 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + openldap/libldap-2.5-0@2.5.17+dfsg-0ubuntu0.22.04.1 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + rtmpdump/librtmp1@2.4+20151223.gitfa8646d.1-2build4 + + gnutls28/libgnutls30@3.7.3-4ubuntu1.4 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream gnutls28 package and not the gnutls28 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A flaw has been discovered in GnuTLS where an application crash can be induced when attempting to verify a specially crafted .pem bundle using the "certtool --verify-chain" command.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 gnutls28.

    +

    References

    + + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/r3labs/diff +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/r3labs/diff@v1.1.0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/r3labs/diff@v1.1.0 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-version +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-version@v1.2.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/hashicorp/go-version@v1.2.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-retryablehttp +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-retryablehttp@v0.7.4 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/hashicorp/go-retryablehttp@v0.7.4 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/helm/v3 /usr/local/bin/helm +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-multierror +
    • + +
    • Introduced through: + + helm.sh/helm/v3@* and github.com/hashicorp/go-multierror@v1.1.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + helm.sh/helm/v3@* + + github.com/hashicorp/go-multierror@v1.1.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/hashicorp/go-cleanhttp +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/hashicorp/go-cleanhttp@v0.5.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/hashicorp/go-cleanhttp@v0.5.2 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    MPL-2.0 license

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Module: + + github.com/gosimple/slug +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/gosimple/slug@v1.13.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/gosimple/slug@v1.13.1 + + + +
    • +
    + +
    + +
    + +

    MPL-2.0 license

    + +
    + + + +
    +
    +

    Improper Handling of Highly Compressed Data (Data Amplification)

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argo-cd/v2 /usr/local/bin/argocd +
    • +
    • + Package Manager: golang +
    • +
    • + Vulnerable module: + + github.com/go-jose/go-jose/v3 +
    • + +
    • Introduced through: + + github.com/argoproj/argo-cd/v2@* and github.com/go-jose/go-jose/v3@v3.0.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + github.com/argoproj/argo-cd/v2@* + + github.com/go-jose/go-jose/v3@v3.0.1 + + + +
    • +
    + +
    + +
    + +

    Overview

    +

    Affected versions of this package are vulnerable to Improper Handling of Highly Compressed Data (Data Amplification). An attacker could send a JWE containing compressed data that, when decompressed by Decrypt or DecryptMulti, would use large amounts of memory and CPU.

    +

    Remediation

    +

    Upgrade github.com/go-jose/go-jose/v3 to version 3.0.3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + bash +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and bash@5.1-6ubuntu1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + bash@5.1-6ubuntu1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream bash package and not the bash package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A flaw was found in the bash package, where a heap-buffer overflow can occur in valid parameter_transform. This issue may lead to memory problems.

    +

    Remediation

    +

    Upgrade Ubuntu:22.04 bash to version 5.1-6ubuntu1.1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-7008

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + systemd/libsystemd0 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and systemd/libsystemd0@249.11-0ubuntu3.12 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + apt@2.4.11 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + procps/libprocps8@2:3.3.17-6ubuntu2.1 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + util-linux@2.37.2-4ubuntu3 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + util-linux/bsdutils@1:2.37.2-4ubuntu3 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + systemd/libsystemd0@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + systemd/libudev1@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + libfido2/libfido2-1@1.10.0-1 + + systemd/libudev1@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + util-linux@2.37.2-4ubuntu3 + + systemd/libudev1@249.11-0ubuntu3.12 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + systemd/libudev1@249.11-0ubuntu3.12 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream systemd package and not the systemd package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A vulnerability was found in systemd-resolved. This issue may allow systemd-resolved to accept records of DNSSEC-signed domains even when they have no signature, allowing man-in-the-middles (or the upstream DNS resolver) to manipulate records.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 systemd.

    +

    References

    + + +
    + + + +
    +
    +

    Arbitrary Code Injection

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + shadow/passwd +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and shadow/passwd@1:4.8.1-2ubuntu2.2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + shadow/login@1:4.8.1-2ubuntu2.2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream shadow package and not the shadow package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    In Shadow 4.13, it is possible to inject control characters into fields provided to the SUID program chfn (change finger). Although it is not possible to exploit this directly (e.g., adding a new user fails because \n is in the block list), it is possible to misrepresent the /etc/passwd file when viewed. Use of \r manipulations and Unicode characters to work around blocking of the : character make it possible to give the impression that a new user has been added. In other words, an adversary may be able to convince a system administrator to take the system offline (an indirect, social-engineered denial of service) by demonstrating that "cat /etc/passwd" shows a rogue user account.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 shadow.

    +

    References

    + + +
    + + + +
    +
    +

    Uncontrolled Recursion

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + pcre3/libpcre3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + grep@3.7-1build1 + + pcre3/libpcre3@2:8.39-13ubuntu0.22.04.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream pcre3 package and not the pcre3 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    In PCRE 8.41, the OP_KETRMAX feature in the match function in pcre_exec.c allows stack exhaustion (uncontrolled recursion) when processing a crafted regular expression.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 pcre3.

    +

    References

    + + +
    + + + +
    +
    +

    Release of Invalid Pointer or Reference

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + patch +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and patch@2.7.6-7build2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + patch@2.7.6-7build2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream patch package and not the patch package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    An Invalid Pointer vulnerability exists in GNU patch 2.7 via the another_hunk function, which causes a Denial of Service.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 patch.

    +

    References

    + + +
    + + + +
    +
    +

    Double Free

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + patch +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and patch@2.7.6-7build2 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + patch@2.7.6-7build2 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream patch package and not the patch package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A double free exists in the another_hunk function in pch.c in GNU patch through 2.7.6.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 patch.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-50495

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + ncurses/libtinfo6 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and ncurses/libtinfo6@6.3-2ubuntu0.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + bash@5.1-6ubuntu1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + less@590-1ubuntu0.22.04.2 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + libedit/libedit2@3.1-20210910-1build1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/ncurses-bin@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + util-linux@2.37.2-4ubuntu3 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + readline/libreadline8@8.1.2-1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + pinentry/pinentry-curses@1.1.1-1build2 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + pinentry/pinentry-curses@1.1.1-1build2 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/ncurses-base@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/ncurses-bin@6.3-2ubuntu0.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream ncurses package and not the ncurses package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    NCurse v6.4-20230418 was discovered to contain a segmentation fault via the component _nc_wrap_entry().

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 ncurses.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-45918

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + ncurses/libtinfo6 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and ncurses/libtinfo6@6.3-2ubuntu0.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + bash@5.1-6ubuntu1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + less@590-1ubuntu0.22.04.2 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + libedit/libedit2@3.1-20210910-1build1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/ncurses-bin@6.3-2ubuntu0.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + util-linux@2.37.2-4ubuntu3 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + readline/libreadline8@8.1.2-1 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + pinentry/pinentry-curses@1.1.1-1build2 + + ncurses/libtinfo6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + pinentry/pinentry-curses@1.1.1-1build2 + + ncurses/libncursesw6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + procps@2:3.3.17-6ubuntu2.1 + + ncurses/libncurses6@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/ncurses-base@6.3-2ubuntu0.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + ncurses/ncurses-bin@6.3-2ubuntu0.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream ncurses package and not the ncurses package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    ncurses 6.4-20230610 has a NULL pointer dereference in tgetstr in tinfo/lib_termcap.c.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 ncurses.

    +

    References

    + + +
    + + + +
    +
    +

    Resource Exhaustion

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + libzstd/libzstd1 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and libzstd/libzstd1@1.4.8+dfsg-3build1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + libzstd/libzstd1@1.4.8+dfsg-3build1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream libzstd package and not the libzstd package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    A vulnerability was found in zstd v1.4.10, where an attacker can supply empty string as an argument to the command line tool to cause buffer overrun.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 libzstd.

    +

    References

    + + +
    + + + +
    +
    +

    Integer Overflow or Wraparound

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + krb5/libk5crypto3 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and krb5/libk5crypto3@1.19.2-2ubuntu0.3 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + krb5/libk5crypto3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + krb5/libkrb5-3@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + openssh/openssh-client@1:8.9p1-3ubuntu0.6 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + curl/libcurl3-gnutls@7.81.0-1ubuntu1.15 + + libssh/libssh-4@0.9.6-2ubuntu0.22.04.3 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + adduser@3.118ubuntu5 + + shadow/passwd@1:4.8.1-2ubuntu2.2 + + pam/libpam-modules@1.4.0-11ubuntu2.4 + + libnsl/libnsl2@1.3.0-2build2 + + libtirpc/libtirpc3@1.3.2-2ubuntu0.1 + + krb5/libgssapi-krb5-2@1.19.2-2ubuntu0.3 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + krb5/libkrb5support0@1.19.2-2ubuntu0.3 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream krb5 package and not the krb5 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    An issue was discovered in MIT Kerberos 5 (aka krb5) through 1.16. There is a variable "dbentry->n_key_data" in kadmin/dbutil/dump.c that can store 16-bit data but unknowingly the developer has assigned a "u4" variable to it, which is for 32-bit data. An attacker can use this vulnerability to affect other artifacts of the database as we know that a Kerberos database dump file contains trusted data.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 krb5.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gnupg2/gpgv +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and gnupg2/gpgv@2.2.27-3ubuntu2.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + apt@2.4.11 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpgv@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + gnupg2/gpgconf@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + gnupg2/dirmngr@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gnupg-l10n@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gnupg-utils@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + gnupg2/gpg@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + gnupg2/gpg-agent@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-client@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpg-wks-server@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + gnupg2/gpgsm@2.2.27-3ubuntu2.1 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gnupg2/gnupg@2.2.27-3ubuntu2.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream gnupg2 package and not the gnupg2 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    GnuPG can be made to spin on a relatively small input by (for example) crafting a public key with thousands of signatures attached, compressed down to just a few KB.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 gnupg2.

    +

    References

    + + +
    + + + +
    +
    +

    Allocation of Resources Without Limits or Throttling

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + glibc/libc-bin +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and glibc/libc-bin@2.35-0ubuntu3.6 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + glibc/libc-bin@2.35-0ubuntu3.6 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + glibc/libc6@2.35-0ubuntu3.6 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream glibc package and not the glibc package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    sha256crypt and sha512crypt through 0.6 allow attackers to cause a denial of service (CPU consumption) because the algorithm's runtime is proportional to the square of the length of the password.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 glibc.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Input Validation

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + git/git-man +
    • + +
    • Introduced through: + + + docker-image|quay.io/argoproj/argocd@v2.9.9, git@1:2.34.1-1ubuntu1.10 and others +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + git/git-man@1:2.34.1-1ubuntu1.10 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git@1:2.34.1-1ubuntu1.10 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + git-lfs@3.0.2-1ubuntu0.2 + + git@1:2.34.1-1ubuntu1.10 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream git package and not the git package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    GIT version 2.15.1 and earlier contains a Input Validation Error vulnerability in Client that can result in problems including messing up terminal configuration to RCE. This attack appear to be exploitable via The user must interact with a malicious git server, (or have their traffic modified in a MITM attack).

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 git.

    +

    References

    + + +
    + + + +
    +
    +

    Uncontrolled Recursion

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + gcc-12/libstdc++6 +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + apt@2.4.11 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + apt@2.4.11 + + apt/libapt-pkg6.0@2.4.11 + + gcc-12/libstdc++6@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gcc-12/gcc-12-base@12.3.0-1ubuntu1~22.04 + + + +
    • +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + gcc-12/libgcc-s1@12.3.0-1ubuntu1~22.04 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream gcc-12 package and not the gcc-12 package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    libiberty/rust-demangle.c in GNU GCC 11.2 allows stack consumption in demangle_const, as demonstrated by nm-new.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 gcc-12.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Input Validation

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Manifest file: quay.io/argoproj/argocd:v2.9.9/argoproj/argocd Dockerfile +
    • +
    • + Package Manager: ubuntu:22.04 +
    • +
    • + Vulnerable module: + + coreutils +
    • + +
    • Introduced through: + + docker-image|quay.io/argoproj/argocd@v2.9.9 and coreutils@8.32-4.1ubuntu1.1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|quay.io/argoproj/argocd@v2.9.9 + + coreutils@8.32-4.1ubuntu1.1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream coreutils package and not the coreutils package as distributed by Ubuntu. + See How to fix? for Ubuntu:22.04 relevant fixed versions and status.

    +

    chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.

    +

    Remediation

    +

    There is no fixed version for Ubuntu:22.04 coreutils.

    +

    References

    + + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/snyk/v2.9.9/redis_7.0.11-alpine.html b/docs/snyk/v2.9.9/redis_7.0.11-alpine.html new file mode 100644 index 0000000000000..55538b9b23982 --- /dev/null +++ b/docs/snyk/v2.9.9/redis_7.0.11-alpine.html @@ -0,0 +1,2032 @@ + + + + + + + + + Snyk test report + + + + + + + + + +
    +
    +
    +
    + + + Snyk - Open Source Security + + + + + + + +
    +

    Snyk test report

    + +

    March 24th 2024, 12:18:14 am (UTC+00:00)

    +
    +
    + Scanned the following path: +
      +
    • redis:7.0.11-alpine (apk)
    • +
    +
    + +
    +
    9 known vulnerabilities
    +
    77 vulnerable dependency paths
    +
    18 dependencies
    +
    +
    +
    +
    +
    + + + + + + + +
    Project docker-image|redis
    Path redis:7.0.11-alpine
    Package Manager apk
    +
    +
    +
    +
    +

    Out-of-bounds Write

    +
    + +
    + critical severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + busybox/busybox +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and busybox/busybox@1.36.1-r0 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/busybox@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + alpine-baselayout/alpine-baselayout@3.4.3-r1 + + busybox/busybox-binsh@1.36.1-r0 + + busybox/busybox@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/busybox-binsh@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + alpine-baselayout/alpine-baselayout@3.4.3-r1 + + busybox/busybox-binsh@1.36.1-r0 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream busybox package and not the busybox package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    There is a stack overflow vulnerability in ash.c:6030 in busybox before 1.35. In the environment of Internet of Vehicles, this vulnerability can be executed from command to arbitrary code execution.

    +

    Remediation

    +

    Upgrade Alpine:3.18 busybox to version 1.36.1-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-5363

    +
    + +
    + high severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: A bug has been identified in the processing of key and + initialisation vector (IV) lengths. This can lead to potential truncation + or overruns during the initialisation of some symmetric ciphers.

    +

    Impact summary: A truncation in the IV can result in non-uniqueness, + which could result in loss of confidentiality for some cipher modes.

    +

    When calling EVP_EncryptInit_ex2(), EVP_DecryptInit_ex2() or + EVP_CipherInit_ex2() the provided OSSL_PARAM array is processed after + the key and IV have been established. Any alterations to the key length, + via the "keylen" parameter or the IV length, via the "ivlen" parameter, + within the OSSL_PARAM array will not take effect as intended, potentially + causing truncation or overreading of these values. The following ciphers + and cipher modes are impacted: RC2, RC4, RC5, CCM, GCM and OCB.

    +

    For the CCM, GCM and OCB cipher modes, truncation of the IV can result in + loss of confidentiality. For example, when following NIST's SP 800-38D + section 8.2.1 guidance for constructing a deterministic IV for AES in + GCM mode, truncation of the counter portion could lead to IV reuse.

    +

    Both truncations and overruns of the key and overruns of the IV will + produce incorrect results and could, in some cases, trigger a memory + exception. However, these issues are not currently assessed as security + critical.

    +

    Changing the key and/or IV lengths is not considered to be a common operation + and the vulnerable API was recently introduced. Furthermore it is likely that + application developers will have spotted this problem during testing since + decryption would fail unless both peers in the communication were similarly + vulnerable. For these reasons we expect the probability of an application being + vulnerable to this to be quite low. However if an application is vulnerable then + this issue is considered very serious. For these reasons we have assessed this + issue as Moderate severity overall.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this because + the issue lies outside of the FIPS provider boundary.

    +

    OpenSSL 3.1 and 3.0 are vulnerable to this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Authentication

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The AES-SIV cipher implementation contains a bug that causes + it to ignore empty associated data entries which are unauthenticated as + a consequence.

    +

    Impact summary: Applications that use the AES-SIV algorithm and want to + authenticate empty data entries as associated data can be mislead by removing + adding or reordering such empty entries as these are ignored by the OpenSSL + implementation. We are currently unaware of any such applications.

    +

    The AES-SIV algorithm allows for authentication of multiple associated + data entries along with the encryption. To authenticate empty data the + application has to call EVP_EncryptUpdate() (or EVP_CipherUpdate()) with + NULL pointer as the output buffer and 0 as the input buffer length. + The AES-SIV implementation in OpenSSL just returns success for such a call + instead of performing the associated data authentication operation. + The empty data thus will not be authenticated.

    +

    As this issue does not affect non-empty associated data authentication and + we expect it to be rare for an application to use empty associated data + entries this is qualified as Low severity issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.1-r2 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Inefficient Regular Expression Complexity

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Checking excessively long DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_check(), DH_check_ex() + or EVP_PKEY_param_check() to check a DH key or DH parameters may experience long + delays. Where the key or parameters that are being checked have been obtained + from an untrusted source this may lead to a Denial of Service.

    +

    The function DH_check() performs various checks on DH parameters. One of those + checks confirms that the modulus ('p' parameter) is not too large. Trying to use + a very large modulus is slow and OpenSSL will not normally use a modulus which + is over 10,000 bits in length.

    +

    However the DH_check() function checks numerous aspects of the key or parameters + that have been supplied. Some of those checks use the supplied modulus value + even if it has already been found to be too large.

    +

    An application that calls DH_check() and supplies a key or parameters obtained + from an untrusted source could be vulernable to a Denial of Service attack.

    +

    The function DH_check() is itself called by a number of other OpenSSL functions. + An application calling any of those other functions may similarly be affected. + The other functions affected by this are DH_check_ex() and + EVP_PKEY_param_check().

    +

    Also vulnerable are the OpenSSL dhparam and pkeyparam command line applications + when using the '-check' option.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue. + The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.1-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Excessive Iteration

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Checking excessively long DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_check(), DH_check_ex() + or EVP_PKEY_param_check() to check a DH key or DH parameters may experience long + delays. Where the key or parameters that are being checked have been obtained + from an untrusted source this may lead to a Denial of Service.

    +

    The function DH_check() performs various checks on DH parameters. After fixing + CVE-2023-3446 it was discovered that a large q parameter value can also trigger + an overly long computation during some of these checks. A correct q value, + if present, cannot be larger than the modulus p parameter, thus it is + unnecessary to perform these checks if q is larger than p.

    +

    An application that calls DH_check() and supplies a key or parameters obtained + from an untrusted source could be vulnerable to a Denial of Service attack.

    +

    The function DH_check() is itself called by a number of other OpenSSL functions. + An application calling any of those other functions may similarly be affected. + The other functions affected by this are DH_check_ex() and + EVP_PKEY_param_check().

    +

    Also vulnerable are the OpenSSL dhparam and pkeyparam command line applications + when using the "-check" option.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.2-r0 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Improper Check for Unusual or Exceptional Conditions

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Generating excessively long X9.42 DH keys or checking + excessively long X9.42 DH keys or parameters may be very slow.

    +

    Impact summary: Applications that use the functions DH_generate_key() to + generate an X9.42 DH key may experience long delays. Likewise, applications + that use DH_check_pub_key(), DH_check_pub_key_ex() or EVP_PKEY_public_check() + to check an X9.42 DH key or X9.42 DH parameters may experience long delays. + Where the key or parameters that are being checked have been obtained from + an untrusted source this may lead to a Denial of Service.

    +

    While DH_check() performs all the necessary checks (as of CVE-2023-3817), + DH_check_pub_key() doesn't make any of these checks, and is therefore + vulnerable for excessively large P and Q parameters.

    +

    Likewise, while DH_generate_key() performs a check for an excessively large + P, it doesn't check for an excessively large Q.

    +

    An application that calls DH_generate_key() or DH_check_pub_key() and + supplies a key or parameters obtained from an untrusted source could be + vulnerable to a Denial of Service attack.

    +

    DH_generate_key() and DH_check_pub_key() are also called by a number of + other OpenSSL functions. An application calling any of those other + functions may similarly be affected. The other functions affected by this + are DH_check_pub_key_ex(), EVP_PKEY_public_check(), and EVP_PKEY_generate().

    +

    Also vulnerable are the OpenSSL pkey command line application when using the + "-pubcheck" option, as well as the OpenSSL genpkey command line application.

    +

    The OpenSSL SSL/TLS implementation is not affected by this issue.

    +

    The OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r1 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    Out-of-bounds Write

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: The POLY1305 MAC (message authentication code) implementation + contains a bug that might corrupt the internal state of applications running + on PowerPC CPU based platforms if the CPU provides vector instructions.

    +

    Impact summary: If an attacker can influence whether the POLY1305 MAC + algorithm is used, the application state might be corrupted with various + application dependent consequences.

    +

    The POLY1305 MAC (message authentication code) implementation in OpenSSL for + PowerPC CPUs restores the contents of vector registers in a different order + than they are saved. Thus the contents of some of these vector registers + are corrupted when returning to the caller. The vulnerable code is used only + on newer PowerPC processors supporting the PowerISA 2.07 instructions.

    +

    The consequences of this kind of internal application state corruption can + be various - from no consequences, if the calling application does not + depend on the contents of non-volatile XMM registers at all, to the worst + consequences, where the attacker could get complete control of the application + process. However unless the compiler uses the vector registers for storing + pointers, the most likely consequence, if any, would be an incorrect result + of some application dependent calculations or a crash leading to a denial of + service.

    +

    The POLY1305 MAC algorithm is most frequently used as part of the + CHACHA20-POLY1305 AEAD (authenticated encryption with associated data) + algorithm. The most common usage of this AEAD cipher is with TLS protocol + versions 1.2 and 1.3. If this cipher is enabled on the server a malicious + client can influence whether this AEAD cipher is used. This implies that + TLS server applications using OpenSSL can be potentially impacted. However + we are currently not aware of any concrete application that would be affected + by this issue therefore we consider this a Low severity security issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r3 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2024-0727

    +
    + +
    + medium severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    Note: Versions mentioned in the description apply only to the upstream openssl package and not the openssl package as distributed by Alpine. + See How to fix? for Alpine:3.18 relevant fixed versions and status.

    +

    Issue summary: Processing a maliciously formatted PKCS12 file may lead OpenSSL + to crash leading to a potential Denial of Service attack

    +

    Impact summary: Applications loading files in the PKCS12 format from untrusted + sources might terminate abruptly.

    +

    A file in PKCS12 format can contain certificates and keys and may come from an + untrusted source. The PKCS12 specification allows certain fields to be NULL, but + OpenSSL does not correctly check for this case. This can lead to a NULL pointer + dereference that results in OpenSSL crashing. If an application processes PKCS12 + files from an untrusted source using the OpenSSL APIs then that application will + be vulnerable to this issue.

    +

    OpenSSL APIs that are vulnerable to this are: PKCS12_parse(), + PKCS12_unpack_p7data(), PKCS12_unpack_p7encdata(), PKCS12_unpack_authsafes() + and PKCS12_newpass().

    +

    We have also fixed a similar issue in SMIME_write_PKCS7(). However since this + function is related to writing data we do not consider it security significant.

    +

    The FIPS modules in 3.2, 3.1 and 3.0 are not affected by this issue.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r5 or higher.

    +

    References

    + + +
    + + + +
    +
    +

    CVE-2023-6237

    +
    + +
    + low severity +
    + +
    + +
      +
    • + Package Manager: alpine:3.18 +
    • +
    • + Vulnerable module: + + openssl/libcrypto3 +
    • + +
    • Introduced through: + + docker-image|redis@7.0.11-alpine and openssl/libcrypto3@3.1.1-r1 + +
    • +
    + +
    + + +

    Detailed paths

    + +
      +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + openssl/libcrypto3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + .redis-rundeps@20230614.215749 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + apk-tools/apk-tools@2.14.0-r2 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    • + Introduced through: + docker-image|redis@7.0.11-alpine + + busybox/ssl_client@1.36.1-r0 + + openssl/libssl3@3.1.1-r1 + + + +
    • +
    + +
    + +
    + +

    NVD Description

    +

    This vulnerability has not been analyzed by NVD yet.

    +

    Remediation

    +

    Upgrade Alpine:3.18 openssl to version 3.1.4-r4 or higher.

    + +
    + + + +
    +
    +
    +
    + + + diff --git a/docs/understand_the_basics.md b/docs/understand_the_basics.md index 15918c74a5dc1..0fd7218d5890b 100644 --- a/docs/understand_the_basics.md +++ b/docs/understand_the_basics.md @@ -11,5 +11,6 @@ Before effectively using Argo CD, it is necessary to understand the underlying t * Depending on how you plan to template your applications: * [Kustomize](https://kustomize.io) * [Helm](https://helm.sh) -* If you're integrating with Jenkins: - * [Jenkins User Guide](https://jenkins.io) +* If you're integrating with a CI tool: + * [GitHub Actions Documentation](https://docs.github.com/en/actions) + * [Jenkins User Guide](https://www.jenkins.io/doc/book/) diff --git a/docs/user-guide/annotations-and-labels.md b/docs/user-guide/annotations-and-labels.md new file mode 100644 index 0000000000000..032824c8708f3 --- /dev/null +++ b/docs/user-guide/annotations-and-labels.md @@ -0,0 +1,26 @@ +# Annotations and Labels used by Argo CD + +## Annotations + +| Annotation key | Target resource(es) | Possible values | Description | +|--------------------------------------------|---------------------|---------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| argocd.argoproj.io/application-set-refresh | ApplicationSet | `"true"` | Added when an ApplicationSet is requested to be refreshed by a webhook. The ApplicationSet controller will remove this annotation at the end of reconciliation. | +| argocd.argoproj.io/compare-options | any | [see compare options docs](compare-options.md) | Configures how an app's current state is compared to its desired state. | +| argocd.argoproj.io/hook | any | [see resource hooks docs](resource_hooks.md) | Used to configure [resource hooks](resource_hooks.md). | +| argocd.argoproj.io/hook-delete-policy | any | [see resource hooks docs](resource_hooks.md#hook-deletion-policies) | Used to set a [resource hook's deletion policy](resource_hooks.md#hook-deletion-policies). | +| argocd.argoproj.io/manifest-generate-paths | Application | [see scaling docs](../operator-manual/high_availability.md#webhook-and-manifest-paths-annotation) | Used to avoid unnecessary Application refreshes, especially in mono-repos. | +| argocd.argoproj.io/refresh | Application | `normal`, `hard` | Indicates that app needs to be refreshed. Removed by application controller after app is refreshed. Value `"hard"` means manifest cache and target cluster state cache should be invalidated before refresh. | +| argocd.argoproj.io/skip-reconcile | Application | `"true"` | Indicates to the Argo CD application controller that the Application should not be reconciled. See the [skip reconcile documentation](skip_reconcile.md) for use cases. | +| argocd.argoproj.io/sync-options | any | [see sync options docs](sync-options.md) | Provides a variety of settings to determine how an Application's resources are synced. | +| argocd.argoproj.io/sync-wave | any | [see sync waves docs](sync-waves.md) | | +| argocd.argoproj.io/tracking-id | any | any | Used by Argo CD to track resources it manages. See [resource tracking docs](resource_tracking.md) for details. | +| link.argocd.argoproj.io/{some link name} | any | An http(s) URL | Adds a link to the Argo CD UI for the resource. See [external URL docs](external-url.md) for details. | +| pref.argocd.argoproj.io/default-pod-sort | Application | [see UI customization docs](../operator-manual/ui-customization.md) | Sets the Application's default grouping mechanism. | +| pref.argocd.argoproj.io/default-view | Application | [see UI customization docs](../operator-manual/ui-customization.md) | Sets the Application's default view mode (e.g. "tree" or "list") | + +## Labels + +| Label key | Target resource(es) | Possible values | Description | +|--------------------------------|---------------------|---------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| argocd.argoproj.io/instance | Application | any | Recommended tracking label to [avoid conflicts with other tools which use `app.kubernetes.io/instance`](../faq.md#why-is-my-app-out-of-sync-even-after-syncing). | +| argocd.argoproj.io/secret-type | Secret | `cluster`, `repository`, `repo-creds` | Identifies certain types of Secrets used by Argo CD. See the [Declarative Setup docs](../operator-manual/declarative-setup.md) for details. | diff --git a/docs/user-guide/app_deletion.md b/docs/user-guide/app_deletion.md index 6f7ca8d9caf06..a1eaedf41cd04 100644 --- a/docs/user-guide/app_deletion.md +++ b/docs/user-guide/app_deletion.md @@ -22,11 +22,12 @@ or argocd app delete APPNAME ``` -# Deletion Using `kubectl` +## Deletion Using `kubectl` -To perform a non-cascade delete: +To perform a non-cascade delete, make sure the finalizer is unset and then delete the app: ```bash +kubectl patch app APPNAME -p '{"metadata": {"finalizers": null}}' --type merge kubectl delete app APPNAME ``` @@ -37,16 +38,23 @@ kubectl patch app APPNAME -p '{"metadata": {"finalizers": ["resources-finalizer kubectl delete app APPNAME ``` -# About The Deletion Finalizer +## About The Deletion Finalizer ```yaml metadata: finalizers: + # The default behaviour is foreground cascading deletion - resources-finalizer.argocd.argoproj.io + # Alternatively, you can use background cascading deletion + # - resources-finalizer.argocd.argoproj.io/background ``` When deleting an Application with this finalizer, the Argo CD application controller will perform a cascading delete of the Application's resources. Adding the finalizer enables cascading deletes when implementing [the App of Apps pattern](../operator-manual/cluster-bootstrapping.md#cascading-deletion). +The default propagation policy for cascading deletion is [foreground cascading deletion](https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion). +Argo CD performs [background cascading deletion](https://kubernetes.io/docs/concepts/architecture/garbage-collection/#background-deletion) when `resources-finalizer.argocd.argoproj.io/background` is set. + When you invoke `argocd app delete` with `--cascade`, the finalizer is added automatically. +You can set the propagation policy with `--propagation-policy `. diff --git a/docs/user-guide/application-set.md b/docs/user-guide/application-set.md index 58a3f58ed4470..c8a05d4cb4bdd 100644 --- a/docs/user-guide/application-set.md +++ b/docs/user-guide/application-set.md @@ -1,6 +1,6 @@ ### Automating the generation of Argo CD Applications with the ApplicationSet Controller -The [ApplicationSet controller](../operator-manual/applicationset/index.md) is a part of Argo CD adds Application automation, and seeks to improve multi-cluster support and cluster multitenant support within Argo CD. Argo CD Applications may be templated from multiple different sources, including from Git or Argo CD's own defined cluster list. +The [ApplicationSet controller](../operator-manual/applicationset/index.md) adds Application automation and seeks to improve multi-cluster support and cluster multitenant support within Argo CD. Argo CD Applications may be templated from multiple different sources, including from Git or Argo CD's own defined cluster list. The set of tools provided by the ApplicationSet controller may also be used to allow developers (without access to the Argo CD namespace) to independently create Applications without cluster-administrator intervention. @@ -8,7 +8,7 @@ The set of tools provided by the ApplicationSet controller may also be used to a Be aware of the [security implications](../operator-manual/applicationset/Security.md) before allowing developers to create Applications via ApplicationSets. -The ApplicationSet controller is installed alongside Argo CD (within the same namespace), and the controller automatically generates Argo CD Applications based on the contents of a new `ApplicationSet` Custom Resource (CR). +The ApplicationSet controller automatically generates Argo CD Applications based on the contents of an `ApplicationSet` Custom Resource (CR). Here is an example of an `ApplicationSet` resource that can be used to target an Argo CD Application to multiple clusters: ```yaml @@ -17,6 +17,8 @@ kind: ApplicationSet metadata: name: guestbook spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] generators: - list: elements: @@ -28,15 +30,15 @@ spec: url: https://9.8.7.6 template: metadata: - name: '{{cluster}}-guestbook' + name: '{{.cluster}}-guestbook' spec: - project: default + project: my-project source: - repoURL: https://github.com/argoproj/argo-cd.git + repoURL: https://github.com/infra-team/cluster-deployments.git targetRevision: HEAD - path: applicationset/examples/list-generator/guestbook/{{cluster}} + path: guestbook/{{.cluster}} destination: - server: '{{url}}' + server: '{{.url}}' namespace: guestbook ``` @@ -46,42 +48,4 @@ Likewise, changes made to the ApplicationSet `template` fields will automaticall Within ApplicationSet there exist other more powerful generators in addition to the List generator, including the Cluster generator (which automatically uses Argo CD-defined clusters to template Applications), and the Git generator (which uses the files/directories of a Git repository to template applications). -To learn more about the ApplicationSet controller, check out [ApplicationSet documentation](../operator-manual/applicationset/index.md) to install the ApplicationSet controller alongside Argo CD. - -**Note:** Starting `v2.3` of Argo CD, we don't need to install ApplicationSet Controller separately. It would be instead as part of Argo CD installation. - -#### Post Selector all generators - -The Selector allows to post-filter based on generated values using the kubernetes common labelSelector format. In the example, the list generator generates a set of two application which then filter by the key value to only select the `env` with value `staging`: - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: ApplicationSet -metadata: - name: guestbook -spec: - generators: - - list: - elements: - - cluster: engineering-dev - url: https://kubernetes.default.svc - env: staging - - cluster: engineering-prod - url: https://kubernetes.default.svc - env: prod - selector: - matchLabels: - env: staging - template: - metadata: - name: '{{cluster}}-guestbook' - spec: - project: default - source: - repoURL: https://github.com/argoproj-labs/applicationset.git - targetRevision: HEAD - path: examples/list-generator/guestbook/{{cluster}} - destination: - server: '{{url}}' - namespace: guestbook -``` \ No newline at end of file +To learn more about the ApplicationSet controller, check out the [ApplicationSet documentation](../operator-manual/applicationset/index.md). diff --git a/docs/user-guide/application-specification.md b/docs/user-guide/application-specification.md new file mode 100644 index 0000000000000..4f581b19a47fa --- /dev/null +++ b/docs/user-guide/application-specification.md @@ -0,0 +1,7 @@ +# Application Specification + +The following describes all the available fields of an Application: + +```yaml +{!docs/operator-manual/application.yaml!} +``` diff --git a/docs/user-guide/best_practices.md b/docs/user-guide/best_practices.md index 61a2005b590f0..718ab022f3e50 100644 --- a/docs/user-guide/best_practices.md +++ b/docs/user-guide/best_practices.md @@ -2,7 +2,7 @@ ## Separating Config Vs. Source Code Repositories -Using a separate Git repository to hold your kubernetes manifests, keeping the config separate +Using a separate Git repository to hold your Kubernetes manifests, keeping the config separate from your application source code, is highly recommended for the following reasons: 1. It provides a clean separation of application code vs. application config. There will be times @@ -63,7 +63,7 @@ repository or kustomize base. For example, consider the following kustomization.yaml ```yaml -bases: +resources: - github.com/argoproj/argo-cd//manifests/cluster-install ``` diff --git a/docs/user-guide/build-environment.md b/docs/user-guide/build-environment.md index 56f6e6b436463..8e2448f4f9e7f 100644 --- a/docs/user-guide/build-environment.md +++ b/docs/user-guide/build-environment.md @@ -3,10 +3,11 @@ [Custom tools](../operator-manual/config-management-plugins.md), [Helm](helm.md), [Jsonnet](jsonnet.md), and [Kustomize](kustomize.md) support the following build env vars: | Variable | Description | -| ----------------------------------- | ----------------------------------------------------------------------- | +|-------------------------------------|-------------------------------------------------------------------------| | `ARGOCD_APP_NAME` | The name of the application. | | `ARGOCD_APP_NAMESPACE` | The destination namespace of the application. | | `ARGOCD_APP_REVISION` | The resolved revision, e.g. `f913b6cbf58aa5ae5ca1f8a2b149477aebcbd9d8`. | +| `ARGOCD_APP_REVISION_SHORT` | The resolved short revision, e.g. `f913b6c`. | | `ARGOCD_APP_SOURCE_PATH` | The path of the app within the source repo. | | `ARGOCD_APP_SOURCE_REPO_URL` | The source repo URL. | | `ARGOCD_APP_SOURCE_TARGET_REVISION` | The target revision from the spec, e.g. `master`. | diff --git a/docs/user-guide/ci_automation.md b/docs/user-guide/ci_automation.md index 9aafa385f0461..433483eba7a3f 100644 --- a/docs/user-guide/ci_automation.md +++ b/docs/user-guide/ci_automation.md @@ -18,7 +18,7 @@ docker push mycompany/guestbook:v2.0 ## Update The Local Manifests Using Your Preferred Templating Tool, And Push The Changes To Git !!! tip - The use of a different Git repository to hold your kubernetes manifests (separate from + The use of a different Git repository to hold your Kubernetes manifests (separate from your application source code), is highly recommended. See [best practices](best_practices.md) for further rationale. @@ -43,7 +43,7 @@ useful so that the CLI used in the CI pipeline is always kept in-sync and uses a that is always compatible with the Argo CD API server. ```bash -export ARGOCD_SERVER=argocd.mycompany.com +export ARGOCD_SERVER=argocd.example.com export ARGOCD_AUTH_TOKEN= curl -sSL -o /usr/local/bin/argocd https://${ARGOCD_SERVER}/download/argocd-linux-amd64 argocd app sync guestbook diff --git a/docs/user-guide/commands/argocd.md b/docs/user-guide/commands/argocd.md index 86cc01d9443fa..b03b3971284f6 100644 --- a/docs/user-guide/commands/argocd.md +++ b/docs/user-guide/commands/argocd.md @@ -1,3 +1,5 @@ +# `argocd` Command Reference + ## argocd argocd controls a Argo CD server @@ -13,6 +15,7 @@ argocd [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -26,8 +29,12 @@ argocd [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_account.md b/docs/user-guide/commands/argocd_account.md index 3880bdb6fffc4..88d483ffac68e 100644 --- a/docs/user-guide/commands/argocd_account.md +++ b/docs/user-guide/commands/argocd_account.md @@ -1,3 +1,5 @@ +# `argocd account` Command Reference + ## argocd account Manage account settings @@ -6,6 +8,22 @@ Manage account settings argocd account [flags] ``` +### Examples + +``` + # List accounts + argocd account list + + # Update the current user's password + argocd account update-password + + # Can I sync any app? + argocd account can-i sync applications '*' + + # Get User information + argocd account get-user-info +``` + ### Options ``` @@ -17,6 +35,7 @@ argocd account [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for account --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -37,6 +56,7 @@ argocd account [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -49,14 +69,18 @@ argocd account [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO * [argocd](argocd.md) - argocd controls a Argo CD server -* [argocd account bcrypt](argocd_account_bcrypt.md) - Generate bcrypt hash for the admin password +* [argocd account bcrypt](argocd_account_bcrypt.md) - Generate bcrypt hash for any password * [argocd account can-i](argocd_account_can-i.md) - Can I * [argocd account delete-token](argocd_account_delete-token.md) - Deletes account token * [argocd account generate-token](argocd_account_generate-token.md) - Generate account token diff --git a/docs/user-guide/commands/argocd_account_bcrypt.md b/docs/user-guide/commands/argocd_account_bcrypt.md index 0e7d1a13a1735..6bc282cfaab1e 100644 --- a/docs/user-guide/commands/argocd_account_bcrypt.md +++ b/docs/user-guide/commands/argocd_account_bcrypt.md @@ -1,11 +1,20 @@ +# `argocd account bcrypt` Command Reference + ## argocd account bcrypt -Generate bcrypt hash for the admin password +Generate bcrypt hash for any password ``` argocd account bcrypt [flags] ``` +### Examples + +``` +# Generate bcrypt hash for any password +argocd account bcrypt --password YOUR_PASSWORD +``` + ### Options ``` @@ -20,6 +29,7 @@ argocd account bcrypt [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +42,12 @@ argocd account bcrypt [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_account_can-i.md b/docs/user-guide/commands/argocd_account_can-i.md index 799d3be70da51..6e6cb2bea524b 100644 --- a/docs/user-guide/commands/argocd_account_can-i.md +++ b/docs/user-guide/commands/argocd_account_can-i.md @@ -1,3 +1,5 @@ +# `argocd account can-i` Command Reference + ## argocd account can-i Can I @@ -37,6 +39,7 @@ Resources: [clusters projects applications applicationsets repositories certific --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -49,8 +52,12 @@ Resources: [clusters projects applications applicationsets repositories certific --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_account_delete-token.md b/docs/user-guide/commands/argocd_account_delete-token.md index 1223b91c037e5..6ef4cf11499fe 100644 --- a/docs/user-guide/commands/argocd_account_delete-token.md +++ b/docs/user-guide/commands/argocd_account_delete-token.md @@ -1,3 +1,5 @@ +# `argocd account delete-token` Command Reference + ## argocd account delete-token Deletes account token @@ -30,6 +32,7 @@ argocd account delete-token --account ID --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -42,8 +45,12 @@ argocd account delete-token --account ID --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_account_generate-token.md b/docs/user-guide/commands/argocd_account_generate-token.md index 34ecdc7704c33..0d21d36ad32ff 100644 --- a/docs/user-guide/commands/argocd_account_generate-token.md +++ b/docs/user-guide/commands/argocd_account_generate-token.md @@ -1,3 +1,5 @@ +# `argocd account generate-token` Command Reference + ## argocd account generate-token Generate account token @@ -32,6 +34,7 @@ argocd account generate-token --account --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -44,8 +47,12 @@ argocd account generate-token --account --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_account_get-user-info.md b/docs/user-guide/commands/argocd_account_get-user-info.md index 9f15863916cdd..66603a52b2628 100644 --- a/docs/user-guide/commands/argocd_account_get-user-info.md +++ b/docs/user-guide/commands/argocd_account_get-user-info.md @@ -1,3 +1,5 @@ +# `argocd account get-user-info` Command Reference + ## argocd account get-user-info Get user info @@ -6,6 +8,16 @@ Get user info argocd account get-user-info [flags] ``` +### Examples + +``` + # Get User information for the currently logged-in user (see 'argocd login') + argocd account get-user-info + + # Get User information in yaml format + argocd account get-user-info -o yaml +``` + ### Options ``` @@ -20,6 +32,7 @@ argocd account get-user-info [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +45,12 @@ argocd account get-user-info [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_account_get.md b/docs/user-guide/commands/argocd_account_get.md index f1149ad1c80fc..fbe0ef6027141 100644 --- a/docs/user-guide/commands/argocd_account_get.md +++ b/docs/user-guide/commands/argocd_account_get.md @@ -1,3 +1,5 @@ +# `argocd account get` Command Reference + ## argocd account get Get account details @@ -31,6 +33,7 @@ argocd account get --account --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -43,8 +46,12 @@ argocd account get --account --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_account_list.md b/docs/user-guide/commands/argocd_account_list.md index 0604b6fb535db..0082c0260496c 100644 --- a/docs/user-guide/commands/argocd_account_list.md +++ b/docs/user-guide/commands/argocd_account_list.md @@ -1,3 +1,5 @@ +# `argocd account list` Command Reference + ## argocd account list List accounts @@ -26,6 +28,7 @@ argocd account list --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -38,8 +41,12 @@ argocd account list --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_account_update-password.md b/docs/user-guide/commands/argocd_account_update-password.md index e386e7b69f20a..ed84a7da00617 100644 --- a/docs/user-guide/commands/argocd_account_update-password.md +++ b/docs/user-guide/commands/argocd_account_update-password.md @@ -1,3 +1,5 @@ +# `argocd account update-password` Command Reference + ## argocd account update-password Update an account's password @@ -29,10 +31,10 @@ argocd account update-password [flags] ### Options ``` - --account string an account name that should be updated. Defaults to current user account - --current-password string password of the currently logged on user + --account string An account name that should be updated. Defaults to current user account + --current-password string Password of the currently logged on user -h, --help help for update-password - --new-password string new password you want to update to + --new-password string New password you want to update to ``` ### Options inherited from parent commands @@ -42,6 +44,7 @@ argocd account update-password [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -54,8 +57,12 @@ argocd account update-password [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin.md b/docs/user-guide/commands/argocd_admin.md index aff92561bdf7f..7966e5a3cb9b1 100644 --- a/docs/user-guide/commands/argocd_admin.md +++ b/docs/user-guide/commands/argocd_admin.md @@ -1,3 +1,5 @@ +# `argocd admin` Command Reference + ## argocd admin Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access @@ -6,6 +8,92 @@ Contains a set of commands useful for Argo CD administrators and requires direct argocd admin [flags] ``` +### Examples + +``` +# List all clusters +$ argocd admin cluster list + +# Add a new cluster +$ argocd admin cluster add my-cluster --name my-cluster --in-cluster-context + +# Remove a cluster +argocd admin cluster remove my-cluster + +# List all projects +$ argocd admin project list + +# Create a new project +$argocd admin project create my-project --src-namespace my-source-namespace --dest-namespace my-dest-namespace + +# Update a project +$ argocd admin project update my-project --src-namespace my-updated-source-namespace --dest-namespace my-updated-dest-namespace + +# Delete a project +$ argocd admin project delete my-project + +# List all settings +$ argocd admin settings list + +# Get the current settings +$ argocd admin settings get + +# Update settings +$ argocd admin settings update --repository.resync --value 15 + +# List all applications +$ argocd admin app list + +# Get application details +$ argocd admin app get my-app + +# Sync an application +$ argocd admin app sync my-app + +# Pause an application +$ argocd admin app pause my-app + +# Resume an application +$ argocd admin app resume my-app + +# List all repositories +$ argocd admin repo list + +# Add a repository +$ argocd admin repo add https://github.com/argoproj/my-repo.git + +# Remove a repository +$ argocd admin repo remove https://github.com/argoproj/my-repo.git + +# Import an application from a YAML file +$ argocd admin app import -f my-app.yaml + +# Export an application to a YAML file +$ argocd admin app export my-app -o my-exported-app.yaml + +# Access the Argo CD web UI +$ argocd admin dashboard + +# List notifications +$ argocd admin notification list + +# Get notification details +$ argocd admin notification get my-notification + +# Create a new notification +$ argocd admin notification create my-notification -f notification-config.yaml + +# Update a notification +$ argocd admin notification update my-notification -f updated-notification-config.yaml + +# Delete a notification +$ argocd admin notification delete my-notification + +# Reset the initial admin password +$ argocd admin initial-password reset + +``` + ### Options ``` @@ -21,6 +109,7 @@ argocd admin [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +120,12 @@ argocd admin [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_app.md b/docs/user-guide/commands/argocd_admin_app.md index 70a548c0e9947..58e0f50f25846 100644 --- a/docs/user-guide/commands/argocd_admin_app.md +++ b/docs/user-guide/commands/argocd_admin_app.md @@ -1,3 +1,5 @@ +# `argocd admin app` Command Reference + ## argocd admin app Manage applications configuration @@ -6,6 +8,21 @@ Manage applications configuration argocd admin app [flags] ``` +### Examples + +``` + +# Compare results of two reconciliations and print diff +argocd admin app diff-reconcile-results APPNAME [flags] + +# Generate declarative config for an application +argocd admin app generate-spec APPNAME + +# Reconcile all applications and store reconciliation summary in the specified file +argocd admin app get-reconcile-results APPNAME + +``` + ### Options ``` @@ -19,6 +36,7 @@ argocd admin app [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +49,12 @@ argocd admin app [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_app_diff-reconcile-results.md b/docs/user-guide/commands/argocd_admin_app_diff-reconcile-results.md index 02ddbee5ac8cb..39190e23349fc 100644 --- a/docs/user-guide/commands/argocd_admin_app_diff-reconcile-results.md +++ b/docs/user-guide/commands/argocd_admin_app_diff-reconcile-results.md @@ -1,3 +1,5 @@ +# `argocd admin app diff-reconcile-results` Command Reference + ## argocd admin app diff-reconcile-results Compare results of two reconciliations and print diff. @@ -19,6 +21,7 @@ argocd admin app diff-reconcile-results PATH1 PATH2 [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +34,12 @@ argocd admin app diff-reconcile-results PATH1 PATH2 [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_app_generate-spec.md b/docs/user-guide/commands/argocd_admin_app_generate-spec.md index fc5ff079954ad..ed9f36a4268c0 100644 --- a/docs/user-guide/commands/argocd_admin_app_generate-spec.md +++ b/docs/user-guide/commands/argocd_admin_app_generate-spec.md @@ -1,3 +1,5 @@ +# `argocd admin app generate-spec` Command Reference + ## argocd admin app generate-spec Generate declarative config for an application @@ -65,6 +67,9 @@ argocd admin app generate-spec APPNAME [flags] --kustomize-force-common-annotation Force common annotations in Kustomize --kustomize-force-common-label Force common labels in Kustomize --kustomize-image stringArray Kustomize images (e.g. --kustomize-image node:8.15.0 --kustomize-image mysql=mariadb,alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d) + --kustomize-label-without-selector Do not apply common label to selectors or templates + --kustomize-namespace string Kustomize namespace + --kustomize-replica stringArray Kustomize replicas (e.g. --kustomize-replica my-development=2 --kustomize-replica my-statefulset=4) --kustomize-version string Kustomize version -l, --label stringArray Labels to apply to the app --name string A name for the app, ignored if a file is set (DEPRECATED) @@ -75,13 +80,14 @@ argocd admin app generate-spec APPNAME [flags] --path string Path in repository to the app directory, ignored if a file is set --plugin-env stringArray Additional plugin envs --project string Application project name + --ref string Ref is reference to another source within sources field --release-name string Helm release-name --repo string Repository URL, ignored if a file is set --revision string The tracking source branch, tag, commit or Helm chart version the application will sync to --revision-history-limit int How many items to keep in revision history (default 10) --self-heal Set self healing when sync is automated --sync-option Prune=false Add or remove a sync option, e.g add Prune=false. Remove using `!` prefix, e.g. `!Prune=false` - --sync-policy string Set the sync policy (one of: none, automated (aliases of automated: auto, automatic)) + --sync-policy string Set the sync policy (one of: manual (aliases of manual: none), automated (aliases of automated: auto, automatic)) --sync-retry-backoff-duration duration Sync retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h) (default 5s) --sync-retry-backoff-factor int Factor multiplies the base duration after each failed sync retry (default 2) --sync-retry-backoff-max-duration duration Max sync retry backoff duration. Input needs to be a duration (e.g. 2m, 1h) (default 3m0s) @@ -98,6 +104,7 @@ argocd admin app generate-spec APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -110,8 +117,12 @@ argocd admin app generate-spec APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_app_get-reconcile-results.md b/docs/user-guide/commands/argocd_admin_app_get-reconcile-results.md index 37aacbdfc65df..29fa5d54d9388 100644 --- a/docs/user-guide/commands/argocd_admin_app_get-reconcile-results.md +++ b/docs/user-guide/commands/argocd_admin_app_get-reconcile-results.md @@ -1,3 +1,5 @@ +# `argocd admin app get-reconcile-results` Command Reference + ## argocd admin app get-reconcile-results Reconcile all applications and stores reconciliation summary in the specified file. @@ -17,6 +19,7 @@ argocd admin app get-reconcile-results PATH [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for get-reconcile-results --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -29,6 +32,7 @@ argocd admin app get-reconcile-results PATH [flags] --repo-server string Repo server address. --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --server string The address and port of the Kubernetes API server + --server-side-diff If set to "true" will use server-side diff while comparing resources. Default ("false") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use @@ -42,6 +46,7 @@ argocd admin app get-reconcile-results PATH [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -54,7 +59,11 @@ argocd admin app get-reconcile-results PATH [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_cluster.md b/docs/user-guide/commands/argocd_admin_cluster.md index 3ad9abadcbf9b..544c0de08959c 100644 --- a/docs/user-guide/commands/argocd_admin_cluster.md +++ b/docs/user-guide/commands/argocd_admin_cluster.md @@ -1,3 +1,5 @@ +# `argocd admin cluster` Command Reference + ## argocd admin cluster Manage clusters configuration @@ -6,6 +8,20 @@ Manage clusters configuration argocd admin cluster [flags] ``` +### Examples + +``` + +#Generate declarative config for a cluster +argocd admin cluster generate-spec my-cluster -o yaml + +#Generate a kubeconfig for a cluster named "my-cluster" and display it in the console +argocd admin cluster kubeconfig my-cluster + +#Print information namespaces which Argo CD manages in each cluster +argocd admin cluster namespaces my-cluster +``` + ### Options ``` @@ -19,6 +35,7 @@ argocd admin cluster [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +48,12 @@ argocd admin cluster [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO @@ -41,6 +62,6 @@ argocd admin cluster [flags] * [argocd admin cluster generate-spec](argocd_admin_cluster_generate-spec.md) - Generate declarative config for a cluster * [argocd admin cluster kubeconfig](argocd_admin_cluster_kubeconfig.md) - Generates kubeconfig for the specified cluster * [argocd admin cluster namespaces](argocd_admin_cluster_namespaces.md) - Print information namespaces which Argo CD manages in each cluster. -* [argocd admin cluster shards](argocd_admin_cluster_shards.md) - Print information about each controller shard and portion of Kubernetes resources it is responsible for. +* [argocd admin cluster shards](argocd_admin_cluster_shards.md) - Print information about each controller shard and the estimated portion of Kubernetes resources it is responsible for. * [argocd admin cluster stats](argocd_admin_cluster_stats.md) - Prints information cluster statistics and inferred shard number diff --git a/docs/user-guide/commands/argocd_admin_cluster_generate-spec.md b/docs/user-guide/commands/argocd_admin_cluster_generate-spec.md index 8dc901a088dc1..79f88233fab32 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_generate-spec.md +++ b/docs/user-guide/commands/argocd_admin_cluster_generate-spec.md @@ -1,3 +1,5 @@ +# `argocd admin cluster generate-spec` Command Reference + ## argocd admin cluster generate-spec Generate declarative config for a cluster @@ -11,8 +13,10 @@ argocd admin cluster generate-spec CONTEXT [flags] ``` --annotation stringArray Set metadata annotations (e.g. --annotation key=value) --aws-cluster-name string AWS Cluster name if set then aws cli eks token command will be used to access cluster + --aws-profile string Optional AWS profile. If set then AWS IAM Authenticator uses this profile to perform cluster operations instead of the default AWS credential provider chain. --aws-role-arn string Optional AWS role arn. If set then AWS IAM Authenticator assumes a role to perform cluster operations instead of the default AWS credential provider chain. --bearer-token string Authentication token that should be used to access K8S API server + --cluster-endpoint string Cluster endpoint to use. Can be one of the following: 'kubeconfig', 'kube-public', or 'internal'. --cluster-resources Indicates if cluster level resources should be managed. The setting is used only if list of managed namespaces is not empty. --exec-command string 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. --exec-command-api-version string Preferred input version of the ExecInfo for the --exec-command executable @@ -40,6 +44,7 @@ argocd admin cluster generate-spec CONTEXT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -52,8 +57,12 @@ argocd admin cluster generate-spec CONTEXT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md b/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md index f1a8455af3256..38f61ce5cd8a2 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md +++ b/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md @@ -1,3 +1,5 @@ +# `argocd admin cluster kubeconfig` Command Reference + ## argocd admin cluster kubeconfig Generates kubeconfig for the specified cluster @@ -6,6 +8,23 @@ Generates kubeconfig for the specified cluster argocd admin cluster kubeconfig CLUSTER_URL OUTPUT_PATH [flags] ``` +### Examples + +``` + +#Generate a kubeconfig for a cluster named "my-cluster" on console +argocd admin cluster kubeconfig my-cluster + +#Listing available kubeconfigs for clusters managed by argocd +argocd admin cluster kubeconfig + +#Removing a specific kubeconfig file +argocd admin cluster kubeconfig my-cluster --delete + +#Generate a Kubeconfig for a Cluster with TLS Verification Disabled +argocd admin cluster kubeconfig https://cluster-api-url:6443 /path/to/output/kubeconfig.yaml --insecure-skip-tls-verify +``` + ### Options ``` @@ -17,6 +36,7 @@ argocd admin cluster kubeconfig CLUSTER_URL OUTPUT_PATH [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for kubeconfig --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -38,6 +58,7 @@ argocd admin cluster kubeconfig CLUSTER_URL OUTPUT_PATH [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -50,7 +71,11 @@ argocd admin cluster kubeconfig CLUSTER_URL OUTPUT_PATH [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_cluster_namespaces.md b/docs/user-guide/commands/argocd_admin_cluster_namespaces.md index f302f14ff8a90..fee5c7679e159 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_namespaces.md +++ b/docs/user-guide/commands/argocd_admin_cluster_namespaces.md @@ -1,3 +1,5 @@ +# `argocd admin cluster namespaces` Command Reference + ## argocd admin cluster namespaces Print information namespaces which Argo CD manages in each cluster. @@ -17,6 +19,7 @@ argocd admin cluster namespaces [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for namespaces --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -38,6 +41,7 @@ argocd admin cluster namespaces [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -50,7 +54,11 @@ argocd admin cluster namespaces [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_cluster_namespaces_disable-namespaced-mode.md b/docs/user-guide/commands/argocd_admin_cluster_namespaces_disable-namespaced-mode.md index 17b66a06d65a0..fcbebd7612337 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_namespaces_disable-namespaced-mode.md +++ b/docs/user-guide/commands/argocd_admin_cluster_namespaces_disable-namespaced-mode.md @@ -1,3 +1,5 @@ +# `argocd admin cluster namespaces disable-namespaced-mode` Command Reference + ## argocd admin cluster namespaces disable-namespaced-mode Disable namespaced mode for clusters which name matches to the specified pattern. @@ -17,6 +19,7 @@ argocd admin cluster namespaces disable-namespaced-mode PATTERN [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server --dry-run Print what will be performed (default true) -h, --help help for disable-namespaced-mode --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure @@ -39,6 +42,7 @@ argocd admin cluster namespaces disable-namespaced-mode PATTERN [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -51,7 +55,11 @@ argocd admin cluster namespaces disable-namespaced-mode PATTERN [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_cluster_namespaces_enable-namespaced-mode.md b/docs/user-guide/commands/argocd_admin_cluster_namespaces_enable-namespaced-mode.md index b4a3f63c0d821..762a652d7ab12 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_namespaces_enable-namespaced-mode.md +++ b/docs/user-guide/commands/argocd_admin_cluster_namespaces_enable-namespaced-mode.md @@ -1,3 +1,5 @@ +# `argocd admin cluster namespaces enable-namespaced-mode` Command Reference + ## argocd admin cluster namespaces enable-namespaced-mode Enable namespaced mode for clusters which name matches to the specified pattern. @@ -18,6 +20,7 @@ argocd admin cluster namespaces enable-namespaced-mode PATTERN [flags] --cluster string The name of the kubeconfig cluster to use --cluster-resources Indicates if cluster level resources should be managed. --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server --dry-run Print what will be performed (default true) -h, --help help for enable-namespaced-mode --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure @@ -41,6 +44,7 @@ argocd admin cluster namespaces enable-namespaced-mode PATTERN [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -53,7 +57,11 @@ argocd admin cluster namespaces enable-namespaced-mode PATTERN [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_cluster_shards.md b/docs/user-guide/commands/argocd_admin_cluster_shards.md index be7cc36ccd662..48f6138d47b4a 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_shards.md +++ b/docs/user-guide/commands/argocd_admin_cluster_shards.md @@ -1,6 +1,8 @@ +# `argocd admin cluster shards` Command Reference + ## argocd admin cluster shards -Print information about each controller shard and portion of Kubernetes resources it is responsible for. +Print information about each controller shard and the estimated portion of Kubernetes resources it is responsible for. ``` argocd admin cluster shards [flags] @@ -19,6 +21,7 @@ argocd admin cluster shards [flags] --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use --default-cache-expiration duration Cache expiration default (default 24h0m0s) + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for shards --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -30,7 +33,7 @@ argocd admin cluster shards [flags] --redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation. --redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt). --redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt). - --redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: none, gzip) (default "none") + --redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip") --redis-insecure-skip-tls-verify Skip Redis server certificate validation. --redis-use-tls Use TLS when connecting to Redis. --redisdb int Redis database. @@ -40,6 +43,7 @@ argocd admin cluster shards [flags] --sentinelmaster string Redis sentinel master group name. (default "master") --server string The address and port of the Kubernetes API server --shard int Cluster shard filter (default -1) + --sharding-method string Sharding method. Defaults: legacy. Supported sharding methods are : [legacy, round-robin] (default "legacy") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use @@ -53,6 +57,7 @@ argocd admin cluster shards [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -65,7 +70,11 @@ argocd admin cluster shards [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_cluster_stats.md b/docs/user-guide/commands/argocd_admin_cluster_stats.md index 586069e08e688..c5297ce7e35ed 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_stats.md +++ b/docs/user-guide/commands/argocd_admin_cluster_stats.md @@ -1,3 +1,5 @@ +# `argocd admin cluster stats` Command Reference + ## argocd admin cluster stats Prints information cluster statistics and inferred shard number @@ -6,6 +8,20 @@ Prints information cluster statistics and inferred shard number argocd admin cluster stats [flags] ``` +### Examples + +``` + +#Display stats and shards for clusters +argocd admin cluster stats + +#Display Cluster Statistics for a Specific Shard +argocd admin cluster stats --shard=1 + +#In a multi-cluster environment to print stats for a specific cluster say(target-cluster) +argocd admin cluster stats target-cluster +``` + ### Options ``` @@ -19,6 +35,7 @@ argocd admin cluster stats [flags] --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use --default-cache-expiration duration Cache expiration default (default 24h0m0s) + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for stats --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -30,7 +47,7 @@ argocd admin cluster stats [flags] --redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation. --redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt). --redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt). - --redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: none, gzip) (default "none") + --redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip") --redis-insecure-skip-tls-verify Skip Redis server certificate validation. --redis-use-tls Use TLS when connecting to Redis. --redisdb int Redis database. @@ -40,6 +57,7 @@ argocd admin cluster stats [flags] --sentinelmaster string Redis sentinel master group name. (default "master") --server string The address and port of the Kubernetes API server --shard int Cluster shard filter (default -1) + --sharding-method string Sharding method. Defaults: legacy. Supported sharding methods are : [legacy, round-robin] (default "legacy") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use @@ -53,6 +71,7 @@ argocd admin cluster stats [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -65,7 +84,11 @@ argocd admin cluster stats [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_dashboard.md b/docs/user-guide/commands/argocd_admin_dashboard.md index 323497ebfc558..71e11a173906a 100644 --- a/docs/user-guide/commands/argocd_admin_dashboard.md +++ b/docs/user-guide/commands/argocd_admin_dashboard.md @@ -1,3 +1,5 @@ +# `argocd admin dashboard` Command Reference + ## argocd admin dashboard Starts Argo CD Web UI locally @@ -6,6 +8,20 @@ Starts Argo CD Web UI locally argocd admin dashboard [flags] ``` +### Examples + +``` +# Start the Argo CD Web UI locally on the default port and address +$ argocd admin dashboard + +# Start the Argo CD Web UI locally on a custom port and address +$ argocd admin dashboard --port 8080 --address 127.0.0.1 + +# Start the Argo CD Web UI with GZip compression +$ argocd admin dashboard --redis-compress gzip + +``` + ### Options ``` @@ -18,6 +34,7 @@ argocd admin dashboard [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for dashboard --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -25,7 +42,9 @@ argocd admin dashboard [flags] --password string Password for basic authentication to the API server --port int Listen on given port (default 8080) --proxy-url string If provided, this URL will be used to connect via proxy + --redis-compress string Enable this if the application controller is configured with redis compression enabled. (possible values: gzip, none) (default "gzip") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use @@ -39,6 +58,7 @@ argocd admin dashboard [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -51,8 +71,11 @@ argocd admin dashboard [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding - --server string Argo CD server address + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_export.md b/docs/user-guide/commands/argocd_admin_export.md index 27ff5ad1f9369..d168fe5450a74 100644 --- a/docs/user-guide/commands/argocd_admin_export.md +++ b/docs/user-guide/commands/argocd_admin_export.md @@ -1,3 +1,5 @@ +# `argocd admin export` Command Reference + ## argocd admin export Export all Argo CD data to stdout (default) or a file @@ -17,6 +19,7 @@ argocd admin export [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for export --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -39,6 +42,7 @@ argocd admin export [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -51,7 +55,11 @@ argocd admin export [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_import.md b/docs/user-guide/commands/argocd_admin_import.md index 62c9c9443e2e6..dc8a4b2dbf947 100644 --- a/docs/user-guide/commands/argocd_admin_import.md +++ b/docs/user-guide/commands/argocd_admin_import.md @@ -1,3 +1,5 @@ +# `argocd admin import` Command Reference + ## argocd admin import Import Argo CD data from stdin (specify `-') or a file @@ -17,6 +19,7 @@ argocd admin import SOURCE [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server --dry-run Print what will be performed -h, --help help for import --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure @@ -42,6 +45,7 @@ argocd admin import SOURCE [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -54,7 +58,11 @@ argocd admin import SOURCE [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_initial-password.md b/docs/user-guide/commands/argocd_admin_initial-password.md index 2f997d25d46d1..dbc44561debdc 100644 --- a/docs/user-guide/commands/argocd_admin_initial-password.md +++ b/docs/user-guide/commands/argocd_admin_initial-password.md @@ -1,3 +1,5 @@ +# `argocd admin initial-password` Command Reference + ## argocd admin initial-password Prints initial password to log in to Argo CD for the first time @@ -17,6 +19,7 @@ argocd admin initial-password [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for initial-password --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -38,6 +41,7 @@ argocd admin initial-password [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -50,7 +54,11 @@ argocd admin initial-password [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_notifications.md b/docs/user-guide/commands/argocd_admin_notifications.md index f2bb15b50d306..87429217f99e9 100644 --- a/docs/user-guide/commands/argocd_admin_notifications.md +++ b/docs/user-guide/commands/argocd_admin_notifications.md @@ -1,3 +1,5 @@ +# `argocd admin notifications` Command Reference + ## argocd admin notifications Set of CLI commands that helps manage notifications settings @@ -21,6 +23,7 @@ argocd admin notifications [flags] --cluster string The name of the kubeconfig cluster to use --config-map string argocd-notifications-cm.yaml file path --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for notifications --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -43,6 +46,7 @@ argocd admin notifications [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -55,7 +59,11 @@ argocd admin notifications [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_notifications_template.md b/docs/user-guide/commands/argocd_admin_notifications_template.md index 472651a267cf7..75d5700aaac04 100644 --- a/docs/user-guide/commands/argocd_admin_notifications_template.md +++ b/docs/user-guide/commands/argocd_admin_notifications_template.md @@ -1,3 +1,5 @@ +# `argocd admin notifications template` Command Reference + ## argocd admin notifications template Notification templates related commands @@ -31,7 +33,9 @@ argocd admin notifications template [flags] --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --config-map string argocd-notifications-cm.yaml file path --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -48,10 +52,14 @@ argocd admin notifications template [flags] --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --secret string argocd-notifications-secret.yaml file path. Use empty secret if provided value is ':empty' --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/commands/argocd_admin_notifications_template_get.md b/docs/user-guide/commands/argocd_admin_notifications_template_get.md index 58993c1abc80f..214a8e5cd442b 100644 --- a/docs/user-guide/commands/argocd_admin_notifications_template_get.md +++ b/docs/user-guide/commands/argocd_admin_notifications_template_get.md @@ -1,3 +1,5 @@ +# `argocd admin notifications template get` Command Reference + ## argocd admin notifications template get Prints information about configured templates @@ -43,7 +45,9 @@ argocd admin notifications template get app-sync-succeeded -o=yaml --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --config-map string argocd-notifications-cm.yaml file path --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -60,10 +64,14 @@ argocd admin notifications template get app-sync-succeeded -o=yaml --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --secret string argocd-notifications-secret.yaml file path. Use empty secret if provided value is ':empty' --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/commands/argocd_admin_notifications_template_notify.md b/docs/user-guide/commands/argocd_admin_notifications_template_notify.md index c9098e594ab22..4f94a9d960476 100644 --- a/docs/user-guide/commands/argocd_admin_notifications_template_notify.md +++ b/docs/user-guide/commands/argocd_admin_notifications_template_notify.md @@ -1,3 +1,5 @@ +# `argocd admin notifications template notify` Command Reference + ## argocd admin notifications template notify Generates notification using the specified template and send it to specified recipients @@ -44,7 +46,9 @@ argocd admin notifications template notify app-sync-succeeded guestbook --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --config-map string argocd-notifications-cm.yaml file path --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -61,10 +65,14 @@ argocd admin notifications template notify app-sync-succeeded guestbook --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --secret string argocd-notifications-secret.yaml file path. Use empty secret if provided value is ':empty' --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/commands/argocd_admin_notifications_trigger.md b/docs/user-guide/commands/argocd_admin_notifications_trigger.md index bc4f82c9393a3..d6ff9e53ab235 100644 --- a/docs/user-guide/commands/argocd_admin_notifications_trigger.md +++ b/docs/user-guide/commands/argocd_admin_notifications_trigger.md @@ -1,3 +1,5 @@ +# `argocd admin notifications trigger` Command Reference + ## argocd admin notifications trigger Notification triggers related commands @@ -31,7 +33,9 @@ argocd admin notifications trigger [flags] --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --config-map string argocd-notifications-cm.yaml file path --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -48,10 +52,14 @@ argocd admin notifications trigger [flags] --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --secret string argocd-notifications-secret.yaml file path. Use empty secret if provided value is ':empty' --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/commands/argocd_admin_notifications_trigger_get.md b/docs/user-guide/commands/argocd_admin_notifications_trigger_get.md index 625d71bd5de1e..acd2ab5af9553 100644 --- a/docs/user-guide/commands/argocd_admin_notifications_trigger_get.md +++ b/docs/user-guide/commands/argocd_admin_notifications_trigger_get.md @@ -1,3 +1,5 @@ +# `argocd admin notifications trigger get` Command Reference + ## argocd admin notifications trigger get Prints information about configured triggers @@ -43,7 +45,9 @@ argocd admin notifications trigger get on-sync-failed -o=yaml --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --config-map string argocd-notifications-cm.yaml file path --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -60,10 +64,14 @@ argocd admin notifications trigger get on-sync-failed -o=yaml --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --secret string argocd-notifications-secret.yaml file path. Use empty secret if provided value is ':empty' --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/commands/argocd_admin_notifications_trigger_run.md b/docs/user-guide/commands/argocd_admin_notifications_trigger_run.md index 92f60bdcb8d1e..f8bebb2937937 100644 --- a/docs/user-guide/commands/argocd_admin_notifications_trigger_run.md +++ b/docs/user-guide/commands/argocd_admin_notifications_trigger_run.md @@ -1,3 +1,5 @@ +# `argocd admin notifications trigger run` Command Reference + ## argocd admin notifications trigger run Evaluates specified trigger condition and prints the result @@ -43,7 +45,9 @@ argocd admin notifications trigger run on-sync-status-unknown ./sample-app.yaml --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --config-map string argocd-notifications-cm.yaml file path --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -60,10 +64,14 @@ argocd admin notifications trigger run on-sync-status-unknown ./sample-app.yaml --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --secret string argocd-notifications-secret.yaml file path. Use empty secret if provided value is ':empty' --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/commands/argocd_admin_proj.md b/docs/user-guide/commands/argocd_admin_proj.md index c78f74159b865..b22a2513b7e4d 100644 --- a/docs/user-guide/commands/argocd_admin_proj.md +++ b/docs/user-guide/commands/argocd_admin_proj.md @@ -1,3 +1,5 @@ +# `argocd admin proj` Command Reference + ## argocd admin proj Manage projects configuration @@ -19,6 +21,7 @@ argocd admin proj [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +34,12 @@ argocd admin proj [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_proj_generate-allow-list.md b/docs/user-guide/commands/argocd_admin_proj_generate-allow-list.md index a16531a200ee4..83dc00a6096b4 100644 --- a/docs/user-guide/commands/argocd_admin_proj_generate-allow-list.md +++ b/docs/user-guide/commands/argocd_admin_proj_generate-allow-list.md @@ -1,3 +1,5 @@ +# `argocd admin proj generate-allow-list` Command Reference + ## argocd admin proj generate-allow-list Generates project allow list from the specified clusterRole file @@ -6,6 +8,13 @@ Generates project allow list from the specified clusterRole file argocd admin proj generate-allow-list CLUSTERROLE_PATH PROJ_NAME [flags] ``` +### Examples + +``` +# Generates project allow list from the specified clusterRole file +argocd admin proj generate-allow-list /path/to/clusterrole.yaml my-project +``` + ### Options ``` @@ -17,6 +26,7 @@ argocd admin proj generate-allow-list CLUSTERROLE_PATH PROJ_NAME [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for generate-allow-list --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -39,6 +49,7 @@ argocd admin proj generate-allow-list CLUSTERROLE_PATH PROJ_NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -51,7 +62,11 @@ argocd admin proj generate-allow-list CLUSTERROLE_PATH PROJ_NAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_proj_generate-spec.md b/docs/user-guide/commands/argocd_admin_proj_generate-spec.md index 4227f17459c64..b4f544367813d 100644 --- a/docs/user-guide/commands/argocd_admin_proj_generate-spec.md +++ b/docs/user-guide/commands/argocd_admin_proj_generate-spec.md @@ -1,3 +1,5 @@ +# `argocd admin proj generate-spec` Command Reference + ## argocd admin proj generate-spec Generate declarative config for a project @@ -6,6 +8,19 @@ Generate declarative config for a project argocd admin proj generate-spec PROJECT [flags] ``` +### Examples + +``` + # Generate a YAML configuration for a project named "myproject" + argocd admin projects generate-spec myproject + + # Generate a JSON configuration for a project named "anotherproject" and specify an output file + argocd admin projects generate-spec anotherproject --output json --file config.json + + # Generate a YAML configuration for a project named "someproject" and write it back to the input file + argocd admin projects generate-spec someproject --inline +``` + ### Options ``` @@ -33,6 +48,7 @@ argocd admin proj generate-spec PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -45,8 +61,12 @@ argocd admin proj generate-spec PROJECT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_proj_update-role-policy.md b/docs/user-guide/commands/argocd_admin_proj_update-role-policy.md index f26b4c511a39a..c1c4823077e01 100644 --- a/docs/user-guide/commands/argocd_admin_proj_update-role-policy.md +++ b/docs/user-guide/commands/argocd_admin_proj_update-role-policy.md @@ -1,3 +1,5 @@ +# `argocd admin proj update-role-policy` Command Reference + ## argocd admin proj update-role-policy Implement bulk project role update. Useful to back-fill existing project policies or remove obsolete actions. @@ -28,6 +30,7 @@ argocd admin proj update-role-policy PROJECT_GLOB MODIFICATION ACTION [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server --dry-run Dry run (default true) -h, --help help for update-role-policy --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure @@ -54,6 +57,7 @@ argocd admin proj update-role-policy PROJECT_GLOB MODIFICATION ACTION [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -66,7 +70,11 @@ argocd admin proj update-role-policy PROJECT_GLOB MODIFICATION ACTION [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_repo.md b/docs/user-guide/commands/argocd_admin_repo.md index ef8566aece9e8..411cf558bac5b 100644 --- a/docs/user-guide/commands/argocd_admin_repo.md +++ b/docs/user-guide/commands/argocd_admin_repo.md @@ -1,3 +1,5 @@ +# `argocd admin repo` Command Reference + ## argocd admin repo Manage repositories configuration @@ -19,6 +21,7 @@ argocd admin repo [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +34,12 @@ argocd admin repo [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_repo_generate-spec.md b/docs/user-guide/commands/argocd_admin_repo_generate-spec.md index bf69b4016efcc..10c722913258b 100644 --- a/docs/user-guide/commands/argocd_admin_repo_generate-spec.md +++ b/docs/user-guide/commands/argocd_admin_repo_generate-spec.md @@ -1,3 +1,5 @@ +# `argocd admin repo generate-spec` Command Reference + ## argocd admin repo generate-spec Generate declarative config for a repo @@ -38,6 +40,7 @@ argocd admin repo generate-spec REPOURL [flags] ``` --enable-lfs enable git-lfs (Large File Support) on this repository --enable-oci enable helm-oci (Helm OCI-Based Repository) + --force-http-basic-auth whether to force use of basic auth when connecting repository via HTTP --gcp-service-account-key-path string service account key for the Google Cloud Platform --github-app-enterprise-base-url string base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3 --github-app-id int id of the GitHub Application @@ -65,6 +68,7 @@ argocd admin repo generate-spec REPOURL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -77,8 +81,12 @@ argocd admin repo generate-spec REPOURL [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_settings.md b/docs/user-guide/commands/argocd_admin_settings.md index e1c9d36f28cd0..3c631cf8f123b 100644 --- a/docs/user-guide/commands/argocd_admin_settings.md +++ b/docs/user-guide/commands/argocd_admin_settings.md @@ -1,3 +1,5 @@ +# `argocd admin settings` Command Reference + ## argocd admin settings Provides set of commands for settings validation and troubleshooting @@ -19,6 +21,7 @@ argocd admin settings [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for settings --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -41,6 +44,7 @@ argocd admin settings [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -53,7 +57,11 @@ argocd admin settings [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_settings_rbac.md b/docs/user-guide/commands/argocd_admin_settings_rbac.md index 9a1d5f942e314..043c39979a98a 100644 --- a/docs/user-guide/commands/argocd_admin_settings_rbac.md +++ b/docs/user-guide/commands/argocd_admin_settings_rbac.md @@ -1,3 +1,5 @@ +# `argocd admin settings rbac` Command Reference + ## argocd admin settings rbac Validate and test RBAC configuration @@ -29,7 +31,9 @@ argocd admin settings rbac [flags] --cluster string The name of the kubeconfig cluster to use --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -47,9 +51,13 @@ argocd admin settings rbac [flags] --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/commands/argocd_admin_settings_rbac_can.md b/docs/user-guide/commands/argocd_admin_settings_rbac_can.md index 560539a5239d6..f14092785facf 100644 --- a/docs/user-guide/commands/argocd_admin_settings_rbac_can.md +++ b/docs/user-guide/commands/argocd_admin_settings_rbac_can.md @@ -1,3 +1,5 @@ +# `argocd admin settings rbac can` Command Reference + ## argocd admin settings rbac can Check RBAC permissions for a role or subject @@ -48,6 +50,7 @@ argocd admin settings rbac can someuser create application 'default/app' --defau --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use --default-role string name of the default role to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for can --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -75,6 +78,7 @@ argocd admin settings rbac can someuser create application 'default/app' --defau --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -88,7 +92,11 @@ argocd admin settings rbac can someuser create application 'default/app' --defau --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_settings_rbac_validate.md b/docs/user-guide/commands/argocd_admin_settings_rbac_validate.md index c68de9ae7ee61..b051c7c63694b 100644 --- a/docs/user-guide/commands/argocd_admin_settings_rbac_validate.md +++ b/docs/user-guide/commands/argocd_admin_settings_rbac_validate.md @@ -1,3 +1,5 @@ +# `argocd admin settings rbac validate` Command Reference + ## argocd admin settings rbac validate Validate RBAC policy @@ -6,18 +8,57 @@ Validate RBAC policy Validates an RBAC policy for being syntactically correct. The policy must be -a local file, and in either CSV or K8s ConfigMap format. +a local file or a K8s ConfigMap in the provided namespace, and in either CSV or K8s ConfigMap format. + + +``` +argocd admin settings rbac validate [--policy-file POLICYFILE] [--namespace NAMESPACE] [flags] +``` +### Examples ``` -argocd admin settings rbac validate --policy-file=POLICYFILE [flags] + +# Check whether a given policy file is valid using a local policy.csv file. +argocd admin settings rbac validate --policy-file policy.csv + +# Policy file can also be K8s config map with data keys like argocd-rbac-cm, +# i.e. 'policy.csv' and (optionally) 'policy.default' +argocd admin settings rbac validate --policy-file argocd-rbac-cm.yaml + +# If --policy-file is not given, and instead --namespace is giventhe ConfigMap 'argocd-rbac-cm' +# from K8s is used. +argocd admin settings rbac validate --namespace argocd + +# Either --policy-file or --namespace must be given. + ``` ### Options ``` - -h, --help help for validate - --policy-file string path to the policy file to use + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --as-uid string UID to impersonate for the operation + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server + -h, --help help for validate + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --namespace string namespace to get argo rbac configmap from + --password string Password for basic authentication to the API server + --policy-file string path to the policy file to use + --proxy-url string If provided, this URL will be used to connect via proxy + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server ``` ### Options inherited from parent commands @@ -25,43 +66,29 @@ argocd admin settings rbac validate --policy-file=POLICYFILE [flags] ``` --argocd-cm-path string Path to local argocd-cm.yaml file --argocd-secret-path string Path to local argocd-secret.yaml file - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --as-uid string UID to impersonate for the operation --auth-token string Authentication token - --certificate-authority string Path to a cert file for the certificate authority - --client-certificate string Path to a client certificate file for TLS --client-crt string Client certificate file --client-crt-key string Client certificate key file - --client-key string Path to a client key file for TLS - --cluster string The name of the kubeconfig cluster to use --config string Path to Argo CD config (default "/home/user/.config/argocd/config") - --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) --http-retry-max int Maximum number of retries to establish http connection to Argo CD server --insecure Skip server certificate and domain verification - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kube-context string Directs the command to the given kube-context - --kubeconfig string Path to a kube config. Only required if out-of-cluster --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided --logformat string Set the logging format. One of: text|json (default "text") --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") - -n, --namespace string If present, the namespace scope for this CLI request - --password string Password for basic authentication to the API server --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding - --proxy-url string If provided, this URL will be used to connect via proxy - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --server string The address and port of the Kubernetes API server + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server-crt string Server certificate file - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_admin_settings_resource-overrides.md b/docs/user-guide/commands/argocd_admin_settings_resource-overrides.md index 95c67e54dd9c9..eeec6bcf5f63a 100644 --- a/docs/user-guide/commands/argocd_admin_settings_resource-overrides.md +++ b/docs/user-guide/commands/argocd_admin_settings_resource-overrides.md @@ -1,3 +1,5 @@ +# `argocd admin settings resource-overrides` Command Reference + ## argocd admin settings resource-overrides Troubleshoot resource overrides @@ -29,7 +31,9 @@ argocd admin settings resource-overrides [flags] --cluster string The name of the kubeconfig cluster to use --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -47,9 +51,13 @@ argocd admin settings resource-overrides [flags] --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use @@ -61,6 +69,7 @@ argocd admin settings resource-overrides [flags] * [argocd admin settings](argocd_admin_settings.md) - Provides set of commands for settings validation and troubleshooting * [argocd admin settings resource-overrides health](argocd_admin_settings_resource-overrides_health.md) - Assess resource health * [argocd admin settings resource-overrides ignore-differences](argocd_admin_settings_resource-overrides_ignore-differences.md) - Renders fields excluded from diffing +* [argocd admin settings resource-overrides ignore-resource-updates](argocd_admin_settings_resource-overrides_ignore-resource-updates.md) - Renders fields excluded from resource updates * [argocd admin settings resource-overrides list-actions](argocd_admin_settings_resource-overrides_list-actions.md) - List available resource actions * [argocd admin settings resource-overrides run-action](argocd_admin_settings_resource-overrides_run-action.md) - Executes resource action diff --git a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_health.md b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_health.md index ea98c7badf309..1e5cc49335cc5 100644 --- a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_health.md +++ b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_health.md @@ -1,3 +1,5 @@ +# `argocd admin settings resource-overrides health` Command Reference + ## argocd admin settings resource-overrides health Assess resource health @@ -40,7 +42,9 @@ argocd admin settings resource-overrides health ./deploy.yaml --argocd-cm-path . --cluster string The name of the kubeconfig cluster to use --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -58,9 +62,13 @@ argocd admin settings resource-overrides health ./deploy.yaml --argocd-cm-path . --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_ignore-differences.md b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_ignore-differences.md index 6d5c3ec262942..752b3a64c59c7 100644 --- a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_ignore-differences.md +++ b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_ignore-differences.md @@ -1,3 +1,5 @@ +# `argocd admin settings resource-overrides ignore-differences` Command Reference + ## argocd admin settings resource-overrides ignore-differences Renders fields excluded from diffing @@ -40,7 +42,9 @@ argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argo --cluster string The name of the kubeconfig cluster to use --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -58,9 +62,13 @@ argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argo --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_ignore-resource-updates.md b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_ignore-resource-updates.md new file mode 100644 index 0000000000000..69f09208cf42f --- /dev/null +++ b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_ignore-resource-updates.md @@ -0,0 +1,81 @@ +# `argocd admin settings resource-overrides ignore-resource-updates` Command Reference + +## argocd admin settings resource-overrides ignore-resource-updates + +Renders fields excluded from resource updates + +### Synopsis + +Renders ignored fields using the 'ignoreResourceUpdates' setting specified in the 'resource.customizations' field of 'argocd-cm' ConfigMap + +``` +argocd admin settings resource-overrides ignore-resource-updates RESOURCE_YAML_PATH [flags] +``` + +### Examples + +``` + +argocd admin settings resource-overrides ignore-resource-updates ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml +``` + +### Options + +``` + -h, --help help for ignore-resource-updates +``` + +### Options inherited from parent commands + +``` + --argocd-cm-path string Path to local argocd-cm.yaml file + --argocd-secret-path string Path to local argocd-secret.yaml file + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --as-uid string UID to impersonate for the operation + --auth-token string Authentication token + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kube-context string Directs the command to the given kube-context + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --load-cluster-settings Indicates that config map and secret should be loaded from cluster unless local file path is provided + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + -n, --namespace string If present, the namespace scope for this CLI request + --password string Password for basic authentication to the API server + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server + --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server +``` + +### SEE ALSO + +* [argocd admin settings resource-overrides](argocd_admin_settings_resource-overrides.md) - Troubleshoot resource overrides + diff --git a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_list-actions.md b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_list-actions.md index a08acaa1c4ea0..57f60f3d726f5 100644 --- a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_list-actions.md +++ b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_list-actions.md @@ -1,3 +1,5 @@ +# `argocd admin settings resource-overrides list-actions` Command Reference + ## argocd admin settings resource-overrides list-actions List available resource actions @@ -40,7 +42,9 @@ argocd admin settings resource-overrides action list /tmp/deploy.yaml --argocd-c --cluster string The name of the kubeconfig cluster to use --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -58,9 +62,13 @@ argocd admin settings resource-overrides action list /tmp/deploy.yaml --argocd-c --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_run-action.md b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_run-action.md index a722fe71eaa87..f7ce62d4559fe 100644 --- a/docs/user-guide/commands/argocd_admin_settings_resource-overrides_run-action.md +++ b/docs/user-guide/commands/argocd_admin_settings_resource-overrides_run-action.md @@ -1,3 +1,5 @@ +# `argocd admin settings resource-overrides run-action` Command Reference + ## argocd admin settings resource-overrides run-action Executes resource action @@ -40,7 +42,9 @@ argocd admin settings resource-overrides action run /tmp/deploy.yaml restart --a --cluster string The name of the kubeconfig cluster to use --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -58,9 +62,13 @@ argocd admin settings resource-overrides action run /tmp/deploy.yaml restart --a --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/commands/argocd_admin_settings_validate.md b/docs/user-guide/commands/argocd_admin_settings_validate.md index bc839546a1d0b..8e40a403441b5 100644 --- a/docs/user-guide/commands/argocd_admin_settings_validate.md +++ b/docs/user-guide/commands/argocd_admin_settings_validate.md @@ -1,3 +1,5 @@ +# `argocd admin settings validate` Command Reference + ## argocd admin settings validate Validate settings @@ -24,7 +26,7 @@ argocd admin settings validate --group accounts --group plugins --load-cluster-s ### Options ``` - --group stringArray Optional list of setting groups that have to be validated ( one of: accounts, general, kustomize, plugins, repositories, resource-overrides) + --group stringArray Optional list of setting groups that have to be validated ( one of: accounts, general, kustomize, repositories, resource-overrides) -h, --help help for validate ``` @@ -45,7 +47,9 @@ argocd admin settings validate --group accounts --group plugins --load-cluster-s --cluster string The name of the kubeconfig cluster to use --config string Path to Argo CD config (default "/home/user/.config/argocd/config") --context string The name of the kubeconfig context to use + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --disable-compression If true, opt-out of response compression for all requests to the server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) @@ -63,9 +67,13 @@ argocd admin settings validate --group accounts --group plugins --load-cluster-s --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding --proxy-url string If provided, this URL will be used to connect via proxy + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --server string The address and port of the Kubernetes API server --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/commands/argocd_app.md b/docs/user-guide/commands/argocd_app.md index fb421a89527dd..a5878502ce5c7 100644 --- a/docs/user-guide/commands/argocd_app.md +++ b/docs/user-guide/commands/argocd_app.md @@ -1,3 +1,5 @@ +# `argocd app` Command Reference + ## argocd app Manage applications @@ -30,6 +32,7 @@ argocd app [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for app --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -50,6 +53,7 @@ argocd app [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -62,14 +66,19 @@ argocd app [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO * [argocd](argocd.md) - argocd controls a Argo CD server * [argocd app actions](argocd_app_actions.md) - Manage Resource actions +* [argocd app add-source](argocd_app_add-source.md) - Adds a source to the list of sources in the application * [argocd app create](argocd_app_create.md) - Create an application * [argocd app delete](argocd_app_delete.md) - Delete an application * [argocd app delete-resource](argocd_app_delete-resource.md) - Delete resource in an application @@ -82,6 +91,7 @@ argocd app [flags] * [argocd app manifests](argocd_app_manifests.md) - Print manifests of an application * [argocd app patch](argocd_app_patch.md) - Patch application * [argocd app patch-resource](argocd_app_patch-resource.md) - Patch resource in an application +* [argocd app remove-source](argocd_app_remove-source.md) - Remove a source from multiple sources application. Index starts with 1. Default value is -1. * [argocd app resources](argocd_app_resources.md) - List resource of application * [argocd app rollback](argocd_app_rollback.md) - Rollback application to a previous deployed version by History ID, omitted will Rollback to the previous version * [argocd app set](argocd_app_set.md) - Set application parameters diff --git a/docs/user-guide/commands/argocd_app_actions.md b/docs/user-guide/commands/argocd_app_actions.md index a2357b2bbf87e..af336f1767b23 100644 --- a/docs/user-guide/commands/argocd_app_actions.md +++ b/docs/user-guide/commands/argocd_app_actions.md @@ -1,3 +1,5 @@ +# `argocd app actions` Command Reference + ## argocd app actions Manage Resource actions @@ -6,6 +8,16 @@ Manage Resource actions argocd app actions [flags] ``` +### Examples + +``` + # List all the available actions for an application + argocd app actions list APPNAME + + # Run an available action for an application + argocd app actions run APPNAME ACTION --kind KIND [--resource-name RESOURCE] [--namespace NAMESPACE] [--group GROUP] +``` + ### Options ``` @@ -19,6 +31,7 @@ argocd app actions [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +44,12 @@ argocd app actions [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_actions_list.md b/docs/user-guide/commands/argocd_app_actions_list.md index 627c33a865544..2d1f78524df50 100644 --- a/docs/user-guide/commands/argocd_app_actions_list.md +++ b/docs/user-guide/commands/argocd_app_actions_list.md @@ -1,3 +1,5 @@ +# `argocd app actions list` Command Reference + ## argocd app actions list Lists available actions on a resource @@ -6,6 +8,13 @@ Lists available actions on a resource argocd app actions list APPNAME [flags] ``` +### Examples + +``` + # List all the available actions for an application + argocd app actions list APPNAME +``` + ### Options ``` @@ -24,6 +33,7 @@ argocd app actions list APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -36,8 +46,12 @@ argocd app actions list APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_actions_run.md b/docs/user-guide/commands/argocd_app_actions_run.md index 8832c1eeb06ea..db8e29fc197b9 100644 --- a/docs/user-guide/commands/argocd_app_actions_run.md +++ b/docs/user-guide/commands/argocd_app_actions_run.md @@ -1,3 +1,5 @@ +# `argocd app actions run` Command Reference + ## argocd app actions run Runs an available action on resource(s) @@ -6,6 +8,13 @@ Runs an available action on resource(s) argocd app actions run APPNAME ACTION [flags] ``` +### Examples + +``` + # Run an available action for an application + argocd app actions run APPNAME ACTION --kind KIND [--resource-name RESOURCE] [--namespace NAMESPACE] [--group GROUP] +``` + ### Options ``` @@ -24,6 +33,7 @@ argocd app actions run APPNAME ACTION [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -36,8 +46,12 @@ argocd app actions run APPNAME ACTION [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_add-source.md b/docs/user-guide/commands/argocd_app_add-source.md new file mode 100644 index 0000000000000..ced4bc7b577ca --- /dev/null +++ b/docs/user-guide/commands/argocd_app_add-source.md @@ -0,0 +1,109 @@ +# `argocd app add-source` Command Reference + +## argocd app add-source + +Adds a source to the list of sources in the application + +``` +argocd app add-source APPNAME [flags] +``` + +### Examples + +``` + # Append a source to the list of sources in the application + argocd app add-source guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook +``` + +### Options + +``` + --allow-empty Set allow zero live resources when sync is automated + -N, --app-namespace string Namespace of the target application where the source will be appended + --auto-prune Set automatic pruning when sync is automated + --config-management-plugin string Config management plugin name + --dest-name string K8s cluster Name (e.g. minikube) + --dest-namespace string K8s target namespace + --dest-server string K8s cluster URL (e.g. https://kubernetes.default.svc) + --directory-exclude string Set glob expression used to exclude files from application source path + --directory-include string Set glob expression used to include files from application source path + --directory-recurse Recurse directory + --env string Application environment to monitor + --helm-chart string Helm Chart name + --helm-pass-credentials Pass credentials to all domain + --helm-set stringArray Helm set values on the command line (can be repeated to set several values: --helm-set key1=val1 --helm-set key2=val2) + --helm-set-file stringArray 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) + --helm-set-string stringArray 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) + --helm-skip-crds Skip helm crd installation step + --helm-version string Helm version + -h, --help help for add-source + --ignore-missing-value-files Ignore locally missing valueFiles when setting helm template --values + --jsonnet-ext-var-code stringArray Jsonnet ext var + --jsonnet-ext-var-str stringArray Jsonnet string ext var + --jsonnet-libs stringArray Additional jsonnet libs (prefixed by repoRoot) + --jsonnet-tla-code stringArray Jsonnet top level code arguments + --jsonnet-tla-str stringArray Jsonnet top level string arguments + --kustomize-common-annotation stringArray Set common labels in Kustomize + --kustomize-common-label stringArray Set common labels in Kustomize + --kustomize-force-common-annotation Force common annotations in Kustomize + --kustomize-force-common-label Force common labels in Kustomize + --kustomize-image stringArray Kustomize images (e.g. --kustomize-image node:8.15.0 --kustomize-image mysql=mariadb,alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d) + --kustomize-label-without-selector Do not apply common label to selectors or templates + --kustomize-namespace string Kustomize namespace + --kustomize-replica stringArray Kustomize replicas (e.g. --kustomize-replica my-development=2 --kustomize-replica my-statefulset=4) + --kustomize-version string Kustomize version + --nameprefix string Kustomize nameprefix + --namesuffix string Kustomize namesuffix + -p, --parameter stringArray set a parameter override (e.g. -p guestbook=image=example/guestbook:latest) + --path string Path in repository to the app directory, ignored if a file is set + --plugin-env stringArray Additional plugin envs + --project string Application project name + --ref string Ref is reference to another source within sources field + --release-name string Helm release-name + --repo string Repository URL, ignored if a file is set + --revision string The tracking source branch, tag, commit or Helm chart version the application will sync to + --revision-history-limit int How many items to keep in revision history (default 10) + --self-heal Set self healing when sync is automated + --sync-option Prune=false Add or remove a sync option, e.g add Prune=false. Remove using `!` prefix, e.g. `!Prune=false` + --sync-policy string Set the sync policy (one of: manual (aliases of manual: none), automated (aliases of automated: auto, automatic)) + --sync-retry-backoff-duration duration Sync retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h) (default 5s) + --sync-retry-backoff-factor int Factor multiplies the base duration after each failed sync retry (default 2) + --sync-retry-backoff-max-duration duration Max sync retry backoff duration. Input needs to be a duration (e.g. 2m, 1h) (default 3m0s) + --sync-retry-limit int Max number of allowed sync retries + --validate Validation of repo and cluster (default true) + --values stringArray Helm values file(s) to use + --values-literal-file string Filename or URL to import as a literal Helm values block +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --kube-context string Directs the command to the given kube-context + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") + --server string Argo CD server address + --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") +``` + +### SEE ALSO + +* [argocd app](argocd_app.md) - Manage applications + diff --git a/docs/user-guide/commands/argocd_app_create.md b/docs/user-guide/commands/argocd_app_create.md index e4e5b1d10c6a8..fb147b8e4aa9f 100644 --- a/docs/user-guide/commands/argocd_app_create.md +++ b/docs/user-guide/commands/argocd_app_create.md @@ -1,3 +1,5 @@ +# `argocd app create` Command Reference + ## argocd app create Create an application @@ -24,6 +26,9 @@ argocd app create APPNAME [flags] # 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 + # Create a MultiSource app while yaml file contains an application with multiple sources + argocd app create guestbook --file + # Create a app using a custom tool: argocd app create kasane --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 ``` @@ -63,6 +68,9 @@ argocd app create APPNAME [flags] --kustomize-force-common-annotation Force common annotations in Kustomize --kustomize-force-common-label Force common labels in Kustomize --kustomize-image stringArray Kustomize images (e.g. --kustomize-image node:8.15.0 --kustomize-image mysql=mariadb,alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d) + --kustomize-label-without-selector Do not apply common label to selectors or templates + --kustomize-namespace string Kustomize namespace + --kustomize-replica stringArray Kustomize replicas (e.g. --kustomize-replica my-development=2 --kustomize-replica my-statefulset=4) --kustomize-version string Kustomize version -l, --label stringArray Labels to apply to the app --name string A name for the app, ignored if a file is set (DEPRECATED) @@ -72,6 +80,7 @@ argocd app create APPNAME [flags] --path string Path in repository to the app directory, ignored if a file is set --plugin-env stringArray Additional plugin envs --project string Application project name + --ref string Ref is reference to another source within sources field --release-name string Helm release-name --repo string Repository URL, ignored if a file is set --revision string The tracking source branch, tag, commit or Helm chart version the application will sync to @@ -79,7 +88,7 @@ argocd app create APPNAME [flags] --self-heal Set self healing when sync is automated --set-finalizer Sets deletion finalizer on the application, application resources will be cascaded on deletion --sync-option Prune=false Add or remove a sync option, e.g add Prune=false. Remove using `!` prefix, e.g. `!Prune=false` - --sync-policy string Set the sync policy (one of: none, automated (aliases of automated: auto, automatic)) + --sync-policy string Set the sync policy (one of: manual (aliases of manual: none), automated (aliases of automated: auto, automatic)) --sync-retry-backoff-duration duration Sync retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h) (default 5s) --sync-retry-backoff-factor int Factor multiplies the base duration after each failed sync retry (default 2) --sync-retry-backoff-max-duration duration Max sync retry backoff duration. Input needs to be a duration (e.g. 2m, 1h) (default 3m0s) @@ -97,6 +106,7 @@ argocd app create APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -109,8 +119,12 @@ argocd app create APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_delete-resource.md b/docs/user-guide/commands/argocd_app_delete-resource.md index 5d3192694a45b..4a305eb4b4489 100644 --- a/docs/user-guide/commands/argocd_app_delete-resource.md +++ b/docs/user-guide/commands/argocd_app_delete-resource.md @@ -1,3 +1,5 @@ +# `argocd app delete-resource` Command Reference + ## argocd app delete-resource Delete resource in an application @@ -16,6 +18,7 @@ argocd app delete-resource APPNAME [flags] --kind string Kind --namespace string Namespace --orphan Indicates whether to force delete the resource + --project string The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist --resource-name string Name of resource ``` @@ -26,6 +29,7 @@ argocd app delete-resource APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -38,8 +42,12 @@ argocd app delete-resource APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_delete.md b/docs/user-guide/commands/argocd_app_delete.md index 33c5d6c0ee38f..827eeaab4ce7a 100644 --- a/docs/user-guide/commands/argocd_app_delete.md +++ b/docs/user-guide/commands/argocd_app_delete.md @@ -1,3 +1,5 @@ +# `argocd app delete` Command Reference + ## argocd app delete Delete an application @@ -26,10 +28,12 @@ argocd app delete APPNAME [flags] ### Options ``` + -N, --app-namespace string Namespace where the application will be deleted from --cascade Perform a cascaded deletion of all application resources (default true) -h, --help help for delete -p, --propagation-policy string Specify propagation policy for deletion of application's resources. One of: foreground|background (default "foreground") -l, --selector string Delete all apps with matching label. Supports '=', '==', '!=', in, notin, exists & not exists. Matching apps must satisfy all of the specified label constraints. + --wait Wait until deletion of the application(s) completes -y, --yes Turn off prompting to confirm cascaded deletion of application resources ``` @@ -40,6 +44,7 @@ argocd app delete APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -52,8 +57,12 @@ argocd app delete APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_diff.md b/docs/user-guide/commands/argocd_app_diff.md index 6af07a0b08b0f..b352c30123eca 100644 --- a/docs/user-guide/commands/argocd_app_diff.md +++ b/docs/user-guide/commands/argocd_app_diff.md @@ -1,3 +1,5 @@ +# `argocd app diff` Command Reference + ## argocd app diff Perform a diff against the target and live state. @@ -7,6 +9,7 @@ Perform a diff against the target and live state. Perform a diff against the target and live state. Uses 'diff' to render the difference. KUBECTL_EXTERNAL_DIFF environment variable can be used to select your own diff tool. Returns the following exit codes: 2 on general errors, 1 when a diff is found, and 0 when no diff is found +Kubernetes Secrets are ignored from this diff. ``` argocd app diff APPNAME [flags] @@ -15,6 +18,7 @@ argocd app diff APPNAME [flags] ### Options ``` + -N, --app-namespace string Only render the difference in namespace --exit-code Return non-zero exit code when there is a diff (default true) --hard-refresh Refresh application data as well as target manifests cache -h, --help help for diff @@ -33,6 +37,7 @@ argocd app diff APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -45,8 +50,12 @@ argocd app diff APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_edit.md b/docs/user-guide/commands/argocd_app_edit.md index 2f051ab186656..e581677b79c12 100644 --- a/docs/user-guide/commands/argocd_app_edit.md +++ b/docs/user-guide/commands/argocd_app_edit.md @@ -1,3 +1,5 @@ +# `argocd app edit` Command Reference + ## argocd app edit Edit application @@ -9,7 +11,8 @@ argocd app edit APPNAME [flags] ### Options ``` - -h, --help help for edit + -N, --app-namespace string Only edit application in namespace + -h, --help help for edit ``` ### Options inherited from parent commands @@ -19,6 +22,7 @@ argocd app edit APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +35,12 @@ argocd app edit APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_get.md b/docs/user-guide/commands/argocd_app_get.md index 3434d20cb8214..d0bf744054c38 100644 --- a/docs/user-guide/commands/argocd_app_get.md +++ b/docs/user-guide/commands/argocd_app_get.md @@ -1,3 +1,5 @@ +# `argocd app get` Command Reference + ## argocd app get Get application details @@ -6,15 +8,47 @@ Get application details argocd app get APPNAME [flags] ``` +### Examples + +``` + # Get basic details about the application "my-app" in wide format + argocd app get my-app -o wide + + # Get detailed information about the application "my-app" in YAML format + argocd app get my-app -o yaml + + # Get details of the application "my-app" in JSON format + argocd get my-app -o json + + # Get application details and include information about the current operation + argocd app get my-app --show-operation + + # Show application parameters and overrides + argocd app get my-app --show-params + + # Refresh application data when retrieving + argocd app get my-app --refresh + + # Perform a hard refresh, including refreshing application data and target manifests cache + argocd app get my-app --hard-refresh + + # Get application details and display them in a tree format + argocd app get my-app --output tree + + # Get application details and display them in a detailed tree format + argocd app get my-app --output tree=detailed +``` + ### Options ``` - --hard-refresh Refresh application data as well as target manifests cache - -h, --help help for get - -o, --output string Output format. One of: json|yaml|wide (default "wide") - --refresh Refresh application data when retrieving - --show-operation Show application operation - --show-params Show application parameters and overrides + -N, --app-namespace string Only get application from namespace + --hard-refresh Refresh application data as well as target manifests cache + -h, --help help for get + -o, --output string Output format. One of: json|yaml|wide|tree (default "wide") + --refresh Refresh application data when retrieving + --show-operation Show application operation + --show-params Show application parameters and overrides ``` ### Options inherited from parent commands @@ -24,6 +58,7 @@ argocd app get APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -36,8 +71,12 @@ argocd app get APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_history.md b/docs/user-guide/commands/argocd_app_history.md index 7c6a122fad82b..eefadef01f417 100644 --- a/docs/user-guide/commands/argocd_app_history.md +++ b/docs/user-guide/commands/argocd_app_history.md @@ -1,3 +1,5 @@ +# `argocd app history` Command Reference + ## argocd app history Show application deployment history @@ -9,8 +11,9 @@ argocd app history APPNAME [flags] ### Options ``` - -h, --help help for history - -o, --output string Output format. One of: wide|id (default "wide") + -N, --app-namespace string Only show application deployment history in namespace + -h, --help help for history + -o, --output string Output format. One of: wide|id (default "wide") ``` ### Options inherited from parent commands @@ -20,6 +23,7 @@ argocd app history APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +36,12 @@ argocd app history APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_list.md b/docs/user-guide/commands/argocd_app_list.md index 5d85f926815d1..17e00fcac9df3 100644 --- a/docs/user-guide/commands/argocd_app_list.md +++ b/docs/user-guide/commands/argocd_app_list.md @@ -1,3 +1,5 @@ +# `argocd app list` Command Reference + ## argocd app list List applications @@ -39,6 +41,7 @@ argocd app list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -51,8 +54,12 @@ argocd app list [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_logs.md b/docs/user-guide/commands/argocd_app_logs.md index 473b95a338bfb..8dc1f6a9f1aae 100644 --- a/docs/user-guide/commands/argocd_app_logs.md +++ b/docs/user-guide/commands/argocd_app_logs.md @@ -1,3 +1,5 @@ +# `argocd app logs` Command Reference + ## argocd app logs Get logs of application pods @@ -6,12 +8,52 @@ Get logs of application pods argocd app logs APPNAME [flags] ``` +### Examples + +``` + # Get logs of pods associated with the application "my-app" + argocd app logs my-app + + # Get logs of pods associated with the application "my-app" in a specific resource group + argocd app logs my-app --group my-group + + # Get logs of pods associated with the application "my-app" in a specific resource kind + argocd app logs my-app --kind my-kind + + # Get logs of pods associated with the application "my-app" in a specific namespace + argocd app logs my-app --namespace my-namespace + + # Get logs of pods associated with the application "my-app" for a specific resource name + argocd app logs my-app --name my-resource + + # Stream logs in real-time for the application "my-app" + argocd app logs my-app -f + + # Get the last N lines of logs for the application "my-app" + argocd app logs my-app --tail 100 + + # Get logs since a specified number of seconds ago + argocd app logs my-app --since-seconds 3600 + + # Get logs until a specified time (format: "2023-10-10T15:30:00Z") + argocd app logs my-app --until-time "2023-10-10T15:30:00Z" + + # Filter logs to show only those containing a specific string + argocd app logs my-app --filter "error" + + # Get logs for a specific container within the pods + argocd app logs my-app -c my-container + + # Get previously terminated container logs + argocd app logs my-app -p +``` + ### Options ``` - --container string Optional container name + -c, --container string Optional container name --filter string Show logs contain this string - --follow Specify if the logs should be streamed + -f, --follow Specify if the logs should be streamed --group string Resource group -h, --help help for logs --kind string Resource kind @@ -30,6 +72,7 @@ argocd app logs APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -42,8 +85,12 @@ argocd app logs APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_manifests.md b/docs/user-guide/commands/argocd_app_manifests.md index f73cfc5b9dd29..d3b91756cbe04 100644 --- a/docs/user-guide/commands/argocd_app_manifests.md +++ b/docs/user-guide/commands/argocd_app_manifests.md @@ -1,3 +1,5 @@ +# `argocd app manifests` Command Reference + ## argocd app manifests Print manifests of an application @@ -23,6 +25,7 @@ argocd app manifests APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -35,8 +38,12 @@ argocd app manifests APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_patch-resource.md b/docs/user-guide/commands/argocd_app_patch-resource.md index 95dd5de6cc19b..c849395cb3ea8 100644 --- a/docs/user-guide/commands/argocd_app_patch-resource.md +++ b/docs/user-guide/commands/argocd_app_patch-resource.md @@ -1,3 +1,5 @@ +# `argocd app patch-resource` Command Reference + ## argocd app patch-resource Patch resource in an application @@ -16,6 +18,7 @@ argocd app patch-resource APPNAME [flags] --namespace string Namespace --patch string Patch --patch-type string Which Patching strategy to use: 'application/json-patch+json', 'application/merge-patch+json', or 'application/strategic-merge-patch+json'. Defaults to 'application/merge-patch+json' (default "application/merge-patch+json") + --project string The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist --resource-name string Name of resource ``` @@ -26,6 +29,7 @@ argocd app patch-resource APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -38,8 +42,12 @@ argocd app patch-resource APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_patch.md b/docs/user-guide/commands/argocd_app_patch.md index 0174252dccc12..0c453ea159e64 100644 --- a/docs/user-guide/commands/argocd_app_patch.md +++ b/docs/user-guide/commands/argocd_app_patch.md @@ -1,26 +1,30 @@ +# `argocd app patch` Command Reference + ## argocd app patch Patch application -### Synopsis - -Examples: - # Update an application's source path using json patch - argocd app patch myapplication --patch='[{"op": "replace", "path": "/spec/source/path", "value": "newPath"}]' --type json +``` +argocd app patch APPNAME [flags] +``` - # Update an application's repository target revision using merge patch - argocd app patch myapplication --patch '{"spec": { "source": { "targetRevision": "master" } }}' --type merge +### Examples ``` -argocd app patch APPNAME [flags] + # Update an application's source path using json patch + argocd app patch myapplication --patch='[{"op": "replace", "path": "/spec/source/path", "value": "newPath"}]' --type json + + # Update an application's repository target revision using merge patch + argocd app patch myapplication --patch '{"spec": { "source": { "targetRevision": "master" } }}' --type merge ``` ### Options ``` - -h, --help help for patch - --patch string Patch body - --type string The type of patch being provided; one of [json merge] (default "json") + -N, --app-namespace string Only patch application in namespace + -h, --help help for patch + --patch string Patch body + --type string The type of patch being provided; one of [json merge] (default "json") ``` ### Options inherited from parent commands @@ -30,6 +34,7 @@ argocd app patch APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -42,8 +47,12 @@ argocd app patch APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_remove-source.md b/docs/user-guide/commands/argocd_app_remove-source.md new file mode 100644 index 0000000000000..b9f29d8c6eb45 --- /dev/null +++ b/docs/user-guide/commands/argocd_app_remove-source.md @@ -0,0 +1,57 @@ +# `argocd app remove-source` Command Reference + +## argocd app remove-source + +Remove a source from multiple sources application. Index starts with 1. Default value is -1. + +``` +argocd app remove-source APPNAME [flags] +``` + +### Examples + +``` + # Remove the source at index 1 from application's sources. Index starts at 1. + argocd app remove-source myapplication --source-index 1 +``` + +### Options + +``` + -N, --app-namespace string Namespace of the target application where the source will be appended + -h, --help help for remove-source + --source-index int Index of the source from the list of sources of the app. Index starts from 1. (default -1) +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --kube-context string Directs the command to the given kube-context + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") + --server string Argo CD server address + --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") +``` + +### SEE ALSO + +* [argocd app](argocd_app.md) - Manage applications + diff --git a/docs/user-guide/commands/argocd_app_resources.md b/docs/user-guide/commands/argocd_app_resources.md index 8edb3093f9dd1..22027f74ba3d7 100644 --- a/docs/user-guide/commands/argocd_app_resources.md +++ b/docs/user-guide/commands/argocd_app_resources.md @@ -1,3 +1,5 @@ +# `argocd app resources` Command Reference + ## argocd app resources List resource of application @@ -9,8 +11,10 @@ argocd app resources APPNAME [flags] ### Options ``` - -h, --help help for resources - --orphaned Lists only orphaned resources + -h, --help help for resources + --orphaned Lists only orphaned resources + --output string Provides the tree view of the resources + --project string The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist ``` ### Options inherited from parent commands @@ -20,6 +24,7 @@ argocd app resources APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +37,12 @@ argocd app resources APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_rollback.md b/docs/user-guide/commands/argocd_app_rollback.md index 80f1d39407dc4..923023e35a2e8 100644 --- a/docs/user-guide/commands/argocd_app_rollback.md +++ b/docs/user-guide/commands/argocd_app_rollback.md @@ -1,3 +1,5 @@ +# `argocd app rollback` Command Reference + ## argocd app rollback Rollback application to a previous deployed version by History ID, omitted will Rollback to the previous version @@ -9,9 +11,11 @@ argocd app rollback APPNAME [ID] [flags] ### Options ``` - -h, --help help for rollback - --prune Allow deleting unexpected resources - --timeout uint Time out after this many seconds + -N, --app-namespace string Rollback application in namespace + -h, --help help for rollback + -o, --output string Output format. One of: json|yaml|wide|tree|tree=detailed (default "wide") + --prune Allow deleting unexpected resources + --timeout uint Time out after this many seconds ``` ### Options inherited from parent commands @@ -21,6 +25,7 @@ argocd app rollback APPNAME [ID] [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -33,8 +38,12 @@ argocd app rollback APPNAME [ID] [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_set.md b/docs/user-guide/commands/argocd_app_set.md index 9e89957e07e9e..97288ad775345 100644 --- a/docs/user-guide/commands/argocd_app_set.md +++ b/docs/user-guide/commands/argocd_app_set.md @@ -1,3 +1,5 @@ +# `argocd app set` Command Reference + ## argocd app set Set application parameters @@ -6,10 +8,33 @@ Set application parameters argocd app set APPNAME [flags] ``` +### Examples + +``` + # Set application parameters for the application "my-app" + argocd app set my-app --parameter key1=value1 --parameter key2=value2 + + # Set and validate application parameters for "my-app" + argocd app set my-app --parameter key1=value1 --parameter key2=value2 --validate + + # Set and override application parameters with JSON or YAML file + argocd app set my-app --from-file path/to/parameters.json + + # Set and override application parameters with a parameter file + argocd app set my-app --parameter-file path/to/parameter-file.yaml + + # Set and override application parameters for a source at index 1 under spec.sources of app my-app. source-index starts at 1. + argocd app set my-app --source-index 1 --repo https://github.com/argoproj/argocd-example-apps.git + + # Set application parameters and specify the namespace + argocd app set my-app --parameter key1=value1 --parameter key2=value2 --namespace my-namespace +``` + ### Options ``` --allow-empty Set allow zero live resources when sync is automated + -N, --app-namespace string Set application parameters in namespace --auto-prune Set automatic pruning when sync is automated --config-management-plugin string Config management plugin name --dest-name string K8s cluster Name (e.g. minikube) @@ -38,6 +63,9 @@ argocd app set APPNAME [flags] --kustomize-force-common-annotation Force common annotations in Kustomize --kustomize-force-common-label Force common labels in Kustomize --kustomize-image stringArray Kustomize images (e.g. --kustomize-image node:8.15.0 --kustomize-image mysql=mariadb,alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d) + --kustomize-label-without-selector Do not apply common label to selectors or templates + --kustomize-namespace string Kustomize namespace + --kustomize-replica stringArray Kustomize replicas (e.g. --kustomize-replica my-development=2 --kustomize-replica my-statefulset=4) --kustomize-version string Kustomize version --nameprefix string Kustomize nameprefix --namesuffix string Kustomize namesuffix @@ -45,13 +73,15 @@ argocd app set APPNAME [flags] --path string Path in repository to the app directory, ignored if a file is set --plugin-env stringArray Additional plugin envs --project string Application project name + --ref string Ref is reference to another source within sources field --release-name string Helm release-name --repo string Repository URL, ignored if a file is set --revision string The tracking source branch, tag, commit or Helm chart version the application will sync to --revision-history-limit int How many items to keep in revision history (default 10) --self-heal Set self healing when sync is automated + --source-index int Index of the source from the list of sources of the app. Index starts at 1. (default -1) --sync-option Prune=false Add or remove a sync option, e.g add Prune=false. Remove using `!` prefix, e.g. `!Prune=false` - --sync-policy string Set the sync policy (one of: none, automated (aliases of automated: auto, automatic)) + --sync-policy string Set the sync policy (one of: manual (aliases of manual: none), automated (aliases of automated: auto, automatic)) --sync-retry-backoff-duration duration Sync retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h) (default 5s) --sync-retry-backoff-factor int Factor multiplies the base duration after each failed sync retry (default 2) --sync-retry-backoff-max-duration duration Max sync retry backoff duration. Input needs to be a duration (e.g. 2m, 1h) (default 3m0s) @@ -68,6 +98,7 @@ argocd app set APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -80,8 +111,12 @@ argocd app set APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_sync.md b/docs/user-guide/commands/argocd_app_sync.md index 798fcecdd8a03..a0a8f8459eeaa 100644 --- a/docs/user-guide/commands/argocd_app_sync.md +++ b/docs/user-guide/commands/argocd_app_sync.md @@ -1,3 +1,5 @@ +# `argocd app sync` Command Reference + ## argocd app sync Sync an application to its target state @@ -36,6 +38,8 @@ argocd app sync [APPNAME... | -l selector | --project project-name] [flags] ### Options ``` + -N, --app-namespace string Only sync an application in namespace + --apply-out-of-sync-only Sync only out-of-sync resources --assumeYes Assume yes as answer for all user queries or prompts --async Do not wait for application to sync before continuing --dry-run Preview apply without affecting cluster @@ -45,6 +49,7 @@ argocd app sync [APPNAME... | -l selector | --project project-name] [flags] --label stringArray Sync only specific resources with a label. This option may be specified repeatedly. --local string Path to a local directory. When this flag is present no git queries will be made --local-repo-root string Path to the repository root. Used together with --local allows setting the repository root (default "/") + -o, --output string Output format. One of: json|yaml|wide|tree|tree=detailed (default "wide") --preview-changes Preview difference against the target and live state before syncing app and wait for user confirmation --project stringArray Sync apps that belong to the specified projects. This option may be specified repeatedly. --prune Allow deleting unexpected resources @@ -68,6 +73,7 @@ argocd app sync [APPNAME... | -l selector | --project project-name] [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -80,8 +86,12 @@ argocd app sync [APPNAME... | -l selector | --project project-name] [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_terminate-op.md b/docs/user-guide/commands/argocd_app_terminate-op.md index fe8475e023aa6..a6d04299ca313 100644 --- a/docs/user-guide/commands/argocd_app_terminate-op.md +++ b/docs/user-guide/commands/argocd_app_terminate-op.md @@ -1,3 +1,5 @@ +# `argocd app terminate-op` Command Reference + ## argocd app terminate-op Terminate running operation of an application @@ -19,6 +21,7 @@ argocd app terminate-op APPNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +34,12 @@ argocd app terminate-op APPNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_unset.md b/docs/user-guide/commands/argocd_app_unset.md index 11d3ae0d7097f..0c3bf25d7fa91 100644 --- a/docs/user-guide/commands/argocd_app_unset.md +++ b/docs/user-guide/commands/argocd_app_unset.md @@ -1,3 +1,5 @@ +# `argocd app unset` Command Reference + ## argocd app unset Unset application parameters @@ -12,9 +14,12 @@ argocd app unset APPNAME parameters [flags] # Unset kustomize override kustomize image argocd app unset my-app --kustomize-image=alpine - # Unset kustomize override prefix + # Unset kustomize override suffix argocd app unset my-app --namesuffix + # Unset kustomize override suffix for source at index 1 under spec.sources of app my-app. source-index starts at 1. + argocd app unset my-app --source-index 1 --namesuffix + # Unset parameter override argocd app unset my-app -p COMPONENT=PARAM ``` @@ -22,17 +27,22 @@ argocd app unset APPNAME parameters [flags] ### Options ``` - -h, --help help for unset - --ignore-missing-value-files Unset the helm ignore-missing-value-files option (revert to false) - --kustomize-image stringArray Kustomize images name (e.g. --kustomize-image node --kustomize-image mysql) - --kustomize-version Kustomize version - --nameprefix Kustomize nameprefix - --namesuffix Kustomize namesuffix - -p, --parameter stringArray Unset a parameter override (e.g. -p guestbook=image) - --pass-credentials Unset passCredentials - --plugin-env stringArray Unset plugin env variables (e.g --plugin-env name) - --values stringArray Unset one or more Helm values files - --values-literal Unset literal Helm values block + -N, --app-namespace string Unset application parameters in namespace + -h, --help help for unset + --ignore-missing-value-files Unset the helm ignore-missing-value-files option (revert to false) + --kustomize-image stringArray Kustomize images name (e.g. --kustomize-image node --kustomize-image mysql) + --kustomize-namespace Kustomize namespace + --kustomize-replica stringArray Kustomize replicas name (e.g. --kustomize-replica my-deployment --kustomize-replica my-statefulset) + --kustomize-version Kustomize version + --nameprefix Kustomize nameprefix + --namesuffix Kustomize namesuffix + -p, --parameter stringArray Unset a parameter override (e.g. -p guestbook=image) + --pass-credentials Unset passCredentials + --plugin-env stringArray Unset plugin env variables (e.g --plugin-env name) + --ref Unset ref on the source + --source-index int Index of the source from the list of sources of the app. Index starts at 1. (default -1) + --values stringArray Unset one or more Helm values files + --values-literal Unset literal Helm values block ``` ### Options inherited from parent commands @@ -42,6 +52,7 @@ argocd app unset APPNAME parameters [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -54,8 +65,12 @@ argocd app unset APPNAME parameters [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_app_wait.md b/docs/user-guide/commands/argocd_app_wait.md index 7bf911acde349..e2d3886f4d3ab 100644 --- a/docs/user-guide/commands/argocd_app_wait.md +++ b/docs/user-guide/commands/argocd_app_wait.md @@ -1,3 +1,5 @@ +# `argocd app wait` Command Reference + ## argocd app wait Wait for an application to reach a synced and healthy state @@ -36,10 +38,13 @@ argocd app wait [APPNAME.. | -l selector] [flags] ### Options ``` + -N, --app-namespace string Only wait for an application in namespace --degraded Wait for degraded + --delete Wait for delete --health Wait for health -h, --help help for wait --operation Wait for pending operations + -o, --output string Output format. One of: json|yaml|wide|tree|tree=detailed (default "wide") --resource stringArray Sync only specific resources as GROUP:KIND:NAME or !GROUP:KIND:NAME. Fields may be blank and '*' can be used. This option may be specified repeatedly -l, --selector string Wait for apps by label. Supports '=', '==', '!=', in, notin, exists & not exists. Matching apps must satisfy all of the specified label constraints. --suspended Wait for suspended @@ -54,6 +59,7 @@ argocd app wait [APPNAME.. | -l selector] [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -66,8 +72,12 @@ argocd app wait [APPNAME.. | -l selector] [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_appset.md b/docs/user-guide/commands/argocd_appset.md index a3af8b8f50002..7b543ae318831 100644 --- a/docs/user-guide/commands/argocd_appset.md +++ b/docs/user-guide/commands/argocd_appset.md @@ -1,3 +1,5 @@ +# `argocd appset` Command Reference + ## argocd appset Manage ApplicationSets @@ -33,6 +35,7 @@ argocd appset [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for appset --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -53,6 +56,7 @@ argocd appset [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -65,8 +69,12 @@ argocd appset [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_appset_create.md b/docs/user-guide/commands/argocd_appset_create.md index 07666ae70b295..fccc03fcc971c 100644 --- a/docs/user-guide/commands/argocd_appset_create.md +++ b/docs/user-guide/commands/argocd_appset_create.md @@ -1,3 +1,5 @@ +# `argocd appset create` Command Reference + ## argocd appset create Create one or more ApplicationSets @@ -27,6 +29,7 @@ argocd appset create [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -39,8 +42,12 @@ argocd appset create [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_appset_delete.md b/docs/user-guide/commands/argocd_appset_delete.md index 9c991e9c33f42..d97ca51b604e8 100644 --- a/docs/user-guide/commands/argocd_appset_delete.md +++ b/docs/user-guide/commands/argocd_appset_delete.md @@ -1,3 +1,5 @@ +# `argocd appset delete` Command Reference + ## argocd appset delete Delete one or more ApplicationSets @@ -27,6 +29,7 @@ argocd appset delete [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -39,8 +42,12 @@ argocd appset delete [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_appset_get.md b/docs/user-guide/commands/argocd_appset_get.md index 4c791524f988a..8024d8ebf0a06 100644 --- a/docs/user-guide/commands/argocd_appset_get.md +++ b/docs/user-guide/commands/argocd_appset_get.md @@ -1,3 +1,5 @@ +# `argocd appset get` Command Reference + ## argocd appset get Get ApplicationSet details @@ -6,6 +8,13 @@ Get ApplicationSet details argocd appset get APPSETNAME [flags] ``` +### Examples + +``` + # Get ApplicationSets + argocd appset get APPSETNAME +``` + ### Options ``` @@ -21,6 +30,7 @@ argocd appset get APPSETNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -33,8 +43,12 @@ argocd appset get APPSETNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_appset_list.md b/docs/user-guide/commands/argocd_appset_list.md index 1320ccc1b7883..92e0b21cb749b 100644 --- a/docs/user-guide/commands/argocd_appset_list.md +++ b/docs/user-guide/commands/argocd_appset_list.md @@ -1,3 +1,5 @@ +# `argocd appset list` Command Reference + ## argocd appset list List ApplicationSets @@ -16,10 +18,11 @@ argocd appset list [flags] ### Options ``` - -h, --help help for list - -o, --output string Output format. One of: wide|name|json|yaml (default "wide") - -p, --project stringArray Filter by project name - -l, --selector string List applicationsets by label + -N, --appset-namespace string Only list applicationsets in namespace + -h, --help help for list + -o, --output string Output format. One of: wide|name|json|yaml (default "wide") + -p, --project stringArray Filter by project name + -l, --selector string List applicationsets by label ``` ### Options inherited from parent commands @@ -29,6 +32,7 @@ argocd appset list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -41,8 +45,12 @@ argocd appset list [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_cert.md b/docs/user-guide/commands/argocd_cert.md index 830434528237e..b126328a4372f 100644 --- a/docs/user-guide/commands/argocd_cert.md +++ b/docs/user-guide/commands/argocd_cert.md @@ -1,3 +1,5 @@ +# `argocd cert` Command Reference + ## argocd cert Manage repository certificates and SSH known hosts entries @@ -40,6 +42,7 @@ argocd cert [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for cert --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -60,6 +63,7 @@ argocd cert [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -72,8 +76,12 @@ argocd cert [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_cert_add-ssh.md b/docs/user-guide/commands/argocd_cert_add-ssh.md index 8fdd6bf655d63..94daf24bf376e 100644 --- a/docs/user-guide/commands/argocd_cert_add-ssh.md +++ b/docs/user-guide/commands/argocd_cert_add-ssh.md @@ -1,3 +1,5 @@ +# `argocd cert add-ssh` Command Reference + ## argocd cert add-ssh Add SSH known host entries for repository servers @@ -22,6 +24,7 @@ argocd cert add-ssh --batch [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -34,8 +37,12 @@ argocd cert add-ssh --batch [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_cert_add-tls.md b/docs/user-guide/commands/argocd_cert_add-tls.md index 0500e19c071dd..e8d3d733697e7 100644 --- a/docs/user-guide/commands/argocd_cert_add-tls.md +++ b/docs/user-guide/commands/argocd_cert_add-tls.md @@ -1,3 +1,5 @@ +# `argocd cert add-tls` Command Reference + ## argocd cert add-tls Add TLS certificate data for connecting to repository server SERVERNAME @@ -9,7 +11,7 @@ argocd cert add-tls SERVERNAME [flags] ### Options ``` - --from string read TLS certificate data from file (default is to read from stdin) + --from string Read TLS certificate data from file (default is to read from stdin) -h, --help help for add-tls --upsert Replace existing TLS certificate if certificate is different in input ``` @@ -21,6 +23,7 @@ argocd cert add-tls SERVERNAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -33,8 +36,12 @@ argocd cert add-tls SERVERNAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_cert_list.md b/docs/user-guide/commands/argocd_cert_list.md index 8aed9cc7ab61f..43a4af5bea783 100644 --- a/docs/user-guide/commands/argocd_cert_list.md +++ b/docs/user-guide/commands/argocd_cert_list.md @@ -1,3 +1,5 @@ +# `argocd cert list` Command Reference + ## argocd cert list List configured certificates @@ -9,11 +11,11 @@ argocd cert list [flags] ### Options ``` - --cert-type string only list certificates of given type, valid: 'ssh','https' + --cert-type string Only list certificates of given type, valid: 'ssh','https' -h, --help help for list - --hostname-pattern string only list certificates for hosts matching given glob-pattern + --hostname-pattern string Only list certificates for hosts matching given glob-pattern -o, --output string Output format. One of: json|yaml|wide (default "wide") - --sort string set display sort order for output format wide. One of: hostname|type + --sort string Set display sort order for output format wide. One of: hostname|type ``` ### Options inherited from parent commands @@ -23,6 +25,7 @@ argocd cert list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -35,8 +38,12 @@ argocd cert list [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_cert_rm.md b/docs/user-guide/commands/argocd_cert_rm.md index 1baad03d7ea3e..602a84aa6b85c 100644 --- a/docs/user-guide/commands/argocd_cert_rm.md +++ b/docs/user-guide/commands/argocd_cert_rm.md @@ -1,3 +1,5 @@ +# `argocd cert rm` Command Reference + ## argocd cert rm Remove certificate of TYPE for REPOSERVER @@ -21,6 +23,7 @@ argocd cert rm REPOSERVER [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -33,8 +36,12 @@ argocd cert rm REPOSERVER [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_cluster.md b/docs/user-guide/commands/argocd_cluster.md index f9c1681a82475..a30c357d54d71 100644 --- a/docs/user-guide/commands/argocd_cluster.md +++ b/docs/user-guide/commands/argocd_cluster.md @@ -1,3 +1,5 @@ +# `argocd cluster` Command Reference + ## argocd cluster Manage cluster credentials @@ -37,6 +39,7 @@ argocd cluster [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for cluster --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -57,6 +60,7 @@ argocd cluster [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -69,8 +73,12 @@ argocd cluster [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_cluster_add.md b/docs/user-guide/commands/argocd_cluster_add.md index 827b3344eb81c..8a80a12f5a4d5 100644 --- a/docs/user-guide/commands/argocd_cluster_add.md +++ b/docs/user-guide/commands/argocd_cluster_add.md @@ -1,3 +1,5 @@ +# `argocd cluster add` Command Reference + ## argocd cluster add argocd cluster add CONTEXT @@ -11,7 +13,9 @@ argocd cluster add CONTEXT [flags] ``` --annotation stringArray Set metadata annotations (e.g. --annotation key=value) --aws-cluster-name string AWS Cluster name if set then aws cli eks token command will be used to access cluster + --aws-profile string Optional AWS profile. If set then AWS IAM Authenticator uses this profile to perform cluster operations instead of the default AWS credential provider chain. --aws-role-arn string Optional AWS role arn. If set then AWS IAM Authenticator assumes a role to perform cluster operations instead of the default AWS credential provider chain. + --cluster-endpoint string Cluster endpoint to use. Can be one of the following: 'kubeconfig', 'kube-public', or 'internal'. --cluster-resources Indicates if cluster level resources should be managed. The setting is used only if list of managed namespaces is not empty. --exec-command string 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. --exec-command-api-version string Preferred input version of the ExecInfo for the --exec-command executable @@ -39,6 +43,7 @@ argocd cluster add CONTEXT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -51,8 +56,12 @@ argocd cluster add CONTEXT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_cluster_get.md b/docs/user-guide/commands/argocd_cluster_get.md index b4e172d511da7..a020a94557e99 100644 --- a/docs/user-guide/commands/argocd_cluster_get.md +++ b/docs/user-guide/commands/argocd_cluster_get.md @@ -1,3 +1,5 @@ +# `argocd cluster get` Command Reference + ## argocd cluster get Get cluster information @@ -27,6 +29,7 @@ argocd cluster get in-cluster --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -39,8 +42,12 @@ argocd cluster get in-cluster --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_cluster_list.md b/docs/user-guide/commands/argocd_cluster_list.md index 7683c909e59d2..9779a4fb8af0b 100644 --- a/docs/user-guide/commands/argocd_cluster_list.md +++ b/docs/user-guide/commands/argocd_cluster_list.md @@ -1,3 +1,5 @@ +# `argocd cluster list` Command Reference + ## argocd cluster list List configured clusters @@ -6,6 +8,28 @@ List configured clusters argocd cluster list [flags] ``` +### Examples + +``` + +# List Clusters in Default "Wide" Format +argocd cluster list + +# List Cluster via specifing the server +argocd cluster list --server + +# List Clusters in JSON Format +argocd cluster list -o json --server + +# List Clusters in YAML Format +argocd cluster list -o yaml --server + +# List Clusters that have been added to your Argo CD +argocd cluster list -o server + + +``` + ### Options ``` @@ -20,6 +44,7 @@ argocd cluster list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +57,12 @@ argocd cluster list [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_cluster_rm.md b/docs/user-guide/commands/argocd_cluster_rm.md index 9c17e3f5f56c5..80057bb5a7082 100644 --- a/docs/user-guide/commands/argocd_cluster_rm.md +++ b/docs/user-guide/commands/argocd_cluster_rm.md @@ -1,3 +1,5 @@ +# `argocd cluster rm` Command Reference + ## argocd cluster rm Remove cluster credentials @@ -27,6 +29,7 @@ argocd cluster rm cluster-name --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -39,8 +42,12 @@ argocd cluster rm cluster-name --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_cluster_rotate-auth.md b/docs/user-guide/commands/argocd_cluster_rotate-auth.md index 41ca1015bc9c2..8dc3a5bf745d3 100644 --- a/docs/user-guide/commands/argocd_cluster_rotate-auth.md +++ b/docs/user-guide/commands/argocd_cluster_rotate-auth.md @@ -1,3 +1,5 @@ +# `argocd cluster rotate-auth` Command Reference + ## argocd cluster rotate-auth argocd cluster rotate-auth SERVER/NAME @@ -26,6 +28,7 @@ argocd cluster rotate-auth cluster-name --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -38,8 +41,12 @@ argocd cluster rotate-auth cluster-name --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_cluster_set.md b/docs/user-guide/commands/argocd_cluster_set.md index f1099fd6e3cc3..2dba9c2ad16e8 100644 --- a/docs/user-guide/commands/argocd_cluster_set.md +++ b/docs/user-guide/commands/argocd_cluster_set.md @@ -1,3 +1,5 @@ +# `argocd cluster set` Command Reference + ## argocd cluster set Set cluster information @@ -29,6 +31,7 @@ argocd cluster set NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -41,8 +44,12 @@ argocd cluster set NAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_completion.md b/docs/user-guide/commands/argocd_completion.md index 24c838913be1e..3d6d981ef4c8f 100644 --- a/docs/user-guide/commands/argocd_completion.md +++ b/docs/user-guide/commands/argocd_completion.md @@ -1,3 +1,5 @@ +# `argocd completion` Command Reference + ## argocd completion output shell completion code for the specified shell (bash or zsh) @@ -11,14 +13,31 @@ To access completions in your current shell, run $ source <(argocd completion bash) Alternatively, write it to a file and source in .bash_profile -For zsh, output to a file in a directory referenced by the $fpath shell -variable. +For zsh, add the following to your ~/.zshrc file: +source <(argocd completion zsh) +compdef _argocd argocd + +Optionally, also add the following, in case you are getting errors involving compdef & compinit such as command not found: compdef: +autoload -Uz compinit +compinit ``` argocd completion SHELL [flags] ``` +### Examples + +``` +# For bash +$ source <(argocd completion bash) + +# For zsh +$ argocd completion zsh > _argocd +$ source _argocd + +``` + ### Options ``` @@ -32,6 +51,7 @@ argocd completion SHELL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -44,8 +64,12 @@ argocd completion SHELL [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_context.md b/docs/user-guide/commands/argocd_context.md index 91ff497679855..f02944cf4f775 100644 --- a/docs/user-guide/commands/argocd_context.md +++ b/docs/user-guide/commands/argocd_context.md @@ -1,3 +1,5 @@ +# `argocd context` Command Reference + ## argocd context Switch between contexts @@ -6,6 +8,19 @@ Switch between contexts argocd context [CONTEXT] [flags] ``` +### Examples + +``` +# List Argo CD Contexts +argocd context + +# Switch Argo CD context +argocd context cd.argoproj.io + +# Delete Argo CD context +argocd context cd.argoproj.io --delete +``` + ### Options ``` @@ -20,6 +35,7 @@ argocd context [CONTEXT] [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +48,12 @@ argocd context [CONTEXT] [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_gpg.md b/docs/user-guide/commands/argocd_gpg.md index 99549701187e6..bca15e98b7c87 100644 --- a/docs/user-guide/commands/argocd_gpg.md +++ b/docs/user-guide/commands/argocd_gpg.md @@ -1,3 +1,5 @@ +# `argocd gpg` Command Reference + ## argocd gpg Manage GPG keys used for signature verification @@ -17,6 +19,7 @@ argocd gpg [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for gpg --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -37,6 +40,7 @@ argocd gpg [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -49,8 +53,12 @@ argocd gpg [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_gpg_add.md b/docs/user-guide/commands/argocd_gpg_add.md index 9fb856d150cbc..3ef5d4e6c72d5 100644 --- a/docs/user-guide/commands/argocd_gpg_add.md +++ b/docs/user-guide/commands/argocd_gpg_add.md @@ -1,3 +1,5 @@ +# `argocd gpg add` Command Reference + ## argocd gpg add Adds a GPG public key to the server's keyring @@ -6,6 +8,13 @@ Adds a GPG public key to the server's keyring argocd gpg add [flags] ``` +### Examples + +``` + # Add a GPG public key to the server's keyring from a file. + argocd gpg add --from /path/to/keyfile +``` + ### Options ``` @@ -20,6 +29,7 @@ argocd gpg add [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +42,12 @@ argocd gpg add [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_gpg_get.md b/docs/user-guide/commands/argocd_gpg_get.md index 4cd1e78f59426..e0ad3d9ee25d6 100644 --- a/docs/user-guide/commands/argocd_gpg_get.md +++ b/docs/user-guide/commands/argocd_gpg_get.md @@ -1,3 +1,5 @@ +# `argocd gpg get` Command Reference + ## argocd gpg get Get the GPG public key with ID from the server @@ -6,6 +8,19 @@ Get the GPG public key with ID from the server argocd gpg get KEYID [flags] ``` +### Examples + +``` + # Get a GPG public key with the specified KEYID in wide format (default). + argocd gpg get KEYID + + # Get a GPG public key with the specified KEYID in JSON format. + argocd gpg get KEYID -o json + + # Get a GPG public key with the specified KEYID in YAML format. + argocd gpg get KEYID -o yaml +``` + ### Options ``` @@ -20,6 +35,7 @@ argocd gpg get KEYID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +48,12 @@ argocd gpg get KEYID [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_gpg_list.md b/docs/user-guide/commands/argocd_gpg_list.md index 0064c780dc652..50f0e72e83c0d 100644 --- a/docs/user-guide/commands/argocd_gpg_list.md +++ b/docs/user-guide/commands/argocd_gpg_list.md @@ -1,3 +1,5 @@ +# `argocd gpg list` Command Reference + ## argocd gpg list List configured GPG public keys @@ -6,6 +8,19 @@ List configured GPG public keys argocd gpg list [flags] ``` +### Examples + +``` + # List all configured GPG public keys in wide format (default). + argocd gpg list + + # List all configured GPG public keys in JSON format. + argocd gpg list -o json + + # List all configured GPG public keys in YAML format. + argocd gpg list -o yaml +``` + ### Options ``` @@ -20,6 +35,7 @@ argocd gpg list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +48,12 @@ argocd gpg list [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_gpg_rm.md b/docs/user-guide/commands/argocd_gpg_rm.md index d84def9d2c2fc..ecf744988d0fd 100644 --- a/docs/user-guide/commands/argocd_gpg_rm.md +++ b/docs/user-guide/commands/argocd_gpg_rm.md @@ -1,3 +1,5 @@ +# `argocd gpg rm` Command Reference + ## argocd gpg rm Removes a GPG public key from the server's keyring @@ -19,6 +21,7 @@ argocd gpg rm KEYID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +34,12 @@ argocd gpg rm KEYID [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_login.md b/docs/user-guide/commands/argocd_login.md index d99a6b433313a..adf02fefbc454 100644 --- a/docs/user-guide/commands/argocd_login.md +++ b/docs/user-guide/commands/argocd_login.md @@ -1,3 +1,5 @@ +# `argocd login` Command Reference + ## argocd login Log in to Argo CD @@ -27,12 +29,12 @@ argocd login cd.argoproj.io --core ``` -h, --help help for login - --name string name to use for the context - --password string the password of an account to authenticate + --name string Name to use for the context + --password string The password of an account to authenticate --skip-test-tls Skip testing whether the server is configured with TLS (this can help when the command hangs for no apparent reason) - --sso perform SSO login - --sso-port int port to run local OAuth2 login application (default 8085) - --username string the username of an account to authenticate + --sso Perform SSO login + --sso-port int Port to run local OAuth2 login application (default 8085) + --username string The username of an account to authenticate ``` ### Options inherited from parent commands @@ -42,6 +44,7 @@ argocd login cd.argoproj.io --core --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -54,8 +57,12 @@ argocd login cd.argoproj.io --core --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_logout.md b/docs/user-guide/commands/argocd_logout.md index aa8dc75f463d5..3e18c70a063a0 100644 --- a/docs/user-guide/commands/argocd_logout.md +++ b/docs/user-guide/commands/argocd_logout.md @@ -1,3 +1,5 @@ +# `argocd logout` Command Reference + ## argocd logout Log out from Argo CD @@ -10,6 +12,15 @@ Log out from Argo CD argocd logout CONTEXT [flags] ``` +### Examples + +``` +# To log out of argocd +$ argocd logout +# This can be helpful for security reasons or when you want to switch between different Argo CD contexts or accounts. + +``` + ### Options ``` @@ -23,6 +34,7 @@ argocd logout CONTEXT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -35,8 +47,12 @@ argocd logout CONTEXT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj.md b/docs/user-guide/commands/argocd_proj.md index 549d08ec8f77b..5586463adee6e 100644 --- a/docs/user-guide/commands/argocd_proj.md +++ b/docs/user-guide/commands/argocd_proj.md @@ -1,3 +1,5 @@ +# `argocd proj` Command Reference + ## argocd proj Manage projects @@ -6,6 +8,22 @@ Manage projects argocd proj [flags] ``` +### Examples + +``` + # List all available projects + argocd proj list + + # Create a new project with name PROJECT + argocd proj create PROJECT + + # Delete the project with name PROJECT + argocd proj delete PROJECT + + # Edit the information on project with name PROJECT + argocd proj edit PROJECT +``` + ### Options ``` @@ -17,6 +35,7 @@ argocd proj [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for proj --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -37,6 +56,7 @@ argocd proj [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -49,8 +69,12 @@ argocd proj [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO @@ -60,6 +84,7 @@ argocd proj [flags] * [argocd proj add-orphaned-ignore](argocd_proj_add-orphaned-ignore.md) - Add a resource to orphaned ignore list * [argocd proj add-signature-key](argocd_proj_add-signature-key.md) - Add GnuPG signature key to project * [argocd proj add-source](argocd_proj_add-source.md) - Add project source repository +* [argocd proj add-source-namespace](argocd_proj_add-source-namespace.md) - Add source namespace to the AppProject * [argocd proj allow-cluster-resource](argocd_proj_allow-cluster-resource.md) - Adds a cluster-scoped API resource to the allow list and removes it from deny list * [argocd proj allow-namespace-resource](argocd_proj_allow-namespace-resource.md) - Removes a namespaced API resource from the deny list or add a namespaced API resource to the allow list * [argocd proj create](argocd_proj_create.md) - Create a project @@ -73,6 +98,7 @@ argocd proj [flags] * [argocd proj remove-orphaned-ignore](argocd_proj_remove-orphaned-ignore.md) - Remove a resource from orphaned ignore list * [argocd proj remove-signature-key](argocd_proj_remove-signature-key.md) - Remove GnuPG signature key from project * [argocd proj remove-source](argocd_proj_remove-source.md) - Remove project source repository +* [argocd proj remove-source-namespace](argocd_proj_remove-source-namespace.md) - Removes the source namespace from the AppProject * [argocd proj role](argocd_proj_role.md) - Manage a project's roles * [argocd proj set](argocd_proj_set.md) - Set project parameters * [argocd proj windows](argocd_proj_windows.md) - Manage a project's sync windows diff --git a/docs/user-guide/commands/argocd_proj_add-destination.md b/docs/user-guide/commands/argocd_proj_add-destination.md index 03a287d50886d..688aebf84156e 100644 --- a/docs/user-guide/commands/argocd_proj_add-destination.md +++ b/docs/user-guide/commands/argocd_proj_add-destination.md @@ -1,3 +1,5 @@ +# `argocd proj add-destination` Command Reference + ## argocd proj add-destination Add project destination @@ -6,6 +8,16 @@ Add project destination argocd proj add-destination PROJECT SERVER/NAME NAMESPACE [flags] ``` +### Examples + +``` + # Add project destination using a server URL (SERVER) in the specified namespace (NAMESPACE) on the project with name PROJECT + argocd proj add-destination PROJECT SERVER NAMESPACE + + # Add project destination using a server name (NAME) in the specified namespace (NAMESPACE) on the project with name PROJECT + argocd proj add-destination PROJECT NAME NAMESPACE --name +``` + ### Options ``` @@ -20,6 +32,7 @@ argocd proj add-destination PROJECT SERVER/NAME NAMESPACE [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +45,12 @@ argocd proj add-destination PROJECT SERVER/NAME NAMESPACE [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_add-orphaned-ignore.md b/docs/user-guide/commands/argocd_proj_add-orphaned-ignore.md index ed4b0ca5142cc..1b36d8a5ff0f1 100644 --- a/docs/user-guide/commands/argocd_proj_add-orphaned-ignore.md +++ b/docs/user-guide/commands/argocd_proj_add-orphaned-ignore.md @@ -1,3 +1,5 @@ +# `argocd proj add-orphaned-ignore` Command Reference + ## argocd proj add-orphaned-ignore Add a resource to orphaned ignore list @@ -6,6 +8,16 @@ Add a resource to orphaned ignore list argocd proj add-orphaned-ignore PROJECT GROUP KIND [flags] ``` +### Examples + +``` + # Add a resource of the specified GROUP and KIND to orphaned ignore list on the project with name PROJECT + argocd proj add-orphaned-ignore PROJECT GROUP KIND + + # Add resources of the specified GROUP and KIND using a NAME pattern to orphaned ignore list on the project with name PROJECT + argocd proj add-orphaned-ignore PROJECT GROUP KIND --name NAME +``` + ### Options ``` @@ -20,6 +32,7 @@ argocd proj add-orphaned-ignore PROJECT GROUP KIND [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +45,12 @@ argocd proj add-orphaned-ignore PROJECT GROUP KIND [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_add-signature-key.md b/docs/user-guide/commands/argocd_proj_add-signature-key.md index c594fccc1343a..404660510700b 100644 --- a/docs/user-guide/commands/argocd_proj_add-signature-key.md +++ b/docs/user-guide/commands/argocd_proj_add-signature-key.md @@ -1,3 +1,5 @@ +# `argocd proj add-signature-key` Command Reference + ## argocd proj add-signature-key Add GnuPG signature key to project @@ -6,6 +8,13 @@ Add GnuPG signature key to project argocd proj add-signature-key PROJECT KEY-ID [flags] ``` +### Examples + +``` + # Add GnuPG signature key KEY-ID to project PROJECT + argocd proj add-signature-key PROJECT KEY-ID +``` + ### Options ``` @@ -19,6 +28,7 @@ argocd proj add-signature-key PROJECT KEY-ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +41,12 @@ argocd proj add-signature-key PROJECT KEY-ID [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_add-source-namespace.md b/docs/user-guide/commands/argocd_proj_add-source-namespace.md new file mode 100644 index 0000000000000..ced1f6fa3c67d --- /dev/null +++ b/docs/user-guide/commands/argocd_proj_add-source-namespace.md @@ -0,0 +1,55 @@ +# `argocd proj add-source-namespace` Command Reference + +## argocd proj add-source-namespace + +Add source namespace to the AppProject + +``` +argocd proj add-source-namespace PROJECT NAMESPACE [flags] +``` + +### Examples + +``` + # Add Kubernetes namespace as source namespace to the AppProject where application resources are allowed to be created in. + argocd proj add-source-namespace PROJECT NAMESPACE +``` + +### Options + +``` + -h, --help help for add-source-namespace +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --kube-context string Directs the command to the given kube-context + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") + --server string Argo CD server address + --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") +``` + +### SEE ALSO + +* [argocd proj](argocd_proj.md) - Manage projects + diff --git a/docs/user-guide/commands/argocd_proj_add-source.md b/docs/user-guide/commands/argocd_proj_add-source.md index c74a056a43654..f0c2f18fd9792 100644 --- a/docs/user-guide/commands/argocd_proj_add-source.md +++ b/docs/user-guide/commands/argocd_proj_add-source.md @@ -1,3 +1,5 @@ +# `argocd proj add-source` Command Reference + ## argocd proj add-source Add project source repository @@ -6,6 +8,13 @@ Add project source repository argocd proj add-source PROJECT URL [flags] ``` +### Examples + +``` + # Add a source repository (URL) to the project with name PROJECT + argocd proj add-source PROJECT URL +``` + ### Options ``` @@ -19,6 +28,7 @@ argocd proj add-source PROJECT URL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +41,12 @@ argocd proj add-source PROJECT URL [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_allow-cluster-resource.md b/docs/user-guide/commands/argocd_proj_allow-cluster-resource.md index ad1c2a641f9a8..338027e724bc2 100644 --- a/docs/user-guide/commands/argocd_proj_allow-cluster-resource.md +++ b/docs/user-guide/commands/argocd_proj_allow-cluster-resource.md @@ -1,3 +1,5 @@ +# `argocd proj allow-cluster-resource` Command Reference + ## argocd proj allow-cluster-resource Adds a cluster-scoped API resource to the allow list and removes it from deny list @@ -6,6 +8,13 @@ Adds a cluster-scoped API resource to the allow list and removes it from deny li argocd proj allow-cluster-resource PROJECT GROUP KIND [flags] ``` +### Examples + +``` + # Adds a cluster-scoped API resource with specified GROUP and KIND to the allow list and removes it from deny list for project PROJECT + argocd proj allow-cluster-resource PROJECT GROUP KIND +``` + ### Options ``` @@ -20,6 +29,7 @@ argocd proj allow-cluster-resource PROJECT GROUP KIND [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +42,12 @@ argocd proj allow-cluster-resource PROJECT GROUP KIND [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_allow-namespace-resource.md b/docs/user-guide/commands/argocd_proj_allow-namespace-resource.md index bd1783892a751..3e4a410f32a47 100644 --- a/docs/user-guide/commands/argocd_proj_allow-namespace-resource.md +++ b/docs/user-guide/commands/argocd_proj_allow-namespace-resource.md @@ -1,3 +1,5 @@ +# `argocd proj allow-namespace-resource` Command Reference + ## argocd proj allow-namespace-resource Removes a namespaced API resource from the deny list or add a namespaced API resource to the allow list @@ -6,6 +8,13 @@ Removes a namespaced API resource from the deny list or add a namespaced API res argocd proj allow-namespace-resource PROJECT GROUP KIND [flags] ``` +### Examples + +``` + # Removes a namespaced API resource with specified GROUP and KIND from the deny list or add a namespaced API resource to the allow list for project PROJECT + argocd proj allow-namespace-resource PROJECT GROUP KIND +``` + ### Options ``` @@ -20,6 +29,7 @@ argocd proj allow-namespace-resource PROJECT GROUP KIND [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +42,12 @@ argocd proj allow-namespace-resource PROJECT GROUP KIND [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_create.md b/docs/user-guide/commands/argocd_proj_create.md index fa9ac85afe9f5..fd8687c1b2982 100644 --- a/docs/user-guide/commands/argocd_proj_create.md +++ b/docs/user-guide/commands/argocd_proj_create.md @@ -1,3 +1,5 @@ +# `argocd proj create` Command Reference + ## argocd proj create Create a project @@ -6,6 +8,16 @@ Create a project argocd proj create PROJECT [flags] ``` +### Examples + +``` + # Create a new project with name PROJECT + argocd proj create PROJECT + + # Create a new project with name PROJECT from a file or URL to a Kubernetes manifest + argocd proj create PROJECT -f FILE|URL +``` + ### Options ``` @@ -32,6 +44,7 @@ argocd proj create PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -44,8 +57,12 @@ argocd proj create PROJECT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_delete.md b/docs/user-guide/commands/argocd_proj_delete.md index a36b57aeb9455..4d6c4a94f609c 100644 --- a/docs/user-guide/commands/argocd_proj_delete.md +++ b/docs/user-guide/commands/argocd_proj_delete.md @@ -1,3 +1,5 @@ +# `argocd proj delete` Command Reference + ## argocd proj delete Delete project @@ -6,6 +8,13 @@ Delete project argocd proj delete PROJECT [flags] ``` +### Examples + +``` + # Delete the project with name PROJECT + argocd proj delete PROJECT +``` + ### Options ``` @@ -19,6 +28,7 @@ argocd proj delete PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +41,12 @@ argocd proj delete PROJECT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_deny-cluster-resource.md b/docs/user-guide/commands/argocd_proj_deny-cluster-resource.md index 25db97712b0db..4621b18c3efe1 100644 --- a/docs/user-guide/commands/argocd_proj_deny-cluster-resource.md +++ b/docs/user-guide/commands/argocd_proj_deny-cluster-resource.md @@ -1,3 +1,5 @@ +# `argocd proj deny-cluster-resource` Command Reference + ## argocd proj deny-cluster-resource Removes a cluster-scoped API resource from the allow list and adds it to deny list @@ -6,6 +8,13 @@ Removes a cluster-scoped API resource from the allow list and adds it to deny li argocd proj deny-cluster-resource PROJECT GROUP KIND [flags] ``` +### Examples + +``` + # Removes a cluster-scoped API resource with specified GROUP and KIND from the allow list and adds it to deny list for project PROJECT + argocd proj deny-cluster-resource PROJECT GROUP KIND +``` + ### Options ``` @@ -20,6 +29,7 @@ argocd proj deny-cluster-resource PROJECT GROUP KIND [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +42,12 @@ argocd proj deny-cluster-resource PROJECT GROUP KIND [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_deny-namespace-resource.md b/docs/user-guide/commands/argocd_proj_deny-namespace-resource.md index bf4fa77797d87..e8b55a7b0adb6 100644 --- a/docs/user-guide/commands/argocd_proj_deny-namespace-resource.md +++ b/docs/user-guide/commands/argocd_proj_deny-namespace-resource.md @@ -1,3 +1,5 @@ +# `argocd proj deny-namespace-resource` Command Reference + ## argocd proj deny-namespace-resource Adds a namespaced API resource to the deny list or removes a namespaced API resource from the allow list @@ -6,6 +8,13 @@ Adds a namespaced API resource to the deny list or removes a namespaced API reso argocd proj deny-namespace-resource PROJECT GROUP KIND [flags] ``` +### Examples + +``` + # Adds a namespaced API resource with specified GROUP and KIND from the deny list or removes a namespaced API resource from the allow list for project PROJECT + argocd proj deny-namespace-resource PROJECT GROUP KIND +``` + ### Options ``` @@ -20,6 +29,7 @@ argocd proj deny-namespace-resource PROJECT GROUP KIND [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +42,12 @@ argocd proj deny-namespace-resource PROJECT GROUP KIND [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_edit.md b/docs/user-guide/commands/argocd_proj_edit.md index 170fc5abfa8e0..63a584accfad8 100644 --- a/docs/user-guide/commands/argocd_proj_edit.md +++ b/docs/user-guide/commands/argocd_proj_edit.md @@ -1,3 +1,5 @@ +# `argocd proj edit` Command Reference + ## argocd proj edit Edit project @@ -6,6 +8,13 @@ Edit project argocd proj edit PROJECT [flags] ``` +### Examples + +``` + # Edit the information on project with name PROJECT + argocd proj edit PROJECT +``` + ### Options ``` @@ -19,6 +28,7 @@ argocd proj edit PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +41,12 @@ argocd proj edit PROJECT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_get.md b/docs/user-guide/commands/argocd_proj_get.md index d9feeeffab02d..2d2e437b79779 100644 --- a/docs/user-guide/commands/argocd_proj_get.md +++ b/docs/user-guide/commands/argocd_proj_get.md @@ -1,3 +1,5 @@ +# `argocd proj get` Command Reference + ## argocd proj get Get project details @@ -6,6 +8,16 @@ Get project details argocd proj get PROJECT [flags] ``` +### Examples + +``` + # Get details from project PROJECT + argocd proj get PROJECT + + # Get details from project PROJECT in yaml format + argocd proj get PROJECT -o yaml +``` + ### Options ``` @@ -20,6 +32,7 @@ argocd proj get PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +45,12 @@ argocd proj get PROJECT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_list.md b/docs/user-guide/commands/argocd_proj_list.md index a82e1bd3407e4..d96c0c4bb13b8 100644 --- a/docs/user-guide/commands/argocd_proj_list.md +++ b/docs/user-guide/commands/argocd_proj_list.md @@ -1,3 +1,5 @@ +# `argocd proj list` Command Reference + ## argocd proj list List projects @@ -6,6 +8,16 @@ List projects argocd proj list [flags] ``` +### Examples + +``` + # List all available projects + argocd proj list + + # List all available projects in yaml format + argocd proj list -o yaml +``` + ### Options ``` @@ -20,6 +32,7 @@ argocd proj list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +45,12 @@ argocd proj list [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_remove-destination.md b/docs/user-guide/commands/argocd_proj_remove-destination.md index be4ed17477532..612e1db68356a 100644 --- a/docs/user-guide/commands/argocd_proj_remove-destination.md +++ b/docs/user-guide/commands/argocd_proj_remove-destination.md @@ -1,3 +1,5 @@ +# `argocd proj remove-destination` Command Reference + ## argocd proj remove-destination Remove project destination @@ -6,6 +8,13 @@ Remove project destination argocd proj remove-destination PROJECT SERVER NAMESPACE [flags] ``` +### Examples + +``` + # Remove the destination (SERVER) from the specified namespace (NAMESPACE) on the project with name PROJECT + argocd proj remove-destination PROJECT SERVER NAMESPACE +``` + ### Options ``` @@ -19,6 +28,7 @@ argocd proj remove-destination PROJECT SERVER NAMESPACE [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +41,12 @@ argocd proj remove-destination PROJECT SERVER NAMESPACE [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_remove-orphaned-ignore.md b/docs/user-guide/commands/argocd_proj_remove-orphaned-ignore.md index c63d59d6e0fbf..857cf3c595177 100644 --- a/docs/user-guide/commands/argocd_proj_remove-orphaned-ignore.md +++ b/docs/user-guide/commands/argocd_proj_remove-orphaned-ignore.md @@ -1,9 +1,21 @@ +# `argocd proj remove-orphaned-ignore` Command Reference + ## argocd proj remove-orphaned-ignore Remove a resource from orphaned ignore list ``` -argocd proj remove-orphaned-ignore PROJECT GROUP KIND NAME [flags] +argocd proj remove-orphaned-ignore PROJECT GROUP KIND [flags] +``` + +### Examples + +``` + # Remove a resource of the specified GROUP and KIND from orphaned ignore list on the project with name PROJECT + argocd proj remove-orphaned-ignore PROJECT GROUP KIND + + # Remove resources of the specified GROUP and KIND using a NAME pattern from orphaned ignore list on the project with name PROJECT + argocd proj remove-orphaned-ignore PROJECT GROUP KIND --name NAME ``` ### Options @@ -20,6 +32,7 @@ argocd proj remove-orphaned-ignore PROJECT GROUP KIND NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +45,12 @@ argocd proj remove-orphaned-ignore PROJECT GROUP KIND NAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_remove-signature-key.md b/docs/user-guide/commands/argocd_proj_remove-signature-key.md index 9dfa69174fe26..61d6085022662 100644 --- a/docs/user-guide/commands/argocd_proj_remove-signature-key.md +++ b/docs/user-guide/commands/argocd_proj_remove-signature-key.md @@ -1,3 +1,5 @@ +# `argocd proj remove-signature-key` Command Reference + ## argocd proj remove-signature-key Remove GnuPG signature key from project @@ -6,6 +8,13 @@ Remove GnuPG signature key from project argocd proj remove-signature-key PROJECT KEY-ID [flags] ``` +### Examples + +``` + # Remove GnuPG signature key KEY-ID from project PROJECT + argocd proj remove-signature-key PROJECT KEY-ID +``` + ### Options ``` @@ -19,6 +28,7 @@ argocd proj remove-signature-key PROJECT KEY-ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +41,12 @@ argocd proj remove-signature-key PROJECT KEY-ID [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_remove-source-namespace.md b/docs/user-guide/commands/argocd_proj_remove-source-namespace.md new file mode 100644 index 0000000000000..6a0ee319c7b9b --- /dev/null +++ b/docs/user-guide/commands/argocd_proj_remove-source-namespace.md @@ -0,0 +1,55 @@ +# `argocd proj remove-source-namespace` Command Reference + +## argocd proj remove-source-namespace + +Removes the source namespace from the AppProject + +``` +argocd proj remove-source-namespace PROJECT NAMESPACE [flags] +``` + +### Examples + +``` + # Remove source NAMESPACE in PROJECT + argocd proj remove-source-namespace PROJECT NAMESPACE +``` + +### Options + +``` + -h, --help help for remove-source-namespace +``` + +### Options inherited from parent commands + +``` + --auth-token string Authentication token + --client-crt string Client certificate file + --client-crt-key string Client certificate key file + --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") + --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server + --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. + --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. + -H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) + --http-retry-max int Maximum number of retries to establish http connection to Argo CD server + --insecure Skip server certificate and domain verification + --kube-context string Directs the command to the given kube-context + --logformat string Set the logging format. One of: text|json (default "text") + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + --plaintext Disable TLS + --port-forward Connect to a random argocd-server port using port forwarding + --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") + --server string Argo CD server address + --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") +``` + +### SEE ALSO + +* [argocd proj](argocd_proj.md) - Manage projects + diff --git a/docs/user-guide/commands/argocd_proj_remove-source.md b/docs/user-guide/commands/argocd_proj_remove-source.md index 37b78d8b87c90..d6a1c353059f3 100644 --- a/docs/user-guide/commands/argocd_proj_remove-source.md +++ b/docs/user-guide/commands/argocd_proj_remove-source.md @@ -1,3 +1,5 @@ +# `argocd proj remove-source` Command Reference + ## argocd proj remove-source Remove project source repository @@ -6,6 +8,13 @@ Remove project source repository argocd proj remove-source PROJECT URL [flags] ``` +### Examples + +``` + # Remove URL source repository to project PROJECT + argocd proj remove-source PROJECT URL +``` + ### Options ``` @@ -19,6 +28,7 @@ argocd proj remove-source PROJECT URL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +41,12 @@ argocd proj remove-source PROJECT URL [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_role.md b/docs/user-guide/commands/argocd_proj_role.md index a8d1ba5c2ac66..9546cc4b7ab27 100644 --- a/docs/user-guide/commands/argocd_proj_role.md +++ b/docs/user-guide/commands/argocd_proj_role.md @@ -1,3 +1,5 @@ +# `argocd proj role` Command Reference + ## argocd proj role Manage a project's roles @@ -19,6 +21,7 @@ argocd proj role [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +34,12 @@ argocd proj role [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_role_add-group.md b/docs/user-guide/commands/argocd_proj_role_add-group.md index c85bc8630fabc..4a3aa2f1e8be1 100644 --- a/docs/user-guide/commands/argocd_proj_role_add-group.md +++ b/docs/user-guide/commands/argocd_proj_role_add-group.md @@ -1,3 +1,5 @@ +# `argocd proj role add-group` Command Reference + ## argocd proj role add-group Add a group claim to a project role @@ -19,6 +21,7 @@ argocd proj role add-group PROJECT ROLE-NAME GROUP-CLAIM [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +34,12 @@ argocd proj role add-group PROJECT ROLE-NAME GROUP-CLAIM [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_role_add-policy.md b/docs/user-guide/commands/argocd_proj_role_add-policy.md index 66c6225e0f169..d4804d31d66a1 100644 --- a/docs/user-guide/commands/argocd_proj_role_add-policy.md +++ b/docs/user-guide/commands/argocd_proj_role_add-policy.md @@ -1,3 +1,5 @@ +# `argocd proj role add-policy` Command Reference + ## argocd proj role add-policy Add a policy to a project role @@ -6,6 +8,35 @@ Add a policy to a project role argocd proj role add-policy PROJECT ROLE-NAME [flags] ``` +### Examples + +``` +# Before adding new policy +$ argocd proj role get test-project test-role +Role Name: test-role +Description: +Policies: +p, proj:test-project:test-role, projects, get, test-project, allow +JWT Tokens: +ID ISSUED-AT EXPIRES-AT +1696759698 2023-10-08T11:08:18+01:00 (3 hours ago) + +# Add a new policy to allow update to the project +$ argocd proj role add-policy test-project test-role -a update -p allow -o project + +# Policy should be updated +$ argocd proj role get test-project test-role +Role Name: test-role +Description: +Policies: +p, proj:test-project:test-role, projects, get, test-project, allow +p, proj:test-project:test-role, applications, update, test-project/project, allow +JWT Tokens: +ID ISSUED-AT EXPIRES-AT +1696759698 2023-10-08T11:08:18+01:00 (3 hours ago) + +``` + ### Options ``` @@ -22,6 +53,7 @@ argocd proj role add-policy PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -34,8 +66,12 @@ argocd proj role add-policy PROJECT ROLE-NAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_role_create-token.md b/docs/user-guide/commands/argocd_proj_role_create-token.md index f71d005bbb20a..fc7eaf93c2307 100644 --- a/docs/user-guide/commands/argocd_proj_role_create-token.md +++ b/docs/user-guide/commands/argocd_proj_role_create-token.md @@ -1,3 +1,5 @@ +# `argocd proj role create-token` Command Reference + ## argocd proj role create-token Create a project token @@ -6,6 +8,18 @@ Create a project token argocd proj role create-token PROJECT ROLE-NAME [flags] ``` +### Examples + +``` +$ argocd proj role create-token test-project test-role +Create token succeeded for proj:test-project:test-role. + ID: f316c466-40bd-4cfd-8a8c-1392e92255d4 + Issued At: 2023-10-08T15:21:40+01:00 + Expires At: Never + Token: xxx + +``` + ### Options ``` @@ -22,6 +36,7 @@ argocd proj role create-token PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -34,8 +49,12 @@ argocd proj role create-token PROJECT ROLE-NAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_role_create.md b/docs/user-guide/commands/argocd_proj_role_create.md index 7bf2d02b1c70b..60974c9e1b4e6 100644 --- a/docs/user-guide/commands/argocd_proj_role_create.md +++ b/docs/user-guide/commands/argocd_proj_role_create.md @@ -1,3 +1,5 @@ +# `argocd proj role create` Command Reference + ## argocd proj role create Create a project role @@ -6,6 +8,13 @@ Create a project role argocd proj role create PROJECT ROLE-NAME [flags] ``` +### Examples + +``` + # Create a project role in the "my-project" project with the name "my-role". + argocd proj role create my-project my-role --description "My project role description" +``` + ### Options ``` @@ -20,6 +29,7 @@ argocd proj role create PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +42,12 @@ argocd proj role create PROJECT ROLE-NAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_role_delete-token.md b/docs/user-guide/commands/argocd_proj_role_delete-token.md index 0d3e9efab54a5..006746f8faeeb 100644 --- a/docs/user-guide/commands/argocd_proj_role_delete-token.md +++ b/docs/user-guide/commands/argocd_proj_role_delete-token.md @@ -1,3 +1,5 @@ +# `argocd proj role delete-token` Command Reference + ## argocd proj role delete-token Delete a project token @@ -6,6 +8,38 @@ Delete a project token argocd proj role delete-token PROJECT ROLE-NAME ISSUED-AT [flags] ``` +### Examples + +``` +#Create project test-project +$ argocd proj create test-project + +# Create a role associated with test-project +$ argocd proj role create test-project test-role +Role 'test-role' created + +# Create test-role associated with test-project +$ argocd proj role create-token test-project test-role +Create token succeeded for proj:test-project:test-role. + ID: c312450e-12e1-4e0d-9f65-fac9cb027b32 + Issued At: 2023-10-08T13:58:57+01:00 + Expires At: Never + Token: xxx + +# Get test-role id to input into the delete-token command below +$ argocd proj role get test-project test-role +Role Name: test-role +Description: +Policies: +p, proj:test-project:test-role, projects, get, test-project, allow +JWT Tokens: +ID ISSUED-AT EXPIRES-AT +1696769937 2023-10-08T13:58:57+01:00 (6 minutes ago) + +$ argocd proj role delete-token test-project test-role 1696769937 + +``` + ### Options ``` @@ -19,6 +53,7 @@ argocd proj role delete-token PROJECT ROLE-NAME ISSUED-AT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +66,12 @@ argocd proj role delete-token PROJECT ROLE-NAME ISSUED-AT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_role_delete.md b/docs/user-guide/commands/argocd_proj_role_delete.md index 29a966d0a3842..fe94a2231db60 100644 --- a/docs/user-guide/commands/argocd_proj_role_delete.md +++ b/docs/user-guide/commands/argocd_proj_role_delete.md @@ -1,3 +1,5 @@ +# `argocd proj role delete` Command Reference + ## argocd proj role delete Delete a project role @@ -6,6 +8,12 @@ Delete a project role argocd proj role delete PROJECT ROLE-NAME [flags] ``` +### Examples + +``` +$ argocd proj role delete test-project test-role +``` + ### Options ``` @@ -19,6 +27,7 @@ argocd proj role delete PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +40,12 @@ argocd proj role delete PROJECT ROLE-NAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_role_get.md b/docs/user-guide/commands/argocd_proj_role_get.md index a610b62af2f1d..e21276ce85116 100644 --- a/docs/user-guide/commands/argocd_proj_role_get.md +++ b/docs/user-guide/commands/argocd_proj_role_get.md @@ -1,3 +1,5 @@ +# `argocd proj role get` Command Reference + ## argocd proj role get Get the details of a specific role @@ -6,6 +8,21 @@ Get the details of a specific role argocd proj role get PROJECT ROLE-NAME [flags] ``` +### Examples + +``` +$ argocd proj role get test-project test-role +Role Name: test-role +Description: +Policies: +p, proj:test-project:test-role, projects, get, test-project, allow +JWT Tokens: +ID ISSUED-AT EXPIRES-AT +1696774900 2023-10-08T15:21:40+01:00 (4 minutes ago) +1696759698 2023-10-08T11:08:18+01:00 (4 hours ago) + +``` + ### Options ``` @@ -19,6 +36,7 @@ argocd proj role get PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +49,12 @@ argocd proj role get PROJECT ROLE-NAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_role_list-tokens.md b/docs/user-guide/commands/argocd_proj_role_list-tokens.md index e5bfdb941935b..8d1fe93163dfc 100644 --- a/docs/user-guide/commands/argocd_proj_role_list-tokens.md +++ b/docs/user-guide/commands/argocd_proj_role_list-tokens.md @@ -1,3 +1,5 @@ +# `argocd proj role list-tokens` Command Reference + ## argocd proj role list-tokens List tokens for a given role. @@ -6,6 +8,16 @@ List tokens for a given role. argocd proj role list-tokens PROJECT ROLE-NAME [flags] ``` +### Examples + +``` +$ argocd proj role list-tokens test-project test-role +ID ISSUED AT EXPIRES AT +f316c466-40bd-4cfd-8a8c-1392e92255d4 2023-10-08T15:21:40+01:00 Never +fa9d3517-c52d-434c-9bff-215b38508842 2023-10-08T11:08:18+01:00 Never + +``` + ### Options ``` @@ -20,6 +32,7 @@ argocd proj role list-tokens PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +45,12 @@ argocd proj role list-tokens PROJECT ROLE-NAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_role_list.md b/docs/user-guide/commands/argocd_proj_role_list.md index 3e2928b2ea7de..3cfd630ddc988 100644 --- a/docs/user-guide/commands/argocd_proj_role_list.md +++ b/docs/user-guide/commands/argocd_proj_role_list.md @@ -1,3 +1,5 @@ +# `argocd proj role list` Command Reference + ## argocd proj role list List all the roles in a project @@ -6,6 +8,16 @@ List all the roles in a project argocd proj role list PROJECT [flags] ``` +### Examples + +``` + # This command will list all the roles in argocd-project in a default table format. + argocd proj role list PROJECT + + # List the roles in the project in formats like json, yaml, wide, or name. + argocd proj role list PROJECT --output json +``` + ### Options ``` @@ -20,6 +32,7 @@ argocd proj role list PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +45,12 @@ argocd proj role list PROJECT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_role_remove-group.md b/docs/user-guide/commands/argocd_proj_role_remove-group.md index 6ca5c8a9f4283..a89e0bcfae315 100644 --- a/docs/user-guide/commands/argocd_proj_role_remove-group.md +++ b/docs/user-guide/commands/argocd_proj_role_remove-group.md @@ -1,3 +1,5 @@ +# `argocd proj role remove-group` Command Reference + ## argocd proj role remove-group Remove a group claim from a role within a project @@ -19,6 +21,7 @@ argocd proj role remove-group PROJECT ROLE-NAME GROUP-CLAIM [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +34,12 @@ argocd proj role remove-group PROJECT ROLE-NAME GROUP-CLAIM [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_role_remove-policy.md b/docs/user-guide/commands/argocd_proj_role_remove-policy.md index a7d68cbd30c64..96aee05da86eb 100644 --- a/docs/user-guide/commands/argocd_proj_role_remove-policy.md +++ b/docs/user-guide/commands/argocd_proj_role_remove-policy.md @@ -1,3 +1,5 @@ +# `argocd proj role remove-policy` Command Reference + ## argocd proj role remove-policy Remove a policy from a role within a project @@ -6,6 +8,35 @@ Remove a policy from a role within a project argocd proj role remove-policy PROJECT ROLE-NAME [flags] ``` +### Examples + +``` +List the policy of the test-role before removing a policy +$ argocd proj role get test-project test-role +Role Name: test-role +Description: +Policies: +p, proj:test-project:test-role, projects, get, test-project, allow +p, proj:test-project:test-role, applications, update, test-project/project, allow +JWT Tokens: +ID ISSUED-AT EXPIRES-AT +1696759698 2023-10-08T11:08:18+01:00 (3 hours ago) + +# Remove the policy to allow update to objects +$ argocd proj role remove-policy test-project test-role -a update -p allow -o project + +# The role should be removed now. +$ argocd proj role get test-project test-role +Role Name: test-role +Description: +Policies: +p, proj:test-project:test-role, projects, get, test-project, allow +JWT Tokens: +ID ISSUED-AT EXPIRES-AT +1696759698 2023-10-08T11:08:18+01:00 (4 hours ago) + +``` + ### Options ``` @@ -22,6 +53,7 @@ argocd proj role remove-policy PROJECT ROLE-NAME [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -34,8 +66,12 @@ argocd proj role remove-policy PROJECT ROLE-NAME [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_set.md b/docs/user-guide/commands/argocd_proj_set.md index 04a87ae2bcf14..3dc0cc06ec787 100644 --- a/docs/user-guide/commands/argocd_proj_set.md +++ b/docs/user-guide/commands/argocd_proj_set.md @@ -1,3 +1,5 @@ +# `argocd proj set` Command Reference + ## argocd proj set Set project parameters @@ -6,6 +8,16 @@ Set project parameters argocd proj set PROJECT [flags] ``` +### Examples + +``` + # Set project parameters with some allowed cluster resources [RES1,RES2,...] for project with name PROJECT + argocd proj set PROJECT --allow-cluster-resource [RES1,RES2,...] + + # Set project parameters with some denied namespaced resources [RES1,RES2,...] for project with name PROJECT + argocd proj set PROJECT ---deny-namespaced-resource [RES1,RES2,...] +``` + ### Options ``` @@ -30,6 +42,7 @@ argocd proj set PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -42,8 +55,12 @@ argocd proj set PROJECT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_windows.md b/docs/user-guide/commands/argocd_proj_windows.md index 5dac140a6861c..0b22c2098dc82 100644 --- a/docs/user-guide/commands/argocd_proj_windows.md +++ b/docs/user-guide/commands/argocd_proj_windows.md @@ -1,3 +1,5 @@ +# `argocd proj windows` Command Reference + ## argocd proj windows Manage a project's sync windows @@ -6,6 +8,23 @@ Manage a project's sync windows argocd proj windows [flags] ``` +### Examples + +``` + +#Add a sync window to a project +argocd proj windows add my-project \ +--schedule "0 0 * * 1-5" \ +--duration 3600 \ +--prune + +#Delete a sync window from a project +argocd proj windows delete + +#List project sync windows +argocd proj windows list +``` + ### Options ``` @@ -19,6 +38,7 @@ argocd proj windows [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +51,12 @@ argocd proj windows [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_windows_add.md b/docs/user-guide/commands/argocd_proj_windows_add.md index de49ef307ab90..52fd3a8354ee3 100644 --- a/docs/user-guide/commands/argocd_proj_windows_add.md +++ b/docs/user-guide/commands/argocd_proj_windows_add.md @@ -1,3 +1,5 @@ +# `argocd proj windows add` Command Reference + ## argocd proj windows add Add a sync window to a project @@ -6,6 +8,29 @@ Add a sync window to a project argocd proj windows add PROJECT [flags] ``` +### Examples + +``` + +#Add a 1 hour allow sync window +argocd proj windows add PROJECT \ + --kind allow \ + --schedule "0 22 * * *" \ + --duration 1h \ + --applications "*" + +#Add a deny sync window with the ability to manually sync. +argocd proj windows add PROJECT \ + --kind deny \ + --schedule "30 10 * * *" \ + --duration 30m \ + --applications "prod-\\*,website" \ + --namespaces "default,\\*-prod" \ + --clusters "prod,staging" \ + --manual-sync + +``` + ### Options ``` @@ -27,6 +52,7 @@ argocd proj windows add PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -39,8 +65,12 @@ argocd proj windows add PROJECT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_windows_delete.md b/docs/user-guide/commands/argocd_proj_windows_delete.md index 2f35f70263b82..6faf7dbeedc19 100644 --- a/docs/user-guide/commands/argocd_proj_windows_delete.md +++ b/docs/user-guide/commands/argocd_proj_windows_delete.md @@ -1,3 +1,5 @@ +# `argocd proj windows delete` Command Reference + ## argocd proj windows delete Delete a sync window from a project. Requires ID which can be found by running "argocd proj windows list PROJECT" @@ -6,6 +8,17 @@ Delete a sync window from a project. Requires ID which can be found by running " argocd proj windows delete PROJECT ID [flags] ``` +### Examples + +``` + +#Delete a sync window from a project (default) with ID 0 +argocd proj windows delete default 0 + +#Delete a sync window from a project (new-project) with ID 1 +argocd proj windows delete new-project 1 +``` + ### Options ``` @@ -19,6 +32,7 @@ argocd proj windows delete PROJECT ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +45,12 @@ argocd proj windows delete PROJECT ID [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_windows_disable-manual-sync.md b/docs/user-guide/commands/argocd_proj_windows_disable-manual-sync.md index 4c3a6e6465318..e3b84ac38cc0e 100644 --- a/docs/user-guide/commands/argocd_proj_windows_disable-manual-sync.md +++ b/docs/user-guide/commands/argocd_proj_windows_disable-manual-sync.md @@ -1,3 +1,5 @@ +# `argocd proj windows disable-manual-sync` Command Reference + ## argocd proj windows disable-manual-sync Disable manual sync for a sync window @@ -10,6 +12,17 @@ Disable manual sync for a sync window. Requires ID which can be found by running argocd proj windows disable-manual-sync PROJECT ID [flags] ``` +### Examples + +``` + +#Disable manual sync for a sync window for the Project +argocd proj windows disable-manual-sync PROJECT ID + +#Disbaling manual sync for a windows set on the default project with Id 0 +argocd proj windows disable-manual-sync default 0 +``` + ### Options ``` @@ -23,6 +36,7 @@ argocd proj windows disable-manual-sync PROJECT ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -35,8 +49,12 @@ argocd proj windows disable-manual-sync PROJECT ID [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_windows_enable-manual-sync.md b/docs/user-guide/commands/argocd_proj_windows_enable-manual-sync.md index 0ce894322bf1d..7ecbb50e6ac1b 100644 --- a/docs/user-guide/commands/argocd_proj_windows_enable-manual-sync.md +++ b/docs/user-guide/commands/argocd_proj_windows_enable-manual-sync.md @@ -1,3 +1,5 @@ +# `argocd proj windows enable-manual-sync` Command Reference + ## argocd proj windows enable-manual-sync Enable manual sync for a sync window @@ -10,6 +12,20 @@ Enable manual sync for a sync window. Requires ID which can be found by running argocd proj windows enable-manual-sync PROJECT ID [flags] ``` +### Examples + +``` + +#Enabling manual sync for a general case +argocd proj windows enable-manual-sync PROJECT ID + +#Enabling manual sync for a windows set on the default project with Id 2 +argocd proj windows enable-manual-sync default 2 + +#Enabling manual sync with a custom message +argocd proj windows enable-manual-sync my-app-project --message "Manual sync initiated by admin +``` + ### Options ``` @@ -23,6 +39,7 @@ argocd proj windows enable-manual-sync PROJECT ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -35,8 +52,12 @@ argocd proj windows enable-manual-sync PROJECT ID [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_windows_list.md b/docs/user-guide/commands/argocd_proj_windows_list.md index 725104f2153f3..3c361f90d2a68 100644 --- a/docs/user-guide/commands/argocd_proj_windows_list.md +++ b/docs/user-guide/commands/argocd_proj_windows_list.md @@ -1,3 +1,5 @@ +# `argocd proj windows list` Command Reference + ## argocd proj windows list List project sync windows @@ -6,6 +8,20 @@ List project sync windows argocd proj windows list PROJECT [flags] ``` +### Examples + +``` + +#List project windows +argocd proj windows list PROJECT + +#List project windows in yaml format +argocd proj windows list PROJECT -o yaml + +#List project windows info for a project name (test-project) +argocd proj windows list test-project +``` + ### Options ``` @@ -20,6 +36,7 @@ argocd proj windows list PROJECT [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +49,12 @@ argocd proj windows list PROJECT [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_proj_windows_update.md b/docs/user-guide/commands/argocd_proj_windows_update.md index 0916042b62bf6..e01e3787d51a2 100644 --- a/docs/user-guide/commands/argocd_proj_windows_update.md +++ b/docs/user-guide/commands/argocd_proj_windows_update.md @@ -1,3 +1,5 @@ +# `argocd proj windows update` Command Reference + ## argocd proj windows update Update a project sync window @@ -10,6 +12,15 @@ Update a project sync window. Requires ID which can be found by running "argocd argocd proj windows update PROJECT ID [flags] ``` +### Examples + +``` +# Change a sync window's schedule +argocd proj windows update PROJECT ID \ + --schedule "0 20 * * *" + +``` + ### Options ``` @@ -29,6 +40,7 @@ argocd proj windows update PROJECT ID [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -41,8 +53,12 @@ argocd proj windows update PROJECT ID [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_relogin.md b/docs/user-guide/commands/argocd_relogin.md index 93d848d46b45c..430ab4a9222c9 100644 --- a/docs/user-guide/commands/argocd_relogin.md +++ b/docs/user-guide/commands/argocd_relogin.md @@ -1,3 +1,5 @@ +# `argocd relogin` Command Reference + ## argocd relogin Refresh an expired authenticate token @@ -10,12 +12,29 @@ Refresh an expired authenticate token argocd relogin [flags] ``` +### Examples + +``` + +# Reinitiates the login with previous contexts +argocd relogin + +# Reinitiates the login with password +argocd relogin --password YOUR_PASSWORD + +# Configure direct access using Kubernetes API server +argocd login cd.argoproj.io --core + +# If user logged in with - "argocd login cd.argoproj.io" with sso login +# The command - "argocd relogin" will Reinitiates SSO login and updates the server context +``` + ### Options ``` -h, --help help for relogin - --password string the password of an account to authenticate - --sso-port int port to run local OAuth2 login application (default 8085) + --password string The password of an account to authenticate + --sso-port int Port to run local OAuth2 login application (default 8085) ``` ### Options inherited from parent commands @@ -25,6 +44,7 @@ argocd relogin [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -37,8 +57,12 @@ argocd relogin [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_repo.md b/docs/user-guide/commands/argocd_repo.md index e6e91d8ae8754..4df85f7b00d3d 100644 --- a/docs/user-guide/commands/argocd_repo.md +++ b/docs/user-guide/commands/argocd_repo.md @@ -1,3 +1,5 @@ +# `argocd repo` Command Reference + ## argocd repo Manage repository connection parameters @@ -6,6 +8,24 @@ Manage repository connection parameters argocd repo [flags] ``` +### Examples + +``` + +# Add git repository connection parameters +argocd repo add git@git.example.com:repos/repo + +# Get a Configured Repository by URL +argocd repo get https://github.com/yourusername/your-repo.git + +# List Configured Repositories +argocd repo list + +# Remove Repository Credentials +argocd repo rm https://github.com/yourusername/your-repo.git + +``` + ### Options ``` @@ -17,6 +37,7 @@ argocd repo [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for repo --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -37,6 +58,7 @@ argocd repo [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -49,8 +71,12 @@ argocd repo [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_repo_add.md b/docs/user-guide/commands/argocd_repo_add.md index e6d64d6c06739..8399d48302509 100644 --- a/docs/user-guide/commands/argocd_repo_add.md +++ b/docs/user-guide/commands/argocd_repo_add.md @@ -1,3 +1,5 @@ +# `argocd repo add` Command Reference + ## argocd repo add Add git repository connection parameters @@ -15,6 +17,12 @@ argocd repo add REPOURL [flags] # Add a Git repository via SSH on a non-default port - need to use ssh:// style URLs here argocd repo add ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa + # Add a Git repository via SSH using socks5 proxy with no proxy credentials + argocd repo add ssh://git@github.com/argoproj/argocd-example-apps --ssh-private-key-path ~/id_rsa --proxy socks5://your.proxy.server.ip:1080 + + # Add a Git repository via SSH using socks5 proxy with proxy credentials + argocd repo add ssh://git@github.com/argoproj/argocd-example-apps --ssh-private-key-path ~/id_rsa --proxy socks5://username:password@your.proxy.server.ip:1080 + # Add a private Git repository via HTTPS using username/password and TLS client certificates: argocd repo add https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key @@ -46,6 +54,7 @@ argocd repo add REPOURL [flags] ``` --enable-lfs enable git-lfs (Large File Support) on this repository --enable-oci enable helm-oci (Helm OCI-Based Repository) + --force-http-basic-auth whether to force use of basic auth when connecting repository via HTTP --gcp-service-account-key-path string service account key for the Google Cloud Platform --github-app-enterprise-base-url string base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3 --github-app-id int id of the GitHub Application @@ -73,6 +82,7 @@ argocd repo add REPOURL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -85,8 +95,12 @@ argocd repo add REPOURL [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_repo_get.md b/docs/user-guide/commands/argocd_repo_get.md index bbe298be7d36d..5a900adb09487 100644 --- a/docs/user-guide/commands/argocd_repo_get.md +++ b/docs/user-guide/commands/argocd_repo_get.md @@ -1,3 +1,5 @@ +# `argocd repo get` Command Reference + ## argocd repo get Get a configured repository by URL @@ -11,7 +13,7 @@ argocd repo get [flags] ``` -h, --help help for get -o, --output string Output format. One of: json|yaml|wide|url (default "wide") - --refresh string Force a cache refresh on connection status + --refresh string Force a cache refresh on connection status , must be one of: 'hard' ``` ### Options inherited from parent commands @@ -21,6 +23,7 @@ argocd repo get [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -33,8 +36,12 @@ argocd repo get [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_repo_list.md b/docs/user-guide/commands/argocd_repo_list.md index 488bda33584ed..06f1f788cb7c2 100644 --- a/docs/user-guide/commands/argocd_repo_list.md +++ b/docs/user-guide/commands/argocd_repo_list.md @@ -1,3 +1,5 @@ +# `argocd repo list` Command Reference + ## argocd repo list List configured repositories @@ -11,7 +13,7 @@ argocd repo list [flags] ``` -h, --help help for list -o, --output string Output format. One of: json|yaml|wide|url (default "wide") - --refresh string Force a cache refresh on connection status + --refresh string Force a cache refresh on connection status , must be one of: 'hard' ``` ### Options inherited from parent commands @@ -21,6 +23,7 @@ argocd repo list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -33,8 +36,12 @@ argocd repo list [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_repo_rm.md b/docs/user-guide/commands/argocd_repo_rm.md index c33ef807bb82e..01e89d48e76a1 100644 --- a/docs/user-guide/commands/argocd_repo_rm.md +++ b/docs/user-guide/commands/argocd_repo_rm.md @@ -1,3 +1,5 @@ +# `argocd repo rm` Command Reference + ## argocd repo rm Remove repository credentials @@ -19,6 +21,7 @@ argocd repo rm REPO [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +34,12 @@ argocd repo rm REPO [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_repocreds.md b/docs/user-guide/commands/argocd_repocreds.md index f2a52f42fbb92..f073b2bbb6161 100644 --- a/docs/user-guide/commands/argocd_repocreds.md +++ b/docs/user-guide/commands/argocd_repocreds.md @@ -1,3 +1,5 @@ +# `argocd repocreds` Command Reference + ## argocd repocreds Manage repository connection parameters @@ -6,6 +8,19 @@ Manage repository connection parameters argocd repocreds [flags] ``` +### Examples + +``` + # Add credentials with user/pass authentication to use for all repositories under the specified URL + argocd repocreds add URL --username USERNAME --password PASSWORD + + # List all the configured repository credentials + argocd repocreds list + + # Remove credentials for the repositories with speficied URL + argocd repocreds rm URL +``` + ### Options ``` @@ -17,6 +32,7 @@ argocd repocreds [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for repocreds --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -37,6 +53,7 @@ argocd repocreds [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -49,8 +66,12 @@ argocd repocreds [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_repocreds_add.md b/docs/user-guide/commands/argocd_repocreds_add.md index 42e30d65e0618..ce66dc49cfe8c 100644 --- a/docs/user-guide/commands/argocd_repocreds_add.md +++ b/docs/user-guide/commands/argocd_repocreds_add.md @@ -1,3 +1,5 @@ +# `argocd repocreds add` Command Reference + ## argocd repocreds add Add git repository connection parameters @@ -33,6 +35,7 @@ argocd repocreds add REPOURL [flags] ``` --enable-oci Specifies whether helm-oci support should be enabled for this repo + --force-http-basic-auth whether to force basic auth when connecting via HTTP --gcp-service-account-key-path string service account key for the Google Cloud Platform --github-app-enterprise-base-url string base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3 --github-app-id int id of the GitHub Application @@ -55,6 +58,7 @@ argocd repocreds add REPOURL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -67,8 +71,12 @@ argocd repocreds add REPOURL [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_repocreds_list.md b/docs/user-guide/commands/argocd_repocreds_list.md index f0d7a6bcae570..ae358afab2056 100644 --- a/docs/user-guide/commands/argocd_repocreds_list.md +++ b/docs/user-guide/commands/argocd_repocreds_list.md @@ -1,3 +1,5 @@ +# `argocd repocreds list` Command Reference + ## argocd repocreds list List configured repository credentials @@ -6,6 +8,22 @@ List configured repository credentials argocd repocreds list [flags] ``` +### Examples + +``` + # List all repo urls + argocd repocreds list + + # List all repo urls in json format + argocd repocreds list -o json + + # List all repo urls in yaml format + argocd repocreds list -o yaml + + # List all repo urls in url format + argocd repocreds list -o url +``` + ### Options ``` @@ -20,6 +38,7 @@ argocd repocreds list [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -32,8 +51,12 @@ argocd repocreds list [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_repocreds_rm.md b/docs/user-guide/commands/argocd_repocreds_rm.md index 678e70afd50c1..3bfee30eb40a3 100644 --- a/docs/user-guide/commands/argocd_repocreds_rm.md +++ b/docs/user-guide/commands/argocd_repocreds_rm.md @@ -1,3 +1,5 @@ +# `argocd repocreds rm` Command Reference + ## argocd repocreds rm Remove repository credentials @@ -6,6 +8,13 @@ Remove repository credentials argocd repocreds rm CREDSURL [flags] ``` +### Examples + +``` + # Remove credentials for the repositories with URL https://git.example.com/repos + argocd repocreds rm https://git.example.com/repos/ +``` + ### Options ``` @@ -19,6 +28,7 @@ argocd repocreds rm CREDSURL [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -31,8 +41,12 @@ argocd repocreds rm CREDSURL [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/commands/argocd_version.md b/docs/user-guide/commands/argocd_version.md index a8ecfa69ea0f1..6a7fa7baf5ecb 100644 --- a/docs/user-guide/commands/argocd_version.md +++ b/docs/user-guide/commands/argocd_version.md @@ -1,3 +1,5 @@ +# `argocd version` Command Reference + ## argocd version Print version information @@ -35,6 +37,7 @@ argocd version [flags] --client-key string Path to a client key file for TLS --cluster string The name of the kubeconfig cluster to use --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server -h, --help help for version --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure --kubeconfig string Path to a kube config. Only required if out-of-cluster @@ -57,6 +60,7 @@ argocd version [flags] --client-crt string Client certificate file --client-crt-key string Client certificate key file --config string Path to Argo CD config (default "/home/user/.config/argocd/config") + --controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller") --core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server --grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. --grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root. @@ -69,8 +73,12 @@ argocd version [flags] --plaintext Disable TLS --port-forward Connect to a random argocd-server port using port forwarding --port-forward-namespace string Namespace name which should be used for port forwarding + --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") + --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") + --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") --server string Argo CD server address --server-crt string Server certificate file + --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` ### SEE ALSO diff --git a/docs/user-guide/config-management-plugins.md b/docs/user-guide/config-management-plugins.md new file mode 100644 index 0000000000000..652f545d7c4c1 --- /dev/null +++ b/docs/user-guide/config-management-plugins.md @@ -0,0 +1,3 @@ +# Config Management Plugins + +This page has been moved to the [operator manual](../operator-manual/config-management-plugins.md). diff --git a/docs/user-guide/diff-strategies.md b/docs/user-guide/diff-strategies.md new file mode 100644 index 0000000000000..2890fe64cbb0e --- /dev/null +++ b/docs/user-guide/diff-strategies.md @@ -0,0 +1,131 @@ +# Diff Strategies + +Argo CD calculates the diff between the desired state and the live +state in order to define if an Application is out-of-sync. This same +logic is also used in Argo CD UI to display the differences between +live and desired states for all resources belonging to an application. + +Argo CD currently has 3 different strategies to calculate diffs: + +- **Legacy**: This is the main diff strategy used by default. It + applies a 3-way diff based on live state, desired state and + last-applied-configuration (annotation). +- **Structured-Merge Diff**: Strategy automatically applied when + enabling Server-Side Apply sync option. +- **Server-Side Diff**: New strategy that invokes a Server-Side Apply + in dryrun mode in order to generate the predicted live state. + +## Structured-Merge Diff +*Current Status: [Beta][1] (Since v2.5.0)* + +This is diff strategy is automatically used when Server-Side Apply +sync option is enabled. It uses the [structured-merge-diff][2] library +used by Kubernetes to calculate diffs based on fields ownership. There +are some challenges using this strategy to calculate diffs for CRDs +that define default values. After different issues were identified by +the community, this strategy is being discontinued in favour of +Server-Side Diff. + +## Server-Side Diff +*Current Status: [Beta][1] (Since v2.10.0)* + +This diff strategy will execute a Server-Side Apply in dryrun mode for +each resource of the application. The response of this operation is then +compared with the live state in order to provide the diff results. The +diff results are cached and new Server-Side Apply requests to Kube API +are only triggered when: + +- An Application refresh or hard-refresh is requested. +- There is a new revision in the repo which the Argo CD Application is + targeting. +- The Argo CD Application spec changed. + +One advantage of Server-Side Diff is that Kubernetes Admission +Controllers will participate in the diff calculation. If for example +a validation webhook identifies a resource to be invalid, that will be +informed to Argo CD during the diff stage rather than during the sync +stage. + +### Enabling it + +Server-Side Diff can be enabled at the Argo CD Controller level or per +Application. + +**Enabling Server-Side Diff for all Applications** + +Add the following entry in the argocd-cmd-params-cm configmap: + +``` +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cmd-params-cm +data: + controller.diff.server.side: "true" +... +``` + +Note: It is necessary to restart the `argocd-application-controller` +after applying this configuration. + +**Enabling Server-Side Diff for one application** + +Add the following annotation in the Argo CD Application resource: + +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + argocd.argoproj.io/compare-options: ServerSideDiff=true +... +``` + +**Disabling Server-Side Diff for one application** + +If Server-Side Diff is enabled globally in your Argo CD instance, it +is possible to disable it at the application level. In order to do so, +add the following annotation in the Application resource: + +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + argocd.argoproj.io/compare-options: ServerSideDiff=false +... +``` + +*Note: Please report any issues that forced you to disable the +Server-Side Diff feature* + +### Mutation Webhooks + +Server-Side Diff does not include changes made by mutation webhooks by +default. If you want to include mutation webhooks in Argo CD diffs add +the following annotation in the Argo CD Application resource: + +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + argocd.argoproj.io/compare-options: IncludeMutationWebhook=true +... +``` + +Note: This annoation is only effective when Server-Side Diff is +enabled. To enable both options for a given application add the +following annotation in the Argo CD Application resource: + +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + argocd.argoproj.io/compare-options: ServerSideDiff=true,IncludeMutationWebhook=true +... +``` + +[1]: https://github.com/argoproj/argoproj/blob/main/community/feature-status.md#beta +[2]: https://github.com/kubernetes-sigs/structured-merge-diff diff --git a/docs/user-guide/diffing.md b/docs/user-guide/diffing.md index ed8bf4d054909..61f799e514d6a 100644 --- a/docs/user-guide/diffing.md +++ b/docs/user-guide/diffing.md @@ -60,14 +60,23 @@ To ignore fields owned by specific managers defined in your live resources: ```yaml spec: ignoreDifferences: - - group: * - kind: * + - group: "*" + kind: "*" managedFieldsManagers: - kube-controller-manager ``` The above configuration will ignore differences from all fields owned by `kube-controller-manager` for all resources belonging to this application. +If you have a slash `/` in your pointer path, you can use the `~1` character. For example: + +```yaml +spec: + ignoreDifferences: + - kind: Node + jsonPointers: /metadata/labels/node-role.kubernetes.io~1worker +``` + ## System-Level Configuration The comparison of resources with well-known issues can be customized at a system level. Ignored differences can be configured for a specified group and kind @@ -172,4 +181,7 @@ data: type: core/v1/PodSpec ``` -The list of supported Kubernetes types is available in [diffing_known_types.txt](https://raw.githubusercontent.com/argoproj/argo-cd/master/util/argo/normalizers/diffing_known_types.txt) +The list of supported Kubernetes types is available in [diffing_known_types.txt](https://raw.githubusercontent.com/argoproj/argo-cd/master/util/argo/normalizers/diffing_known_types.txt) and additionally: + +* `core/Quantity` +* `meta/v1/duration` diff --git a/docs/user-guide/directory.md b/docs/user-guide/directory.md index 278911b013410..f422f7311cfd3 100644 --- a/docs/user-guide/directory.md +++ b/docs/user-guide/directory.md @@ -43,6 +43,9 @@ spec: recurse: true ``` +!!! warning + Directory-type applications only work for plain manifest files. If Argo CD encounters Kustomize, Helm, or Jsonnet files when directory: is set, it will fail to render the manifests. + ## Including/Excluding Files ### Including Only Certain Files diff --git a/docs/user-guide/environment-variables.md b/docs/user-guide/environment-variables.md index ceea5798e83a3..cff6446617fa3 100644 --- a/docs/user-guide/environment-variables.md +++ b/docs/user-guide/environment-variables.md @@ -2,8 +2,14 @@ The following environment variables can be used with `argocd` CLI: -| Environment Variable | Description | -| --- | --- | -| `ARGOCD_SERVER` | the address of the ArgoCD server without `https://` prefix
    (instead of specifying `--server` for every command)
    eg. `ARGOCD_SERVER=argocd.mycompany.com` if served through an ingress with DNS | -| `ARGOCD_AUTH_TOKEN` | the ArgoCD `apiKey` for your ArgoCD user to be able to authenticate | -| `ARGOCD_OPTS` | command-line options to pass to `argocd` CLI
    eg. `ARGOCD_OPTS="--grpc-web"` | +| Environment Variable | Description | +|--------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `ARGOCD_SERVER` | the address of the Argo CD server without `https://` prefix
    (instead of specifying `--server` for every command)
    eg. `ARGOCD_SERVER=argocd.example.com` if served through an ingress with DNS | +| `ARGOCD_AUTH_TOKEN` | the Argo CD `apiKey` for your Argo CD user to be able to authenticate | +| `ARGOCD_OPTS` | command-line options to pass to `argocd` CLI
    eg. `ARGOCD_OPTS="--grpc-web"` | +| `ARGOCD_SERVER_NAME` | the Argo CD API Server name (default "argocd-server") | +| `ARGOCD_REPO_SERVER_NAME` | the Argo CD Repository Server name (default "argocd-repo-server") | +| `ARGOCD_APPLICATION_CONTROLLER_NAME` | the Argo CD Application Controller name (default "argocd-application-controller") | +| `ARGOCD_REDIS_NAME` | the Argo CD Redis name (default "argocd-redis") | +| `ARGOCD_REDIS_HAPROXY_NAME` | the Argo CD Redis HA Proxy name (default "argocd-redis-ha-haproxy") | +| `ARGOCD_GRPC_KEEP_ALIVE_MIN` | defines the GRPCKeepAliveEnforcementMinimum, used in the grpc.KeepaliveEnforcementPolicy. Expects a "Duration" format (default `10s`). | diff --git a/docs/user-guide/external-url.md b/docs/user-guide/external-url.md index 173a8724c5fea..7f08ea6c80bf4 100644 --- a/docs/user-guide/external-url.md +++ b/docs/user-guide/external-url.md @@ -1,6 +1,6 @@ # Add external URL -You can add additional external links to ArgoCD dashboard. For example +You can add additional external links to Argo CD dashboard. For example links monitoring pages or documentation instead of just ingress hosts or other apps. ArgoCD generates a clickable links to external pages for a resource based on per resource annotation. @@ -12,7 +12,7 @@ kind: Deployment metadata: name: my-svc annotations: - link.argocd.argoproj.io/external-link: http://my-grafana.com/pre-generated-link + link.argocd.argoproj.io/external-link: http://my-grafana.example.com/pre-generated-link ``` ![External link](../assets/external-link.png) diff --git a/docs/user-guide/extra_info.md b/docs/user-guide/extra_info.md new file mode 100644 index 0000000000000..298b457a81bd4 --- /dev/null +++ b/docs/user-guide/extra_info.md @@ -0,0 +1,28 @@ +# Add extra Application info + +You can add additional information to an Application on your Argo CD dashboard. +If you wish to add clickable links, see [Add external URL](https://argo-cd.readthedocs.io/en/stable/user-guide/external-url/). + +This is done by providing the 'info' field a key-value in your Application manifest. + +Example: +```yaml +project: argo-demo +source: + repoURL: 'https://demo' + path: argo-demo +destination: + server: https://demo + namespace: argo-demo +info: + - name: Example: + value: >- + https://example.com +``` +![External link](../assets/extra_info-1.png) + +The additional information will be visible on the Argo CD Application details page. + +![External link](../assets/extra_info.png) + +![External link](../assets/extra_info-2.png) diff --git a/docs/user-guide/helm.md b/docs/user-guide/helm.md index b75cebc43078f..7a763336abcc8 100644 --- a/docs/user-guide/helm.md +++ b/docs/user-guide/helm.md @@ -2,7 +2,9 @@ ## Declarative -You can install Helm charts through the UI, or in the declarative GitOps way. Here is an example: +You can install Helm charts through the UI, or in the declarative GitOps way. +Helm is [only used to inflate charts with `helm template`](../../faq#after-deploying-my-helm-application-with-argo-cd-i-cannot-see-it-with-helm-ls-and-other-helm-commands). The lifecycle of the application is handled by Argo CD instead of Helm. +Here is an example: ```yaml apiVersion: argoproj.io/v1alpha1 @@ -23,6 +25,28 @@ spec: namespace: kubeseal ``` +Another example using a public OCI helm chart: +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: nginx +spec: + project: default + source: + chart: nginx + repoURL: registry-1.docker.io/bitnamicharts # note: the oci:// syntax is not included. + targetRevision: 15.9.0 + destination: + name: "in-cluster" + namespace: nginx +``` + +!!! note "When using multiple ways to provide values" + Order of precedence is `parameters > valuesObject > values > valueFiles > helm repository values.yaml` (see [Here](./helm.md#helm-value-precedence) for a more detailed example) + +See [here](../operator-manual/declarative-setup.md#helm-chart-repositories) for more info about how to configure private Helm repositories. + ## Values Files Helm has the ability to use a different, or even multiple "values.yaml" files to derive its @@ -33,9 +57,11 @@ flag. The flag can be repeated to support multiple values files: argocd app set helm-guestbook --values values-production.yaml ``` !!! note - Values files must be in the same git repository as the Helm chart. The files can be in a different - location in which case it can be accessed using a relative path relative to the root directory of - the Helm chart. + Before `v2.6` of Argo CD, Values files must be in the same git repository as the Helm + chart. The files can be in a different location in which case it can be accessed using + a relative path relative to the root directory of the Helm chart. + As of `v2.6`, values files can be sourced from a separate repository than the Helm chart + by taking advantage of [multiple sources for Applications](./multiple_sources.md#helm-value-files-from-external-git-repository). In the declarative syntax: @@ -46,6 +72,50 @@ source: - values-production.yaml ``` +## Values + +Argo CD supports the equivalent of a values file directly in the Application manifest using the `source.helm.valuesObject` key. + +```yaml +source: + helm: + valuesObject: + ingress: + enabled: true + path: / + hosts: + - mydomain.example.com + annotations: + kubernetes.io/ingress.class: nginx + kubernetes.io/tls-acme: "true" + labels: {} + tls: + - secretName: mydomain-tls + hosts: + - mydomain.example.com +``` + +Alternatively, values can be passed in as a string using the `source.helm.values` key. + +```yaml +source: + helm: + values: | + ingress: + enabled: true + path: / + hosts: + - mydomain.example.com + annotations: + kubernetes.io/ingress.class: nginx + kubernetes.io/tls-acme: "true" + labels: {} + tls: + - secretName: mydomain-tls + hosts: + - mydomain.example.com +``` + ## Helm Parameters Helm has the ability to set parameter values, which override any values in @@ -72,9 +142,68 @@ source: value: LoadBalancer ``` +## Helm Value Precedence +Values injections have the following order of precedence + `parameters > valuesObject > values > valueFiles > helm repository values.yaml` + Or rather + +``` + lowest -> valueFiles + -> values + -> valuesObject + highest -> parameters +``` + +so values/valuesObject trumps valueFiles, and parameters trump both. + +Precedence of valueFiles themselves is the order they are defined in + +``` +if we have + +valuesFile: + - values-file-2.yaml + - values-file-1.yaml + +the last values-file i.e. values-file-1.yaml will trump the first +``` + +When multiple of the same key are found the last one wins i.e + +``` +e.g. if we only have values-file-1.yaml and it contains + +param1: value1 +param1: value3000 + +we get param1=value3000 +``` + +``` +parameters: + - name: "param1" + value: value2 + - name: "param1" + value: value1 + +the result will be param1=value1 +``` + +``` +values: | + param1: value2 + param1: value5 + +the result will be param1=value5 +``` + +!!! note "When valuesFiles or values is used" + The list of parameters seen in the ui is not what is used for resources, rather it is the values/valuesObject merged with parameters (see [this issue](https://github.com/argoproj/argo-cd/issues/9213) incase it has been resolved) + As a workaround using parameters instead of values/valuesObject will provide a better overview of what will be used for resources + ## Helm Release Name -By default, the Helm release name is equal to the Application name to which it belongs. Sometimes, especially on a centralised ArgoCD, +By default, the Helm release name is equal to the Application name to which it belongs. Sometimes, especially on a centralised Argo CD, you may want to override that name, and it is possible with the `release-name` flag on the cli: ```bash @@ -90,7 +219,7 @@ source: ``` !!! warning "Important notice on overriding the release name" - Please note that overriding the Helm release name might cause problems when the chart you are deploying is using the `app.kubernetes.io/instance` label. ArgoCD injects this label with the value of the Application name for tracking purposes. So when overriding the release name, the Application name will stop being equal to the release name. Because ArgoCD will overwrite the label with the Application name it might cause some selectors on the resources to stop working. In order to avoid this we can configure ArgoCD to use another label for tracking in the [ArgoCD configmap argocd-cm.yaml](../operator-manual/argocd-cm.yaml) - check the lines describing `application.instanceLabelKey`. + Please note that overriding the Helm release name might cause problems when the chart you are deploying is using the `app.kubernetes.io/instance` label. Argo CD injects this label with the value of the Application name for tracking purposes. So when overriding the release name, the Application name will stop being equal to the release name. Because Argo CD will overwrite the label with the Application name it might cause some selectors on the resources to stop working. In order to avoid this we can configure Argo CD to use another label for tracking in the [ArgoCD configmap argocd-cm.yaml](../operator-manual/argocd-cm.yaml) - check the lines describing `application.instanceLabelKey`. ## Helm Hooks @@ -99,25 +228,29 @@ is any normal Kubernetes resource annotated with the `helm.sh/hook` annotation. Argo CD supports many (most?) Helm hooks by mapping the Helm annotations onto Argo CD's own hook annotations: -| Helm Annotation | Notes | -|---|---| -| `helm.sh/hook: crd-install` | Supported as equivalent to `argocd.argoproj.io/hook: PreSync`. | -| `helm.sh/hook: pre-delete` | Not supported. In Helm stable there are 3 cases used to clean up CRDs and 3 to clean-up jobs. | -| `helm.sh/hook: pre-rollback` | Not supported. Never used in Helm stable. | -| `helm.sh/hook: pre-install` | Supported as equivalent to `argocd.argoproj.io/hook: PreSync`. | -| `helm.sh/hook: pre-upgrade` | Supported as equivalent to `argocd.argoproj.io/hook: PreSync`. | -| `helm.sh/hook: post-upgrade` | Supported as equivalent to `argocd.argoproj.io/hook: PostSync`. | -| `helm.sh/hook: post-install` | Supported as equivalent to `argocd.argoproj.io/hook: PostSync`. | -| `helm.sh/hook: post-delete` | Not supported. Never used in Helm stable. | -| `helm.sh/hook: post-rollback` | Not supported. Never used in Helm stable. | -| `helm.sh/hook: test-success` | Not supported. No equivalent in Argo CD. | -| `helm.sh/hook: test-failure` | Not supported. No equivalent in Argo CD. | -| `helm.sh/hook-delete-policy` | Supported. See also `argocd.argoproj.io/hook-delete-policy`). | -| `helm.sh/hook-delete-timeout` | No supported. Never used in Helm stable | -| `helm.sh/hook-weight` | Supported as equivalent to `argocd.argoproj.io/sync-wave`. | +| Helm Annotation | Notes | +| ------------------------------- |-----------------------------------------------------------------------------------------------| +| `helm.sh/hook: crd-install` | Supported as equivalent to `argocd.argoproj.io/hook: PreSync`. | +| `helm.sh/hook: pre-delete` | Not supported. In Helm stable there are 3 cases used to clean up CRDs and 3 to clean-up jobs. | +| `helm.sh/hook: pre-rollback` | Not supported. Never used in Helm stable. | +| `helm.sh/hook: pre-install` | Supported as equivalent to `argocd.argoproj.io/hook: PreSync`. | +| `helm.sh/hook: pre-upgrade` | Supported as equivalent to `argocd.argoproj.io/hook: PreSync`. | +| `helm.sh/hook: post-upgrade` | Supported as equivalent to `argocd.argoproj.io/hook: PostSync`. | +| `helm.sh/hook: post-install` | Supported as equivalent to `argocd.argoproj.io/hook: PostSync`. | +| `helm.sh/hook: post-delete` | Supported as equivalent to `argocd.argoproj.io/hook: PostDelete`. | +| `helm.sh/hook: post-rollback` | Not supported. Never used in Helm stable. | +| `helm.sh/hook: test-success` | Not supported. No equivalent in Argo CD. | +| `helm.sh/hook: test-failure` | Not supported. No equivalent in Argo CD. | +| `helm.sh/hook-delete-policy` | Supported. See also `argocd.argoproj.io/hook-delete-policy`). | +| `helm.sh/hook-delete-timeout` | Not supported. Never used in Helm stable | +| `helm.sh/hook-weight` | Supported as equivalent to `argocd.argoproj.io/sync-wave`. | +| `helm.sh/resource-policy: keep` | Supported as equivalent to `argocd.argoproj.io/sync-options: Delete=false`. | Unsupported hooks are ignored. In Argo CD, hooks are created by using `kubectl apply`, rather than `kubectl create`. This means that if the hook is named and already exists, it will not change unless you have annotated it with `before-hook-creation`. +!!! warning "Helm hooks + ArgoCD hooks" + If you define any Argo CD hooks, _all_ Helm hooks will be ignored. + !!! warning "'install' vs 'upgrade' vs 'sync'" Argo CD cannot know if it is running a first-time "install" or an "upgrade" - every operation is a "sync'. This means that, by default, apps that have `pre-install` and `pre-upgrade` will have those hooks run at the same time. @@ -202,7 +335,7 @@ One way to use this plugin is to prepare your own ArgoCD image where it is inclu Example `Dockerfile`: -``` +```dockerfile FROM argoproj/argocd:v1.5.7 USER root @@ -232,38 +365,43 @@ Some users find this pattern preferable to maintaining their own version of the Below is an example of how to add Helm plugins when installing ArgoCD with the [official ArgoCD helm chart](https://github.com/argoproj/argo-helm/tree/master/charts/argo-cd): -``` +```yaml repoServer: volumes: - - name: gcloud + - name: gcp-credentials secret: - secretName: helm-credentials + secretName: my-gcp-credentials volumeMounts: - - mountPath: /gcloud - name: gcloud + - name: gcp-credentials + mountPath: /gcp env: - - name: HELM_PLUGINS - value: /helm-working-dir/plugins/ - - name: GOOGLE_APPLICATION_CREDENTIALS - value: /gcloud/key.json + - name: HELM_CACHE_HOME + value: /helm-working-dir + - name: HELM_CONFIG_HOME + value: /helm-working-dir + - name: HELM_DATA_HOME + value: /helm-working-dir initContainers: - - name: install-helm-plugins - image: alpine/helm:3.8.0 + - name: helm-gcp-authentication + image: alpine/helm:3.8.1 volumeMounts: - - mountPath: /helm-working-dir - name: helm-working-dir - - mountPath: /gcloud - name: gcloud + - name: helm-working-dir + mountPath: /helm-working-dir + - name: gcp-credentials + mountPath: /gcp env: - - name: GOOGLE_APPLICATION_CREDENTIALS - value: /gcloud/key.json - - name: HELM_PLUGINS - value: /helm-working-dir/plugins - command: ["/bin/sh", "-c"] + - name: HELM_CACHE_HOME + value: /helm-working-dir + - name: HELM_CONFIG_HOME + value: /helm-working-dir + - name: HELM_DATA_HOME + value: /helm-working-dir + command: [ "/bin/sh", "-c" ] args: - apk --no-cache add curl; helm plugin install https://github.com/hayorov/helm-gcs.git; - helm repo add my-private-gcs-repo gs://my-private-gcs-repo; + helm repo add my-gcs-repo gs://my-private-helm-gcs-repository; + chmod -R 777 $HELM_DATA_HOME; ``` ## Helm Version @@ -291,7 +429,7 @@ Helm, [starting with v3.6.1](https://github.com/helm/helm/releases/tag/v3.6.1), prevents sending repository credentials to download charts that are being served from a different domain than the repository. -If needed, it is possible to specifically set the Helm version to template with by setting the `helm-pass-credentials` flag on the cli: +If needed, it is possible to opt into passing credentials for all domains by setting the `helm-pass-credentials` flag on the cli: ```bash argocd app set helm-guestbook --helm-pass-credentials diff --git a/docs/user-guide/jsonnet.md b/docs/user-guide/jsonnet.md index 699cd45335b61..194daa06c2591 100644 --- a/docs/user-guide/jsonnet.md +++ b/docs/user-guide/jsonnet.md @@ -1,6 +1,6 @@ # Jsonnet -Any file matching `*.jsonnet` in a directory app is treated as a Jsonnet file. ArgoCD evaluates the Jsonnet and is able to parse a generated object or array. +Any file matching `*.jsonnet` in a directory app is treated as a Jsonnet file. Argo CD evaluates the Jsonnet and is able to parse a generated object or array. ## Build Environment diff --git a/docs/user-guide/kustomize.md b/docs/user-guide/kustomize.md index b940cc9a154e1..1aa876fb74224 100644 --- a/docs/user-guide/kustomize.md +++ b/docs/user-guide/kustomize.md @@ -5,14 +5,139 @@ The following configuration options are available for Kustomize: * `namePrefix` is a prefix appended to resources for Kustomize apps * `nameSuffix` is a suffix appended to resources for Kustomize apps * `images` is a list of Kustomize image overrides +* `replicas` is a list of Kustomize replica overrides * `commonLabels` is a string map of additional labels +* `labelWithoutSelector` is a boolean value which defines if the common label(s) should be applied to resource selectors and templates. +* `forceCommonLabels` is a boolean value which defines if it's allowed to override existing labels * `commonAnnotations` is a string map of additional annotations +* `namespace` is a Kubernetes resources namespace +* `forceCommonAnnotations` is a boolean value which defines if it's allowed to override existing annotations +* `commonAnnotationsEnvsubst` is a boolean value which enables env variables substition in annotation values +* `patches` is a list of Kustomize patches that supports inline updates +* `components` is a list of Kustomize components To use Kustomize with an overlay, point your path to the overlay. !!! tip If you're generating resources, you should read up how to ignore those generated resources using the [`IgnoreExtraneous` compare option](compare-options.md). +## Patches +Patches are a way to kustomize resources using inline configurations in Argo CD applications. `patches` follow the same logic as the corresponding Kustomization. Any patches that target existing Kustomization file will be merged. + +This Kustomize example sources manifests from the `/kustomize-guestbook` folder of the `argoproj/argocd-example-apps` repository, and patches the `Deployment` to use port `443` on the container. +```yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +metadata: + name: kustomize-inline-example +namespace: test1 +resources: + - https://github.com/argoproj/argocd-example-apps//kustomize-guestbook/ +patches: + - target: + kind: Deployment + name: guestbook-ui + patch: |- + - op: replace + path: /spec/template/spec/containers/0/ports/0/containerPort + value: 443 +``` + +This `Application` does the equivalent using the inline `kustomize.patches` configuration. +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: kustomize-inline-guestbook + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + destination: + namespace: test1 + server: https://kubernetes.default.svc + project: default + source: + path: kustomize-guestbook + repoURL: https://github.com/argoproj/argocd-example-apps.git + targetRevision: master + kustomize: + patches: + - target: + kind: Deployment + name: guestbook-ui + patch: |- + - op: replace + path: /spec/template/spec/containers/0/ports/0/containerPort + value: 443 +``` + +The inline kustomize patches work well with `ApplicationSets`, too. Instead of maintaining a patch or overlay for each cluster, patches can now be done in the `Application` template and utilize attributes from the generators. For example, with [`external-dns`](https://github.com/kubernetes-sigs/external-dns/) to set the [`txt-owner-id`](https://github.com/kubernetes-sigs/external-dns/blob/e1adc9079b12774cccac051966b2c6a3f18f7872/docs/registry/registry.md?plain=1#L6) to the cluster name. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: external-dns +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + - clusters: {} + template: + metadata: + name: 'external-dns' + spec: + project: default + source: + repoURL: https://github.com/kubernetes-sigs/external-dns/ + targetRevision: v0.14.0 + path: kustomize + kustomize: + patches: + - target: + kind: Deployment + name: external-dns + patch: |- + - op: add + path: /spec/template/spec/containers/0/args/3 + value: --txt-owner-id={{.name}} # patch using attribute from generator + destination: + name: 'in-cluster' + namespace: default +``` + +## Components +Kustomize [components](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/components.md) encapsulate both resources and patches together. They provide a powerful way to modularize and reuse configuration in Kubernetes applications. + +Outside of Argo CD, to utilize components, you must add the following to the `kustomization.yaml` that the Application references. For example: +```yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +... +components: +- ../component +``` + +With support added for components in `v2.10.0`, you can now reference a component directly in the Application: +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: application-kustomize-components +spec: + ... + source: + path: examples/application-kustomize-components/base + repoURL: https://github.com/my-user/my-repo + targetRevision: main + + # This! + kustomize: + components: + - ../component # relative to the kustomization.yaml (`source.path`). +``` + ## Private Remote Bases If you have remote bases that are either (a) HTTPS and need username/password (b) SSH and need SSH private key, then they'll inherit that from the app's repo. @@ -38,6 +163,9 @@ data: kustomize.buildOptions: --load-restrictor LoadRestrictionsNone kustomize.buildOptions.v4.4.0: --output /tmp ``` + +After modifying `kustomize.buildOptions`, you may need to restart ArgoCD for the changes to take effect. + ## Custom Kustomize versions Argo CD supports using multiple Kustomize versions simultaneously and specifies required version per application. @@ -86,6 +214,34 @@ argocd app set --kustomize-version v3.5.4 Kustomize apps have access to the [standard build environment](build-environment.md) which can be used in combination with a [config managment plugin](../operator-manual/config-management-plugins.md) to alter the rendered manifests. +You can use these build environment variables in your Argo CD Application manifests. You can enable this by setting `.spec.source.kustomize.commonAnnotationsEnvsubst` to `true` in your Application manifest. + +For example, the following Application manifest will set the `app-source` annotation to the name of the Application: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: guestbook-app + namespace: argocd +spec: + project: default + destination: + namespace: demo + server: https://kubernetes.default.svc + source: + path: kustomize-guestbook + repoURL: https://github.com/argoproj/argocd-example-apps + targetRevision: HEAD + kustomize: + commonAnnotationsEnvsubst: true + commonAnnotations: + app-source: ${ARGOCD_APP_NAME} + syncPolicy: + syncOptions: + - CreateNamespace=true +``` + ## Kustomizing Helm charts It's possible to [render Helm charts with Kustomize](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/chart.md). @@ -104,3 +260,10 @@ data: kustomize.buildOptions: --enable-helm ``` +## Setting the manifests' namespace + +The `spec.destination.namespace` field only adds a namespace when it's missing from the manifests generated by Kustomize. It also uses `kubectl` to set the namespace, which sometimes misses namespace fields in certain resources (for example, custom resources). In these cases, you might get an error like this: `ClusterRoleBinding.rbac.authorization.k8s.io "example" is invalid: subjects[0].namespace: Required value.` + +Using Kustomize directly to set the missing namespaces can resolve this problem. Setting `spec.source.kustomize.namespace` instructs Kustomize to set namespace fields to the given value. + +If `spec.destination.namespace` and `spec.source.kustomize.namespace` are both set, Argo CD will defer to the latter, the namespace value set by Kustomize. diff --git a/docs/user-guide/multiple_sources.md b/docs/user-guide/multiple_sources.md index 5aef3825389f7..e539f8f6288aa 100644 --- a/docs/user-guide/multiple_sources.md +++ b/docs/user-guide/multiple_sources.md @@ -36,6 +36,9 @@ spec: The above example has two sources specified. Argo CD will generate the manifests for each source separately and combine the resulting manifests. +!!! warning "Do not abuse multiple sources" + Note that the example above is just for illustration purposes. This feature is **NOT** destined as a generic way to group your applications. Take a look at [applicationsets](../user-guide/application-set.md) and the [app-of-apps](../../operator-manual/cluster-bootstrapping/) pattern if you want to have a single entity for multiple applications. If you find yourself using more than 2-3 items in the `sources` array then you are almost certainly abusing this feature and you need to rethink your application grouping strategy. + If multiple sources produce the same resource (same `group`, `kind`, `name`, and `namespace`), the last source to produce the resource will take precedence. Argo CD will produce a `RepeatedResourceWarning` in this case, but it will sync the resources. This provides a convenient way to override a resource from a chart with a resource from a Git repo. @@ -56,7 +59,7 @@ spec: helm: valueFiles: - $values/charts/prometheus/values.yaml - - repoURL: 'https://git.example.gom/org/value-files.git' + - repoURL: 'https://git.example.com/org/value-files.git' targetRevision: dev ref: values ``` @@ -71,3 +74,6 @@ at that URL. If the `path` field is not set, Argo CD will use the repository sol !!! note Sources with the `ref` field set must not also specify the `chart` field. Argo CD does not currently support using another Helm chart as a source for value files. + +!!! note + Even when the `ref` field is configured with the `path` field, `$value` still represents the root of sources with the `ref` field. Consequently, `valueFiles` must be specified as relative paths from the root of sources. diff --git a/docs/user-guide/private-repositories.md b/docs/user-guide/private-repositories.md index 04fedaea4f99d..074e5caad9e96 100644 --- a/docs/user-guide/private-repositories.md +++ b/docs/user-guide/private-repositories.md @@ -3,7 +3,7 @@ !!!note Some Git hosters - notably GitLab and possibly on-premise GitLab instances as well - require you to specify the `.git` suffix in the repository URL, otherwise they will send a HTTP 301 redirect to the - repository URL suffixed with `.git`. ArgoCD will **not** follow these redirects, so you have to + repository URL suffixed with `.git`. Argo CD will **not** follow these redirects, so you have to adapt your repository URL to be suffixed with `.git`. ## Credentials @@ -26,13 +26,13 @@ or UI: ![connect repo overview](../assets/repo-add-overview.png) -1. Click `Connect Repo using HTTPS` button and enter credentials +2. Click `Connect Repo using HTTPS` button and enter credentials ![connect repo](../assets/repo-add-https.png) *Note: username in screenshot is for illustration purposes only , we have no relationship to this GitHub account should it exist.* -1. Click `Connect` to test the connection and have the repository added +3. Click `Connect` to test the connection and have the repository added ![connect repo](../assets/connect-repo.png) @@ -52,7 +52,7 @@ Then, connect the repository using any non-empty string as username and the acce ### TLS Client Certificates for HTTPS repositories -If your repository server requires you to use TLS client certificates for authentication, you can configure ArgoCD repositories to make use of them. For this purpose, `--tls-client-cert-path` and `--tls-client-cert-key-path` switches to the `argocd repo add` command can be used to specify the files on your local system containing client certificate and the corresponding key, respectively: +If your repository server requires you to use TLS client certificates for authentication, you can configure Argo CD repositories to make use of them. For this purpose, `--tls-client-cert-path` and `--tls-client-cert-key-path` switches to the `argocd repo add` command can be used to specify the files on your local system containing client certificate and the corresponding key, respectively: ``` argocd repo add https://repo.example.com/repo.git --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key @@ -63,7 +63,7 @@ Of course, you can also use this in combination with the `--username` and `--pas Your TLS client certificate and corresponding key can also be configured using the UI, see instructions for adding Git repos using HTTPS. !!! note - Your client certificate and key data must be in PEM format, other formats (such as PKCS12) are not understood. Also make sure that your certificate's key is not password protected, otherwise it cannot be used by ArgoCD. + Your client certificate and key data must be in PEM format, other formats (such as PKCS12) are not understood. Also make sure that your certificate's key is not password protected, otherwise it cannot be used by Argo CD. !!! note When pasting TLS client certificate and key in the text areas in the web UI, make sure they contain no unintended line breaks or additional characters. @@ -92,11 +92,11 @@ Using the UI: ![connect repo overview](../assets/repo-add-overview.png) -1. Click `Connect Repo using SSH` button, enter the URL and paste the SSH private key +2. Click `Connect Repo using SSH` button, enter the URL and paste the SSH private key ![connect repo](../assets/repo-add-ssh.png) -1. Click `Connect` to test the connection and have the repository added +3. Click `Connect` to test the connection and have the repository added !!!note When pasting SSH private key in the UI, make sure there are no unintended line breaks or additional characters in the text area @@ -125,11 +125,11 @@ Using the UI: ![connect repo overview](../assets/repo-add-overview.png) -1. Click `Connect Repo using GitHub App` button, enter the URL, App Id, Installation Id, and the app's private key. +2. Click `Connect Repo using GitHub App` button, enter the URL, App Id, Installation Id, and the app's private key. ![connect repo](../assets/repo-add-github-app.png) -1. Click `Connect` to test the connection and have the repository added +3. Click `Connect` to test the connection and have the repository added !!!note When pasting GitHub App private key in the UI, make sure there are no unintended line breaks or additional characters in the text area @@ -155,11 +155,11 @@ Using the UI: ![connect repo overview](../assets/repo-add-overview.png) -1. Click `Connect Repo using Google Cloud Source` button, enter the URL and the Google Cloud service account in JSON format. +2. Click `Connect Repo using Google Cloud Source` button, enter the URL and the Google Cloud service account in JSON format. ![connect repo](../assets/repo-add-google-cloud-source.png) -1. Click `Connect` to test the connection and have the repository added +3. Click `Connect` to test the connection and have the repository added ## Credential templates @@ -169,7 +169,7 @@ To set up a credential template using the Web UI, simply fill in all relevant cr To manage credential templates using the CLI, use the `repocreds` sub-command, for example `argocd repocreds add https://github.com/argoproj --username youruser --password yourpass` would setup a credential template for the URL prefix `https://github.com/argoproj` using the specified username/password combination. Similar to the `repo` sub-command, you can also list and remove repository credentials using the `argocd repocreds list` and `argocd repocreds rm` commands, respectively. -In order for ArgoCD to use a credential template for any given repository, the following conditions must be met: +In order for Argo CD to use a credential template for any given repository, the following conditions must be met: * The repository must either not be configured at all, or if configured, must not contain any credential information * The URL configured for a credential template (e.g. `https://github.com/argoproj`) must match as prefix for the repository URL (e.g. `https://github.com/argoproj/argocd-example-apps`). @@ -204,7 +204,7 @@ FATA[0000] rpc error: code = Unknown desc = authentication required ## Self-signed & Untrusted TLS Certificates -If you are connecting a repository on a HTTPS server using a self-signed certificate, or a certificate signed by a custom Certificate Authority (CA) which are not known to ArgoCD, the repository will not be added due to security reasons. This is indicated by an error message such as `x509: certificate signed by unknown authority`. +If you are connecting a repository on a HTTPS server using a self-signed certificate, or a certificate signed by a custom Certificate Authority (CA) which are not known to Argo CD, the repository will not be added due to security reasons. This is indicated by an error message such as `x509: certificate signed by unknown authority`. 1. You can let ArgoCD connect the repository in an insecure way, without verifying the server's certificate at all. This can be accomplished by using the `--insecure-skip-server-verification` flag when adding the repository with the `argocd` CLI utility. However, this should be done only for non-production setups, as it imposes a serious security issue through possible man-in-the-middle attacks. @@ -265,21 +265,21 @@ It is possible to add and remove TLS certificates using the ArgoCD web UI: 1. In the navigation pane to the left, click on "Settings" and choose "Certificates" from the settings menu -1. The following page lists all currently configured certificates and provides you with the option to add either a new TLS certificate or SSH known entries: +2. The following page lists all currently configured certificates and provides you with the option to add either a new TLS certificate or SSH known entries: ![manage certificates](../assets/cert-management-overview.png) -1. Click on "Add TLS certificate", fill in relevant data and click on "Create". Take care to specify only the FQDN of your repository server (not the URL) and that you C&P the complete PEM of your TLS certificate into the text area field, including the `----BEGIN CERTIFICATE----` and `----END CERTIFICATE----` lines: +3. Click on "Add TLS certificate", fill in relevant data and click on "Create". Take care to specify only the FQDN of your repository server (not the URL) and that you C&P the complete PEM of your TLS certificate into the text area field, including the `----BEGIN CERTIFICATE----` and `----END CERTIFICATE----` lines: ![add tls certificate](../assets/cert-management-add-tls.png) -1. To remove a certificate, click on the small three-dotted button next to the certificate entry, select "Remove" from the pop-up menu and confirm the removal in the following dialogue. +4. To remove a certificate, click on the small three-dotted button next to the certificate entry, select "Remove" from the pop-up menu and confirm the removal in the following dialogue. ![remove certificate](../assets/cert-management-remove.png) ### Managing TLS certificates using declarative configuration -You can also manage TLS certificates in a declarative, self-managed ArgoCD setup. All TLS certificates are stored in the ConfigMap object `argocd-tls-cert-cm`. +You can also manage TLS certificates in a declarative, self-managed ArgoCD setup. All TLS certificates are stored in the ConfigMap object `argocd-tls-certs-cm`. Please refer to the [Operator Manual](../../operator-manual/declarative-setup/#repositories-using-self-signed-tls-certificates-or-are-signed-by-custom-ca) for more information. ## Unknown SSH Hosts @@ -303,8 +303,8 @@ You can list all configured SSH known host entries using the `argocd cert list` ```bash $ argocd cert list --cert-type ssh HOSTNAME TYPE SUBTYPE FINGERPRINT/SUBJECT -bitbucket.org ssh ssh-rsa SHA256:zzXQOXSRBEiUtuE8AikJYKwbHaxvSc0ojez9YXaGp1A -github.com ssh ssh-rsa SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8 +bitbucket.org ssh ssh-rsa SHA256:46OSHA1Rmj8E8ERTC6xkNcmGOw9oFxYr0WF6zWW8l1E +github.com ssh ssh-rsa SHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s gitlab.com ssh ecdsa-sha2-nistp256 SHA256:HbW3g8zUjNSksFbqTiUWPWg2Bq1x8xdGUrliXFzSnUw gitlab.com ssh ssh-ed25519 SHA256:eUXGGm1YGsMAS7vkcx6JOJdOGHPem5gQp4taiCfCLB8 gitlab.com ssh ssh-rsa SHA256:ROQFvPThGrW4RuWLoL9tq9I9zJ42fK4XywyRtbOz/EQ @@ -345,15 +345,15 @@ It is possible to add and remove SSH known hosts entries using the ArgoCD web UI 1. In the navigation pane to the left, click on "Settings" and choose "Certificates" from the settings menu -1. The following page lists all currently configured certificates and provides you with the option to add either a new TLS certificate or SSH known entries: +2. The following page lists all currently configured certificates and provides you with the option to add either a new TLS certificate or SSH known entries: ![manage certificates](../assets/cert-management-overview.png) -1. Click on "Add SSH known hosts" and paste your SSH known hosts data in the following mask. **Important**: Make sure there are no line breaks in the entries (key data) when you paste the data. Afterwards, click on "Create". +3. Click on "Add SSH known hosts" and paste your SSH known hosts data in the following mask. **Important**: Make sure there are no line breaks in the entries (key data) when you paste the data. Afterwards, click on "Create". ![manage ssh known hosts](../assets/cert-management-add-ssh.png) -1. To remove a certificate, click on the small three-dotted button next to the certificate entry, select "Remove" from the pop-up menu and confirm the removal in the following dialogue. +4. To remove a certificate, click on the small three-dotted button next to the certificate entry, select "Remove" from the pop-up menu and confirm the removal in the following dialogue. ![remove certificate](../assets/cert-management-remove.png) diff --git a/docs/user-guide/projects.md b/docs/user-guide/projects.md index 0973a19afe1fc..f5979cf3c47b3 100644 --- a/docs/user-guide/projects.md +++ b/docs/user-guide/projects.md @@ -3,8 +3,8 @@ Projects provide a logical grouping of applications, which is useful when Argo CD is used by multiple teams. Projects provide the following features: -* restrict *what* may be deployed (trusted Git source repositories) -* restrict *where* apps may be deployed to (destination clusters and namespaces) +* restrict what may be deployed (trusted Git source repositories) +* restrict where apps may be deployed to (destination clusters and namespaces) * restrict what kinds of objects may or may not be deployed (e.g. RBAC, CRDs, DaemonSets, NetworkPolicy etc...) * defining project roles to provide application RBAC (bound to OIDC groups and/or JWT tokens) @@ -271,15 +271,15 @@ projectName: `proj-global-test` should be replaced with your own global project ## Project scoped Repositories and Clusters -Normally, an ArgoCD admin creates a project and decides in advance which clusters and Git repositories +Normally, an Argo CD admin creates a project and decides in advance which clusters and Git repositories it defines. However, this creates a problem in scenarios where a developer wants to add a repository or cluster -after the initial creation of the project. This forces the developer to contact their ArgoCD admin again to update the project definition. +after the initial creation of the project. This forces the developer to contact their Argo CD admin again to update the project definition. It is possible to offer a self-service process for developers so that they can add a repository and/or cluster in a project on their own even after the initial creation of the project. -For this purpose ArgoCD supports project-scoped repositories and clusters. +For this purpose Argo CD supports project-scoped repositories and clusters. -To begin the process, ArgoCD admins must configure RBAC security to allow this self-service behavior. +To begin the process, Argo CD admins must configure RBAC security to allow this self-service behavior. For example, to allow users to add project scoped repositories and admin would have to add the following RBAC rules: @@ -292,7 +292,7 @@ p, proj:my-project:admin, repositories, update, my-project/*, allow This provides extra flexibility so that admins can have stricter rules. e.g.: ``` -p, proj:my-project:admin, repositories, update, my-project/https://github.my-company.com/*, allow +p, proj:my-project:admin, repositories, update, my-project/https://github.example.com/*, allow ``` Once the appropriate RBAC rules are in place, developers can create their own Git repositories and (assuming @@ -321,7 +321,29 @@ stringData: All the examples above talk about Git repositories, but the same principles apply to clusters as well. -With cluster-scoped clusters we can also restrict projects to only allow applications whose destinations belong to the +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: mycluster-secret + labels: + argocd.argoproj.io/secret-type: cluster +type: Opaque +stringData: + name: mycluster.example.com + project: my-project1 # Project scoped + server: https://mycluster.example.com + config: | + { + "bearerToken": "", + "tlsClientConfig": { + "insecure": false, + "caData": "" + } + } +``` + +With project-scoped clusters we can also restrict projects to only allow applications whose destinations belong to the same project. The default behavior allows for applications to be installed onto clusters which are not a part of the same project, as the example below demonstrates: @@ -346,4 +368,4 @@ spec: ``` With this set, the application above would no longer be allowed to be synced to any cluster other than the ones which -are a part of the same project. \ No newline at end of file +are a part of the same project. diff --git a/docs/user-guide/resource_hooks.md b/docs/user-guide/resource_hooks.md index 9f8f98e033a20..6e15a55bb20c2 100644 --- a/docs/user-guide/resource_hooks.md +++ b/docs/user-guide/resource_hooks.md @@ -8,7 +8,9 @@ and after a Sync operation. Hooks can also be run if a Sync operation fails at a * Using a `Sync` hook to orchestrate a complex deployment requiring more sophistication than the Kubernetes rolling update strategy. * Using a `PostSync` hook to run integration and health checks after a deployment. -* Using a `SyncFail` hook to run clean-up or finalizer logic if a Sync operation fails. _`SyncFail` hooks are only available starting in v1.2_ +* Using a `SyncFail` hook to run clean-up or finalizer logic if a Sync operation fails. +* Using a `PostDelete` hook to run clean-up or finalizer logic after all Application resources are deleted. Please note that + `PostDelete` hooks are only deleted if the delete policy matches the aggregated deletion hooks status and not garbage collected after the application is deleted. ## Usage @@ -37,7 +39,8 @@ The following hooks are defined: | `Sync` | Executes after all `PreSync` hooks completed and were successful, at the same time as the application of the manifests. | | `Skip` | Indicates to Argo CD to skip the application of the manifest. | | `PostSync` | Executes after all `Sync` hooks completed and were successful, a successful application, and all resources in a `Healthy` state. | -| `SyncFail` | Executes when the sync operation fails. _Available starting in v1.2_ | +| `SyncFail` | Executes when the sync operation fails. | +| `PostDelete` | Executes after all Application resources are deleted. _Available starting in v2.10._ | ### Generate Name @@ -60,6 +63,7 @@ metadata: argocd.argoproj.io/hook: PostSync argocd.argoproj.io/hook-delete-policy: HookSucceeded ``` +Multiple hook delete policies can be specified as a comma separated list. The following policies define when the hook will be deleted. @@ -69,7 +73,7 @@ The following policies define when the hook will be deleted. | `HookFailed` | The hook resource is deleted after the hook failed. | | `BeforeHookCreation` | Any existing hook resource is deleted before the new one is created (since v1.3). It is meant to be used with `/metadata/name`. | -Note that if no deletion policy is specified, ArgoCD will automatically assume `BeforeHookCreation` rules. +Note that if no deletion policy is specified, Argo CD will automatically assume `BeforeHookCreation` rules. ### Sync Status with Jobs/Workflows with Time to Live (ttl) diff --git a/docs/user-guide/resource_tracking.md b/docs/user-guide/resource_tracking.md index cb69ef143d7a2..e62a7c094f4e2 100644 --- a/docs/user-guide/resource_tracking.md +++ b/docs/user-guide/resource_tracking.md @@ -24,6 +24,22 @@ There are however several limitations: * Other external tools might write/append to this label and create conflicts with Argo CD. For example several Helm charts and operators also use this label for generated manifests confusing Argo CD about the owner of the application * You might want to deploy more than one Argo CD instance on the same cluster (with cluster wide privileges) and have an easy way to identify which resource is managed by which instance of Argo CD +### Use custom label + +Instead of using the default `app.kubernetes.io/instance` label for resource tracking, Argo CD can be configured to use a custom label. Below example sets the resource tracking label to `argocd.argoproj.io/instance`. + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cm + labels: + app.kubernetes.io/name: argocd-cm + app.kubernetes.io/part-of: argocd +data: + application.instanceLabelKey: argocd.argoproj.io/instance +``` + ## Additional tracking methods via an annotation >v2.2 @@ -49,18 +65,28 @@ metadata: The advantages of using the tracking id annotation is that there are no clashes any more with other Kubernetes tools and Argo CD is never confused about the owner of a resource. The `annotation+label` can also be used if you want other tools to understand resources managed by Argo CD. +### Non self-referencing annotations +When using the tracking method `annotation` or `annotation+label`, Argo CD will consider the resource properties in the annotation (name, namespace, group and kind) to determine whether the resource should be compared against the desired state. If the tracking annotation does not reference the resource it is applied to, the resource will neither affect the application's sync status nor be marked for pruning. + +This allows other kubernetes tools (e.g. [HNC](https://github.com/kubernetes-sigs/hierarchical-namespaces)) to copy a resource to a different namespace without impacting the Argo CD application's sync status. Copied resources will be visible on the UI at top level. They will have no sync status and won't impact the application's sync status. + ## Choosing a tracking method To actually select your preferred tracking method edit the `resourceTrackingMethod` value contained inside the `argocd-cm` configmap. ```yaml apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cm + labels: + app.kubernetes.io/name: argocd-cm + app.kubernetes.io/part-of: argocd data: application.resourceTrackingMethod: annotation -kind: ConfigMap ``` Possible values are `label`, `annotation+label` and `annotation` as described in the previous section. Note that once you change the value you need to sync your applications again (or wait for the sync mechanism to kick-in) in order to apply your changes. -You can revert to a previous choice, by changing again the configmap. \ No newline at end of file +You can revert to a previous choice, by changing again the configmap. diff --git a/docs/user-guide/skip_reconcile.md b/docs/user-guide/skip_reconcile.md new file mode 100644 index 0000000000000..d5eec86a6a866 --- /dev/null +++ b/docs/user-guide/skip_reconcile.md @@ -0,0 +1,70 @@ +# Skip Application Reconcile + +!!! warning "Alpha Feature" + This is an experimental, alpha-quality feature. + The primary use case is to provide integration with third party projects. + This feature may be removed in future releases or modified in backwards-incompatible ways. + +Argo CD allows users to stop an Application from reconciling. +The skip reconcile option is configured with the `argocd.argoproj.io/skip-reconcile: "true"` annotation. +When the Application is configured to skip reconcile, +all processing is stopped for the Application. +During the period of time when the Application is not processing, +the Application `status` field will not be updated. +If an Application is newly created with the skip reconcile annotation, +then the Application `status` field will not be present. +To resume the reconciliation or processing of the Application, +remove the annotation or set the value to `"false"`. + +See the below example for enabling an Application to skip reconcile: + +```yaml +metadata: + annotations: + argocd.argoproj.io/skip-reconcile: "true" +``` + +See the below example for an Application that is newly created with the skip reconcile enabled: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + argocd.argoproj.io/skip-reconcile: "true" + name: guestbook + namespace: argocd +spec: + destination: + namespace: guestbook + server: https://kubernetes.default.svc + project: default + source: + path: guestbook + repoURL: https://github.com/argoproj/argocd-example-apps.git + targetRevision: HEAD +``` + +The `status` field is not present. + +## Primary Use Case + +The skip reconcile option is intended to be used with third party projects that wishes +to make updates to the Application status without having the changes being overwritten by the Application controller. +An example of this usage is the [Open Cluster Management (OCM)](https://github.com/open-cluster-management-io/) project using +[pull-integration](https://github.com/open-cluster-management-io/argocd-pull-integration) controller. +In the example, the hub cluster Application is not meant to be reconciled by the Argo CD Application controller. +Instead, the OCM pull-integration controller will populate the primary/hub cluster Application status +using the collected Application status from the remote/spoke/managed cluster. + +## Alternative Use Cases + +There are other alternative use cases for this skip reconcile option. +It's important to note that this is an experimental, alpha-quality feature +and the following use cases are generally not recommended. + +* Ease of debugging when the Application reconcile is skipped. +* Orphan resources without deleting the Application might provide a safer way to migrate applications. +* ApplicationSet can generate dry-run like Applications that don't reconcile automatically. +* Pause and resume Applications reconcile during a disaster recovery process. +* Provide another alternative approval flow by not allowing an Application to start reconciling right away. diff --git a/docs/user-guide/status-badge.md b/docs/user-guide/status-badge.md index 8355be458f026..3363227997309 100644 --- a/docs/user-guide/status-badge.md +++ b/docs/user-guide/status-badge.md @@ -9,7 +9,12 @@ To show this badge, use the following URL format `${argoCdBaseUrl}/api/badge?nam The URLs for status image are available on application details page: 1. Navigate to application details page and click on 'Details' button. -1. Scroll down to 'Status Badge' section. -1. Select required template such as URL, Markdown etc. +2. Scroll down to 'Status Badge' section. +3. Select required template such as URL, Markdown etc. for the status image URL in markdown, html, etc are available . -1. Copy the text and paste it into your README or website. \ No newline at end of file +4. Copy the text and paste it into your README or website. + +The application name may optionally be displayed in the status badge by adding the `?showAppName=true` query parameter. + +For example, `${argoCdBaseUrl}/api/badge?name=${appName}&showAppName=true`. +To remove the application name from the badge, remove the query parameter from the URL or set it to `false`. \ No newline at end of file diff --git a/docs/user-guide/sync-kubectl.md b/docs/user-guide/sync-kubectl.md new file mode 100644 index 0000000000000..100ec2cdf70b1 --- /dev/null +++ b/docs/user-guide/sync-kubectl.md @@ -0,0 +1,144 @@ +# Sync Applications with Kubectl + +You can use "kubectl" to ask Argo CD to synchronize applications the same way you can use the CLI or UI. Many configurations like "force", "prune", "apply" and even synchronize a specific list of resources are equally supported. This is done by applying or patching the Argo CD application with a document that defines an "operation". + +This "operation" defines how a synchronization should be done and for what resources these synchronization is to be done. + +There are many configuration options that can be added to the "operation". Next, a few of them are explained. For more details, you can have a look at the CRD [applications.argoproj.io](https://github.com/argoproj/argo-cd/blob/master/manifests/crds/application-crd.yaml). Some of them are required, whereas others are optional. + +To ask Argo CD to synchronize all resources of a given application, we can do: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: + namespace: +spec: + ... +operation: + initiatedBy: + username: + sync: + syncStrategy: + hook: {} +``` + +```bash +$ kubectl apply -f +``` + +The most important part is the "sync" definition in the "operation" field. You can pass optional information like "info" or "initiatedBy". "info" allows you to add information about the operation in the form of a list. "initiatedBy" contains information about who initiated the operation request. + +Or if you prefer, you also can patch: + +```yaml +operation: + initiatedBy: + username: + sync: + syncStrategy: + hook: {} +``` + +```bash +$ kubectl patch -n app --patch-file --type merge +``` + +Be aware that patches, specially with merge strategies, may not work the way you expect especially if you change sync strategies or options. +In these cases, "kubectl apply" gives better results. + +Either with a "kubectl patch" or "kubectl apply", the state of the synchronization is reported in the "operationState" field in the application object. + +```bash +$ kubectl get -n get app -o yaml +... +status: + operationState: + finishedAt: "2023-08-03T11:16:17Z" + message: successfully synced (all tasks run) + phase: Succeeded +``` + +# Apply and Hook synchronization strategies + +There are two types of synchronization strategies: "hook", which is the default value, and "apply". + +An "apply" sync strategy tells Argo CD to "kubectl apply", whereas a "hook" sync strategy informs Argo CD to submit any resource that's referenced in the operation. This way the synchronization of these resources will take into consideration any hook the resource has been annotated with. + +```yaml +operation: + sync: + syncStrategy: + apply: {} +``` + +```yaml +operation: + sync: + syncStrategy: + hook: {} +``` + +Both strategies support "force". However, you need to be aware that a force operation deletes the resource when patch encounters a conflict after having retried 5 times. + +```yaml +operation: + sync: + syncStrategy: + apply: + force: true +``` + +```yaml +operation: + sync: + syncStrategy: + hook: + force: true +``` + +# Prune + +If you want to prune your resources before applying, you can instruct Argo CD to do so: + +```yaml +operation: + sync: + prune: true +``` + +# List of resources + +There's always the possibility to pass a list of resources. This list can be all resources the application manages or only a subset, for example resources that remained out of sync for some reason. + +Only "kind" and "name" are required fields when referencing resources, but the fields "groups" and "namespace" can also be defined: + +```yaml +operation: + sync: + resources: + - kind: Namespace + name: namespace-name + - kind: ServiceAccount + name: service-account-name + namespace: namespace-name + - group: networking.k8s.io + kind: NetworkPolicy + name: network-policy-name + namespace: namespace-name +``` + +# Sync Options + +In an operation, you can also pass sync-options. Each of these options is passed as "name=value" pairs. For example: + +```yaml +operations: + sync: + syncOptions: + - Validate=false + - Prune=false +``` + +For more information about sync options, please refer to [sync-options](https://argo-cd.readthedocs.io/en/stable/user-guide/sync-options/) diff --git a/docs/user-guide/sync-options.md b/docs/user-guide/sync-options.md index 098291e80d28b..a563821967d04 100644 --- a/docs/user-guide/sync-options.md +++ b/docs/user-guide/sync-options.md @@ -1,6 +1,6 @@ # Sync Options -Argo CD allows users to customize some aspects of how it syncs the desired state in the target cluster. Some Sync Options can defined as annotations in a specific resource. Most of the Sync Options are configured in the Application resource `spec.syncPolicy.syncOptions` attribute. Multiple Sync Options which are configured with the `argocd.argoproj.io/sync-options` annotation can be concatenated with a `,` in the annotation value; white spaces will be trimmed. +Argo CD allows users to customize some aspects of how it syncs the desired state in the target cluster. Some Sync Options can be defined as annotations in a specific resource. Most of the Sync Options are configured in the Application resource `spec.syncPolicy.syncOptions` attribute. Multiple Sync Options which are configured with the `argocd.argoproj.io/sync-options` annotation can be concatenated with a `,` in the annotation value; white spaces will be trimmed. Below you can find details about each available Sync Option: @@ -29,7 +29,7 @@ The app will be out of sync if Argo CD expects a resource to be pruned. You may ## Disable Kubectl Validation -For a certain class of objects, it is necessary to `kubectl apply` them using the `--validate=false` flag. Examples of this are kubernetes types which uses `RawExtension`, such as [ServiceCatalog](https://github.com/kubernetes-incubator/service-catalog/blob/master/pkg/apis/servicecatalog/v1beta1/types.go#L497). You can do using this annotations: +For a certain class of objects, it is necessary to `kubectl apply` them using the `--validate=false` flag. Examples of this are Kubernetes types which uses `RawExtension`, such as [ServiceCatalog](https://github.com/kubernetes-incubator/service-catalog/blob/master/pkg/apis/servicecatalog/v1beta1/types.go#L497). You can do using this annotations: ```yaml @@ -58,6 +58,18 @@ metadata: The dry run will still be executed if the CRD is already present in the cluster. +## No Resource Deletion + +For certain resources you might want to retain them even after your application is deleted, for eg. Persistent Volume Claims. +In such situations you can stop those resources from being cleaned up during app deletion by using the following annotation: + + +```yaml +metadata: + annotations: + argocd.argoproj.io/sync-options: Delete=false +``` + ## Selective Sync Currently when syncing using auto sync Argo CD applies every object in the application. @@ -258,7 +270,7 @@ spec: - RespectIgnoreDifferences=true ``` -The example above shows how an Argo CD Application can be configured so it will ignore the `spec.replicas` field from the desired state (git) during the sync stage. This is achieve by calculating and pre-patching the desired state before applying it in the cluster. Note that the `RespectIgnoreDifferences` sync option is only effective when the resource is already created in the cluster. If the Application is being created and no live state exists, the desired state is applied as-is. +The example above shows how an Argo CD Application can be configured so it will ignore the `spec.replicas` field from the desired state (git) during the sync stage. This is achieved by calculating and pre-patching the desired state before applying it in the cluster. Note that the `RespectIgnoreDifferences` sync option is only effective when the resource is already created in the cluster. If the Application is being created and no live state exists, the desired state is applied as-is. ## Create Namespace @@ -304,10 +316,10 @@ spec: - CreateNamespace=true ``` -In order for ArgoCD to manage the labels and annotations on the namespace, `CreateNamespace=true` needs to be set as a +In order for Argo CD to manage the labels and annotations on the namespace, `CreateNamespace=true` needs to be set as a sync option, otherwise nothing will happen. If the namespace doesn't already exist, or if it already exists and doesn't already have labels and/or annotations set on it, you're good to go. Using `managedNamespaceMetadata` will also set the -resource tracking label (or annotation) on the namespace, so you can easily track which namespaces are managed by ArgoCD. +resource tracking label (or annotation) on the namespace, so you can easily track which namespaces are managed by Argo CD. In the case you do not have any custom annotations or labels but would nonetheless want to have resource tracking set on your namespace, that can be done by setting `managedNamespaceMetadata` with an empty `labels` and/or `annotations` map, @@ -327,53 +339,13 @@ spec: - CreateNamespace=true ``` -In the case where ArgoCD is "adopting" an existing namespace which already has metadata set on it, we rely on using -Server Side Apply in order not to lose metadata which has already been set. The main implication here is that it takes -a few extra steps to get rid of an already preexisting field. - -Imagine we have a pre-existing namespace as below: - -```yaml -apiVersion: v1 -kind: Namespace -metadata: - name: foobar - annotations: - foo: bar - abc: "123" -``` - -If we want to manage the `foobar` namespace with ArgoCD and to then also remove the `foo: bar` annotation, in -`managedNamespaceMetadata` we'd need to first rename the `foo` value: - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -spec: - syncPolicy: - managedNamespaceMetadata: - annotations: - abc: 123 # adding this is informational with SSA; this would be sticking around in any case until we set a new value - foo: remove-me - syncOptions: - - CreateNamespace=true -``` - -Once that has been synced, we're ok to remove `foo` - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Application -spec: - syncPolicy: - managedNamespaceMetadata: - annotations: - abc: 123 # adding this is informational with SSA; this would be sticking around in any case until we set a new value - syncOptions: - - CreateNamespace=true -``` +In the case where Argo CD is "adopting" an existing namespace which already has metadata set on it, you should first +[upgrade the resource to server-side apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/#upgrading-from-client-side-apply-to-server-side-apply) +before enabling `managedNamespaceMetadata`. Argo CD relies on `kubectl`, which does not support managing +client-side-applied resources with server-side-applies. If you do not upgrade the resource to server-side apply, Argo CD +may remove existing labels/annotations, which may or may not be the desired behavior. -Another thing to keep mind of is that if you have a k8s manifest for the same namespace in your ArgoCD application, that +Another thing to keep mind of is that if you have a k8s manifest for the same namespace in your Argo CD application, that will take precedence and *overwrite whatever values that have been set in `managedNamespaceMetadata`*. In other words, if you have an application that sets `managedNamespaceMetadata` diff --git a/docs/user-guide/sync-waves.md b/docs/user-guide/sync-waves.md index 932ba396d68d2..8b17237c87571 100644 --- a/docs/user-guide/sync-waves.md +++ b/docs/user-guide/sync-waves.md @@ -37,7 +37,7 @@ Hooks and resources are assigned to wave zero by default. The wave can be negati When Argo CD starts a sync, it orders the resources in the following precedence: * The phase -* The wave they are in (lower values first) +* The wave they are in (lower values first for creation & updation and higher values first for deletion) * By kind (e.g. [namespaces first and then other Kubernetes resources, followed by custom resources](https://github.com/argoproj/gitops-engine/blob/bc9ce5764fa306f58cf59199a94f6c968c775a2d/pkg/sync/sync_tasks.go#L27-L66)) * By name @@ -49,6 +49,8 @@ It repeats this process until all phases and waves are in-sync and healthy. Because an application can have resources that are unhealthy in the first wave, it may be that the app can never get to healthy. +During pruning of resources, resources from higher waves are processed first before moving to lower waves. If, for any reason, a resource isn't removed/pruned in a wave, the resources in next waves won't be processed. This is to ensure proper resource cleanup between waves. + Note that there's currently a delay between each sync wave in order give other controllers a chance to react to the spec change that we just applied. This also prevent Argo CD from assessing resource health too quickly (against the stale object), causing hooks to fire prematurely. The current delay between each sync wave is 2 seconds and can be configured via environment diff --git a/docs/user-guide/sync_windows.md b/docs/user-guide/sync_windows.md index 8129e78c27062..f6bc6b82f8b69 100644 --- a/docs/user-guide/sync_windows.md +++ b/docs/user-guide/sync_windows.md @@ -7,7 +7,7 @@ of both manual and automated syncs but allow an override for manual syncs which in preventing automated syncs or if you need to temporarily override a window to perform a sync. The windows work in the following way. If there are no windows matching an application then all syncs are allowed. If there -are any `allow` windows matching an application then syncs will only be allowed when there is an active `allow` windows. If there +are any `allow` windows matching an application then syncs will only be allowed when there is an active `allow` window. If there are any `deny` windows matching an application then all syncs will be denied when the `deny` windows are active. If there is an active matching `allow` and an active matching `deny` then syncs will be denied as `deny` windows override `allow` windows. The UI and the CLI will both display the state of the sync windows. The UI has a panel which will display different colours depending @@ -64,6 +64,7 @@ spec: manualSync: true - kind: deny schedule: '0 22 * * *' + timeZone: "Europe/Amsterdam" duration: 1h namespaces: - default diff --git a/docs/user-guide/tracking_strategies.md b/docs/user-guide/tracking_strategies.md index e1abe85717724..57dfc5f907b65 100644 --- a/docs/user-guide/tracking_strategies.md +++ b/docs/user-guide/tracking_strategies.md @@ -11,7 +11,7 @@ is detected. ## Helm -For Helm, all versions are [Semantic Versions](https://semver.org/). As a result, you can either version ranges: +Helm chart versions are [Semantic Versions](https://semver.org/). As a result, you can use any of the following version ranges: | Use Case | How | Examples | |-|-|-| @@ -19,6 +19,7 @@ For Helm, all versions are [Semantic Versions](https://semver.org/). As a result | Track patches (e.g. in pre-production) | Use a range | `1.2.*` or `>=1.2.0 <1.3.0` | | Track minor releases (e.g. in QA) | Use a range | `1.*` or `>=1.0.0 <2.0.0` | | Use the latest (e.g. in local development) | Use star range | `*` or `>=0.0.0` | +| Use the latest including pre-releases | Use star range with `-0` suffix | `*-0` or `>=0.0.0-0` | [Read about version ranges](https://www.telerik.com/blogs/the-mystical-magical-semver-ranges-used-by-npm-bower) diff --git a/entrypoint.sh b/entrypoint.sh index 88515e217e4be..24862aca2172d 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # If we're started as PID 1, we should wrap command execution through tini to # prevent leakage of orphaned processes ("zombies"). diff --git a/examples/dashboard.json b/examples/dashboard.json index 7e992a5363324..108ac81918ba3 100644 --- a/examples/dashboard.json +++ b/examples/dashboard.json @@ -3,7 +3,10 @@ "list": [ { "builtIn": 1, - "datasource": "-- Grafana --", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", @@ -13,15 +16,17 @@ ] }, "editable": true, - "gnetId": null, + "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 1, - "iteration": 1605574886303, + "id": 28, "links": [], + "liveNow": false, "panels": [ { "collapsed": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "gridPos": { "h": 1, "w": 24, @@ -30,12 +35,21 @@ }, "id": 68, "panels": [], + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "refId": "A" + } + ], "title": "Overview", "type": "row" }, { - "content": "![argoimage](https://avatars1.githubusercontent.com/u/30269780?s=110&v=4)", - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "gridPos": { "h": 4, "w": 2, @@ -44,29 +58,64 @@ }, "id": 26, "links": [], - "mode": "markdown", - "options": {}, - "title": "", + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "![argoimage](https://avatars1.githubusercontent.com/u/30269780?s=110&v=4)", + "mode": "markdown" + }, + "pluginVersion": "10.3.1", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "refId": "A" + } + ], "transparent": true, "type": "text" }, { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "$datasource", - "format": "dtdurations", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "dtdurations", + "unitScale": true + }, + "overrides": [] }, "gridPos": { "h": 4, @@ -75,79 +124,77 @@ "y": 1 }, "id": 32, - "interval": null, "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true }, - "tableColumn": "", + "pluginVersion": "10.3.1", "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "time() - max(process_start_time_seconds{job=\"argocd-server-metrics\",namespace=~\"$namespace\"})", "format": "time_series", "intervalFactor": 1, "refId": "A" } ], - "thresholds": "", "title": "Uptime", - "type": "singlestat", - "valueFontSize": "70%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" + "type": "stat" }, { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "$datasource", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "0" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none", + "unitScale": true + }, + "overrides": [] }, "gridPos": { "h": 4, @@ -156,43 +203,30 @@ "y": 1 }, "id": 94, - "interval": null, "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true }, - "tableColumn": "", + "pluginVersion": "10.3.1", "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "count(count by (server) (argocd_cluster_info{namespace=~\"$namespace\"}))", "format": "time_series", "instant": false, @@ -200,40 +234,47 @@ "refId": "A" } ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, "title": "Clusters", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "0", - "value": "null" - } - ], - "valueName": "current" + "type": "stat" }, { - "cacheTimeout": null, - "colorBackground": false, - "colorPostfix": false, - "colorPrefix": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "$datasource", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none", + "unitScale": true + }, + "overrides": [] }, "gridPos": { "h": 4, @@ -242,45 +283,31 @@ "y": 1 }, "id": 75, - "interval": null, "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "repeat": null, - "repeatDirection": "h", - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true }, - "tableColumn": "", + "pluginVersion": "10.3.1", + "repeatDirection": "h", "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(argocd_app_info{namespace=~\"$namespace\",dest_server=~\"$cluster\",health_status=~\"$health_status\",sync_status=~\"$sync_status\"})", "format": "time_series", "instant": false, @@ -288,38 +315,47 @@ "refId": "A" } ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, "title": "Applications", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" + "type": "stat" }, { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "$datasource", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "0" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none", + "unitScale": true + }, + "overrides": [] }, "gridPos": { "h": 4, @@ -328,43 +364,30 @@ "y": 1 }, "id": 107, - "interval": null, "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true }, - "tableColumn": "", + "pluginVersion": "10.3.1", "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "count(count by (repo) (argocd_app_info{namespace=~\"$namespace\"}))", "format": "time_series", "instant": false, @@ -372,24 +395,47 @@ "refId": "A" } ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, "title": "Repositories", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "0", - "value": "null" - } - ], - "valueName": "current" + "type": "stat" }, { - "cacheTimeout": null, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "id": 0, + "op": "=", + "text": "0", + "type": 1, + "value": "null" + } + ], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none", + "unitScale": true + }, + "overrides": [] + }, "gridPos": { "h": 4, "w": 3, @@ -399,47 +445,27 @@ "id": 100, "links": [], "options": { - "fieldOptions": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "horizontal", + "reduceOptions": { "calcs": [ "lastNotNull" ], - "defaults": { - "mappings": [ - { - "id": 0, - "op": "=", - "text": "0", - "type": 1, - "value": "null" - } - ], - "max": 100, - "min": 0, - "nullValueMode": "connected", - "thresholds": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ], - "unit": "none" - }, - "override": {}, - "overrides": [], + "fields": "", "values": false }, - "orientation": "horizontal", "showThresholdLabels": false, - "showThresholdMarkers": true + "showThresholdMarkers": true, + "sizing": "auto" }, - "pluginVersion": "6.5.2", + "pluginVersion": "10.3.1", "repeatDirection": "h", "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(argocd_app_info{namespace=~\"$namespace\",dest_server=~\"$cluster\",operation!=\"\"})", "format": "time_series", "instant": true, @@ -448,19 +474,24 @@ "refId": "A" } ], - "timeFrom": null, - "timeShift": null, "title": "Operations", "type": "gauge" }, { "aliasColors": {}, "bars": false, - "cacheTimeout": null, "dashLength": 10, "dashes": false, - "datasource": "$datasource", - "decimals": null, + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "unitScale": true + }, + "overrides": [] + }, "fill": 1, "fillGradient": 0, "gridPos": { @@ -491,10 +522,11 @@ "links": [], "nullPointMode": "connected", "options": { - "dataLinks": [] + "alertThreshold": true }, "paceLength": 10, "percentage": false, + "pluginVersion": "10.3.1", "pointradius": 2, "points": false, "renderer": "flot", @@ -504,6 +536,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(argocd_app_info{namespace=~\"$namespace\",dest_server=~\"$cluster\",health_status=~\"$health_status\",sync_status=~\"$sync_status\"}) by (namespace)", "format": "time_series", "instant": false, @@ -513,9 +548,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Applications", "tooltip": { "shared": false, @@ -524,9 +557,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -534,29 +565,24 @@ { "decimals": 0, "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { "collapsed": true, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "gridPos": { "h": 1, "w": 24, @@ -575,11 +601,18 @@ "Unknown": "rgb(255, 255, 255)" }, "bars": false, - "cacheTimeout": null, "dashLength": 10, "dashes": false, - "datasource": "$datasource", - "decimals": null, + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "unitScale": true + }, + "overrides": [] + }, "fill": 1, "fillGradient": 0, "gridPos": { @@ -601,7 +634,6 @@ "min": false, "rightSide": true, "show": true, - "sideWidth": null, "sort": "current", "sortDesc": true, "total": false, @@ -612,10 +644,11 @@ "links": [], "nullPointMode": "null as zero", "options": { - "dataLinks": [] + "alertThreshold": true }, "paceLength": 10, "percentage": false, + "pluginVersion": "10.3.1", "pointradius": 2, "points": false, "renderer": "flot", @@ -625,6 +658,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(argocd_app_info{namespace=~\"$namespace\",dest_server=~\"$cluster\",health_status=~\"$health_status\",sync_status=~\"$sync_status\",health_status!=\"\"}) by (health_status)", "format": "time_series", "instant": false, @@ -634,9 +670,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Health Status", "tooltip": { "shared": true, @@ -645,33 +679,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 2, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -686,11 +711,18 @@ "Unknown": "rgb(255, 255, 255)" }, "bars": false, - "cacheTimeout": null, "dashLength": 10, "dashes": false, - "datasource": "$datasource", - "decimals": null, + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "unitScale": true + }, + "overrides": [] + }, "fill": 1, "fillGradient": 0, "gridPos": { @@ -712,7 +744,6 @@ "min": false, "rightSide": true, "show": true, - "sideWidth": null, "sort": "current", "sortDesc": true, "total": false, @@ -723,10 +754,11 @@ "links": [], "nullPointMode": "null as zero", "options": { - "dataLinks": [] + "alertThreshold": true }, "paceLength": 10, "percentage": false, + "pluginVersion": "10.3.1", "pointradius": 2, "points": false, "renderer": "flot", @@ -736,6 +768,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(argocd_app_info{namespace=~\"$namespace\",dest_server=~\"$cluster\",health_status=~\"$health_status\",sync_status=~\"$sync_status\",health_status!=\"\"}) by (sync_status)", "format": "time_series", "instant": false, @@ -745,9 +780,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Sync Status", "tooltip": { "shared": true, @@ -756,42 +789,43 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 2, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } } ], + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "refId": "A" + } + ], "title": "Application Status", "type": "row" }, { "collapsed": true, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "gridPos": { "h": 1, "w": 24, @@ -805,8 +839,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", - "decimals": null, + "datasource": { + "uid": "$datasource" + }, "fill": 1, "fillGradient": 0, "gridPos": { @@ -851,6 +886,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(round(increase(argocd_app_sync_total{namespace=~\"$namespace\",dest_server=~\"$cluster\"}[$interval]))) by ($grouping)", "format": "time_series", "intervalFactor": 1, @@ -859,9 +897,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Sync Activity", "tooltip": { "shared": true, @@ -870,9 +906,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -882,7 +916,6 @@ "format": "short", "label": "", "logBase": 1, - "max": null, "min": "0", "show": true }, @@ -891,14 +924,11 @@ "format": "short", "label": "", "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -906,8 +936,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", - "decimals": null, + "datasource": { + "uid": "$datasource" + }, "fill": 1, "fillGradient": 0, "gridPos": { @@ -951,6 +982,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(round(increase(argocd_app_sync_total{namespace=~\"$namespace\",phase=~\"Error|Failed\",dest_server=~\"$cluster\"}[$interval]))) by ($grouping, phase)", "format": "time_series", "intervalFactor": 1, @@ -959,9 +993,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Sync Failures", "tooltip": { "shared": true, @@ -970,9 +1002,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -982,7 +1012,6 @@ "format": "none", "label": "", "logBase": 1, - "max": null, "min": "0", "show": true }, @@ -990,23 +1019,30 @@ "format": "short", "label": "", "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } } ], + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "refId": "A" + } + ], "title": "Sync Stats", "type": "row" }, { "collapsed": true, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "gridPos": { "h": 1, "w": 24, @@ -1020,7 +1056,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "fillGradient": 0, "gridPos": { @@ -1062,6 +1100,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(argocd_app_reconcile_count{namespace=~\"$namespace\",dest_server=~\"$cluster\"}[$interval])) by ($grouping)", "format": "time_series", "intervalFactor": 1, @@ -1070,9 +1111,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Reconciliation Activity", "tooltip": { "shared": false, @@ -1081,50 +1120,39 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { - "cards": { - "cardPadding": null, - "cardRound": null - }, + "cards": {}, "color": { "cardColor": "#b4ff00", "colorScale": "sqrt", "colorScheme": "interpolateSpectral", "exponent": 0.5, - "min": null, "mode": "spectrum" }, "dataFormat": "tsbuckets", - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "gridPos": { "h": 7, "w": 24, @@ -1143,6 +1171,9 @@ "reverseYBuckets": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(argocd_app_reconcile_bucket{namespace=~\"$namespace\"}[$interval])) by (le)", "format": "heatmap", "instant": false, @@ -1151,8 +1182,6 @@ "refId": "A" } ], - "timeFrom": null, - "timeShift": null, "title": "Reconciliation Performance", "tooltip": { "show": true, @@ -1163,27 +1192,21 @@ "xAxis": { "show": true }, - "xBucketNumber": null, - "xBucketSize": null, "yAxis": { - "decimals": null, "format": "short", "logBase": 1, - "max": null, - "min": null, - "show": true, - "splitFactor": null + "show": true }, - "yBucketBound": "auto", - "yBucketNumber": null, - "yBucketSize": null + "yBucketBound": "auto" }, { "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "fillGradient": 0, "gridPos": { @@ -1204,7 +1227,6 @@ "min": false, "rightSide": true, "show": true, - "sideWidth": null, "sort": "current", "sortDesc": true, "total": false, @@ -1215,11 +1237,315 @@ "links": [], "nullPointMode": "null as zero", "options": { - "dataLinks": [] + "dataLinks": [] + }, + "paceLength": 10, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(increase(argocd_app_k8s_request_total{namespace=~\"$namespace\",server=~\"$cluster\"}[$interval])) by (verb, resource_kind)", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "legendFormat": "{{verb}} {{resource_kind}}", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "K8s API Activity", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 31 + }, + "hiddenSeries": false, + "id": 96, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideZero": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(workqueue_depth{namespace=~\"$namespace\",name=~\"app_.*\"}) by (name)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Workqueue Depth", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "min": "0", + "show": true + }, + { + "format": "short", + "logBase": 1, + "min": "0", + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 31 + }, + "hiddenSeries": false, + "id": 98, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "hideZero": false, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(argocd_kubectl_exec_pending{namespace=~\"$namespace\"}) by (command)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{command}}", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Pending kubectl run", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": "", + "logBase": 1, + "min": "0", + "show": true + }, + { + "decimals": 0, + "format": "short", + "label": "", + "logBase": 1, + "min": "0", + "show": true + } + ], + "yaxis": { + "align": false + } + } + ], + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "refId": "A" + } + ], + "title": "Controller Stats", + "type": "row" + }, + { + "collapsed": true, + "datasource": { + "uid": "$datasource" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 102, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "unitScale": true + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 34, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "options": { + "alertThreshold": true }, "paceLength": 10, "percentage": false, - "pointradius": 2, + "pluginVersion": "10.3.1", + "pointradius": 5, "points": false, "renderer": "flot", "seriesOverrides": [], @@ -1228,53 +1554,44 @@ "steppedLine": false, "targets": [ { - "expr": "sum(increase(argocd_app_k8s_request_total{namespace=~\"$namespace\",server=~\"$cluster\"}[$interval])) by (verb, resource_kind)", + "datasource": { + "uid": "$datasource" + }, + "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-server-metrics\",namespace=~\"$namespace\"}", "format": "time_series", - "instant": false, "intervalFactor": 1, - "legendFormat": "{{verb}} {{resource_kind}}", + "legendFormat": "{{namespace}}", "refId": "A" } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, - "title": "K8s API Activity", + "title": "Memory Usage", "tooltip": { - "shared": true, + "shared": false, "sort": 2, "value_type": "individual" }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { - "format": "short", - "label": null, + "format": "bytes", "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -1282,39 +1599,52 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "unitScale": true + }, + "overrides": [] + }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, - "w": 12, + "w": 24, "x": 0, - "y": 31 + "y": 16 }, "hiddenSeries": false, - "id": 96, + "id": 108, "legend": { "alignAsTable": true, "avg": true, "current": true, + "hideEmpty": true, "hideZero": true, "max": true, "min": false, - "rightSide": false, + "rightSide": true, "show": true, - "sideWidth": null, + "sort": "avg", + "sortDesc": true, "total": false, "values": true }, "lines": true, "linewidth": 1, "links": [], - "nullPointMode": "null", + "nullPointMode": "connected", "options": { - "dataLinks": [] + "alertThreshold": true }, + "paceLength": 10, "percentage": false, - "pointradius": 2, + "pluginVersion": "10.3.1", + "pointradius": 5, "points": false, "renderer": "flot", "seriesOverrides": [], @@ -1323,52 +1653,45 @@ "steppedLine": false, "targets": [ { - "expr": "sum(workqueue_depth{namespace=~\"$namespace\",name=~\"app_.*\"}) by (name)", + "datasource": { + "uid": "$datasource" + }, + "expr": "irate(process_cpu_seconds_total{job=\"argocd-server-metrics\",namespace=~\"$namespace\"}[1m])", "format": "time_series", "intervalFactor": 1, - "legendFormat": "{{name}}", + "legendFormat": "{{namespace}}", "refId": "A" } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, - "title": "Workqueue Depth", + "title": "CPU Usage", "tooltip": { - "shared": true, + "shared": false, "sort": 2, "value_type": "individual" }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { - "format": "short", - "label": null, + "decimals": 1, + "format": "none", "logBase": 1, - "max": null, - "min": "0", "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": "0", "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -1376,26 +1699,38 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", - "decimals": null, + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "unitScale": true + }, + "overrides": [] + }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, - "w": 12, - "x": 12, - "y": 31 + "w": 24, + "x": 0, + "y": 23 }, "hiddenSeries": false, - "id": 98, + "id": 62, "legend": { "alignAsTable": true, "avg": true, "current": true, + "hideEmpty": false, "hideZero": false, "max": true, "min": false, + "rightSide": true, "show": true, + "sort": "current", + "sortDesc": true, "total": false, "values": true }, @@ -1404,10 +1739,12 @@ "links": [], "nullPointMode": "null", "options": { - "dataLinks": [] + "alertThreshold": true }, + "paceLength": 10, "percentage": false, - "pointradius": 2, + "pluginVersion": "10.3.1", + "pointradius": 5, "points": false, "renderer": "flot", "seriesOverrides": [], @@ -1416,63 +1753,64 @@ "steppedLine": false, "targets": [ { - "expr": "sum(argocd_kubectl_exec_pending{namespace=~\"$namespace\"}) by (command)", + "datasource": { + "uid": "$datasource" + }, + "expr": "go_goroutines{job=\"argocd-server-metrics\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, - "legendFormat": "{{command}}", + "legendFormat": "{{namespace}}", "refId": "A" } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, - "title": "Pending kubectl run", + "title": "Goroutines", "tooltip": { - "shared": true, + "shared": false, "sort": 2, "value_type": "individual" }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { - "decimals": 0, "format": "short", - "label": "", "logBase": 1, - "max": null, - "min": "0", "show": true }, { - "decimals": 0, "format": "short", - "label": "", "logBase": 1, - "max": null, - "min": "0", "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } } ], - "title": "Controller Stats", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "refId": "A" + } + ], + "title": "Controller Telemetry", "type": "row" }, + { "collapsed": true, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "gridPos": { "h": 1, "w": 24, @@ -1486,14 +1824,23 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "unitScale": true + }, + "overrides": [] + }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, "w": 24, "x": 0, - "y": 26 + "y": 9 }, "hiddenSeries": false, "id": 34, @@ -1515,10 +1862,11 @@ "links": [], "nullPointMode": "connected", "options": { - "dataLinks": [] + "alertThreshold": true }, "paceLength": 10, "percentage": false, + "pluginVersion": "10.3.1", "pointradius": 5, "points": false, "renderer": "flot", @@ -1528,7 +1876,10 @@ "steppedLine": false, "targets": [ { - "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-metrics\",namespace=~\"$namespace\"}", + "datasource": { + "uid": "$datasource" + }, + "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-applicationset-controller-metrics\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{namespace}}", @@ -1536,9 +1887,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Memory Usage", "tooltip": { "shared": false, @@ -1547,33 +1896,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "bytes", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -1581,14 +1921,23 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "unitScale": true + }, + "overrides": [] + }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, "w": 24, "x": 0, - "y": 33 + "y": 16 }, "hiddenSeries": false, "id": 108, @@ -1612,10 +1961,11 @@ "links": [], "nullPointMode": "connected", "options": { - "dataLinks": [] + "alertThreshold": true }, "paceLength": 10, "percentage": false, + "pluginVersion": "10.3.1", "pointradius": 5, "points": false, "renderer": "flot", @@ -1625,7 +1975,10 @@ "steppedLine": false, "targets": [ { - "expr": "irate(process_cpu_seconds_total{job=\"argocd-metrics\",namespace=~\"$namespace\"}[1m])", + "datasource": { + "uid": "$datasource" + }, + "expr": "irate(process_cpu_seconds_total{job=\"argocd-applicationset-controller-metrics\",namespace=~\"$namespace\"}[1m])", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{namespace}}", @@ -1633,9 +1986,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "CPU Usage", "tooltip": { "shared": false, @@ -1644,9 +1995,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -1654,24 +2003,17 @@ { "decimals": 1, "format": "none", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -1679,14 +2021,23 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "unitScale": true + }, + "overrides": [] + }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, "w": 24, "x": 0, - "y": 40 + "y": 23 }, "hiddenSeries": false, "id": 62, @@ -1710,10 +2061,11 @@ "links": [], "nullPointMode": "null", "options": { - "dataLinks": [] + "alertThreshold": true }, "paceLength": 10, "percentage": false, + "pluginVersion": "10.3.1", "pointradius": 5, "points": false, "renderer": "flot", @@ -1723,7 +2075,10 @@ "steppedLine": false, "targets": [ { - "expr": "go_goroutines{job=\"argocd-metrics\",namespace=~\"$namespace\"}", + "datasource": { + "uid": "$datasource" + }, + "expr": "go_goroutines{job=\"argocd-applicationset-controller-metrics\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{namespace}}", @@ -1731,9 +2086,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Goroutines", "tooltip": { "shared": false, @@ -1742,42 +2095,43 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } } ], - "title": "Controller Telemetry", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "refId": "A" + } + ], + "title": "AppSet Controller Telemetry", "type": "row" }, { "collapsed": true, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "gridPos": { "h": 1, "w": 24, @@ -1791,7 +2145,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "fillGradient": 0, "gridPos": { @@ -1832,6 +2188,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(argocd_cluster_api_resource_objects{namespace=~\"$namespace\",server=~\"$cluster\"}) by (server)", "format": "time_series", "intervalFactor": 1, @@ -1840,9 +2199,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Resource Objects Count", "tooltip": { "shared": false, @@ -1851,33 +2208,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -1885,7 +2233,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "fillGradient": 0, "gridPos": { @@ -1927,6 +2277,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": " sum(argocd_cluster_api_resources{namespace=~\"$namespace\",server=~\"$cluster\"}) by (server)", "format": "time_series", "intervalFactor": 1, @@ -1935,9 +2288,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "API Resources Count", "tooltip": { "shared": false, @@ -1946,33 +2297,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -1980,7 +2322,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "fillGradient": 0, "gridPos": { @@ -2021,6 +2365,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(argocd_cluster_events_total{namespace=~\"$namespace\",server=~\"$cluster\"}[$interval])) by (server)", "format": "time_series", "intervalFactor": 1, @@ -2029,9 +2376,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Cluster Events Count", "tooltip": { "shared": false, @@ -2040,42 +2385,43 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } } ], + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "refId": "A" + } + ], "title": "Cluster Stats", "type": "row" }, { "collapsed": true, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "gridPos": { "h": 1, "w": 24, @@ -2089,14 +2435,23 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "unitScale": true + }, + "overrides": [] + }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 7 + "y": 11 }, "hiddenSeries": false, "id": 82, @@ -2114,9 +2469,10 @@ "links": [], "nullPointMode": "null", "options": { - "dataLinks": [] + "alertThreshold": true }, "percentage": false, + "pluginVersion": "10.3.1", "pointradius": 2, "points": false, "renderer": "flot", @@ -2126,6 +2482,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(argocd_git_request_total{request_type=\"ls-remote\", namespace=~\"$namespace\"}[10m])) by (namespace)", "format": "time_series", "intervalFactor": 1, @@ -2134,9 +2493,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Git Requests (ls-remote)", "tooltip": { "shared": true, @@ -2145,33 +2502,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -2179,14 +2527,23 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "unitScale": true + }, + "overrides": [] + }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 12, "x": 12, - "y": 7 + "y": 11 }, "hiddenSeries": false, "id": 84, @@ -2204,9 +2561,10 @@ "links": [], "nullPointMode": "null", "options": { - "dataLinks": [] + "alertThreshold": true }, "percentage": false, + "pluginVersion": "10.3.1", "pointradius": 2, "points": false, "renderer": "flot", @@ -2216,6 +2574,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(argocd_git_request_total{request_type=\"fetch\", namespace=~\"$namespace\"}[10m])) by (namespace)", "format": "time_series", "intervalFactor": 1, @@ -2224,9 +2585,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Git Requests (checkout)", "tooltip": { "shared": true, @@ -2235,9 +2594,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -2246,29 +2603,20 @@ "format": "short", "label": "", "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { - "cards": { - "cardPadding": null, - "cardRound": null - }, + "cards": {}, "color": { "cardColor": "#b4ff00", "colorScale": "sqrt", @@ -2277,12 +2625,30 @@ "mode": "spectrum" }, "dataFormat": "tsbuckets", - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + }, + "unitScale": true + }, + "overrides": [] + }, "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 15 + "y": 19 }, "heatmap": {}, "hideZeroBuckets": false, @@ -2291,10 +2657,51 @@ "legend": { "show": false }, - "options": {}, + "options": { + "calculate": false, + "calculation": {}, + "cellGap": 2, + "cellValues": {}, + "color": { + "exponent": 0.5, + "fill": "#b4ff00", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Spectral", + "steps": 128 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": false + }, + "rowsFrame": { + "layout": "auto" + }, + "showValue": "never", + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false, + "unit": "short" + } + }, + "pluginVersion": "10.3.1", "reverseYBuckets": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(argocd_git_request_duration_seconds_bucket{request_type=\"fetch\", namespace=~\"$namespace\"}[$interval])) by (le)", "format": "heatmap", "intervalFactor": 10, @@ -2302,8 +2709,6 @@ "refId": "A" } ], - "timeFrom": null, - "timeShift": null, "title": "Git Fetch Performance", "tooltip": { "show": true, @@ -2313,26 +2718,15 @@ "xAxis": { "show": true }, - "xBucketNumber": null, - "xBucketSize": null, "yAxis": { - "decimals": null, "format": "short", "logBase": 1, - "max": null, - "min": null, - "show": true, - "splitFactor": null + "show": true }, - "yBucketBound": "auto", - "yBucketNumber": null, - "yBucketSize": null + "yBucketBound": "auto" }, { - "cards": { - "cardPadding": null, - "cardRound": null - }, + "cards": {}, "color": { "cardColor": "#b4ff00", "colorScale": "sqrt", @@ -2341,12 +2735,30 @@ "mode": "spectrum" }, "dataFormat": "tsbuckets", - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + }, + "unitScale": true + }, + "overrides": [] + }, "gridPos": { "h": 8, "w": 12, "x": 12, - "y": 15 + "y": 19 }, "heatmap": {}, "hideZeroBuckets": false, @@ -2355,10 +2767,51 @@ "legend": { "show": false }, - "options": {}, + "options": { + "calculate": false, + "calculation": {}, + "cellGap": 2, + "cellValues": {}, + "color": { + "exponent": 0.5, + "fill": "#b4ff00", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Spectral", + "steps": 128 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": false + }, + "rowsFrame": { + "layout": "auto" + }, + "showValue": "never", + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false, + "unit": "short" + } + }, + "pluginVersion": "10.3.1", "reverseYBuckets": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(argocd_git_request_duration_seconds_bucket{request_type=\"ls-remote\", namespace=~\"$namespace\"}[$interval])) by (le)", "format": "heatmap", "intervalFactor": 10, @@ -2366,8 +2819,6 @@ "refId": "A" } ], - "timeFrom": null, - "timeShift": null, "title": "Git Ls-Remote Performance", "tooltip": { "show": true, @@ -2377,34 +2828,28 @@ "xAxis": { "show": true }, - "xBucketNumber": null, - "xBucketSize": null, "yAxis": { - "decimals": null, "format": "short", "logBase": 1, - "max": null, - "min": null, - "show": true, - "splitFactor": null + "show": true }, - "yBucketBound": "auto", - "yBucketNumber": null, - "yBucketSize": null + "yBucketBound": "auto" }, { "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 24, "x": 0, - "y": 23 + "y": 27 }, "hiddenSeries": false, "id": 71, @@ -2435,7 +2880,10 @@ "steppedLine": false, "targets": [ { - "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-repo-server\",namespace=~\"$namespace\"}", + "datasource": { + "uid": "$datasource" + }, + "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-repo-server-metrics\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{pod}}", @@ -2443,9 +2891,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Memory Used", "tooltip": { "shared": true, @@ -2454,33 +2900,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "bytes", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -2488,14 +2925,16 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, "w": 24, "x": 0, - "y": 31 + "y": 35 }, "hiddenSeries": false, "id": 72, @@ -2526,7 +2965,10 @@ "steppedLine": false, "targets": [ { - "expr": "go_goroutines{job=\"argocd-repo-server\",namespace=~\"$namespace\"}", + "datasource": { + "uid": "$datasource" + }, + "expr": "go_goroutines{job=\"argocd-repo-server-metrics\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{pod}}", @@ -2534,9 +2976,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Goroutines", "tooltip": { "shared": true, @@ -2545,42 +2985,43 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } } ], + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "refId": "A" + } + ], "title": "Repo Server Stats", "type": "row" }, { "collapsed": true, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "gridPos": { "h": 1, "w": 24, @@ -2594,14 +3035,24 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "unitScale": true + }, + "overrides": [] + }, "fill": 1, + "fillGradient": 0, "gridPos": { "h": 8, "w": 24, "x": 0, - "y": 89 + "y": 12 }, + "hiddenSeries": false, "id": 61, "legend": { "avg": false, @@ -2616,8 +3067,12 @@ "linewidth": 1, "links": [], "nullPointMode": "connected", + "options": { + "alertThreshold": true + }, "paceLength": 10, "percentage": false, + "pluginVersion": "10.3.1", "pointradius": 5, "points": false, "renderer": "flot", @@ -2627,7 +3082,10 @@ "steppedLine": false, "targets": [ { - "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-server-metrics\",namespace=~\"$namespace\"}", + "datasource": { + "uid": "$datasource" + }, + "expr": "go_memstats_heap_alloc_bytes{job=\"argocd-repo-server-metrics\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{pod}}", @@ -2635,9 +3093,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Memory Used", "tooltip": { "shared": true, @@ -2646,33 +3102,25 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "bytes", - "label": null, "logBase": 1, - "max": null, "min": "0", "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -2680,14 +3128,24 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "unitScale": true + }, + "overrides": [] + }, "fill": 1, + "fillGradient": 0, "gridPos": { "h": 9, "w": 24, "x": 0, - "y": 97 + "y": 20 }, + "hiddenSeries": false, "id": 36, "legend": { "avg": false, @@ -2702,8 +3160,12 @@ "linewidth": 1, "links": [], "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "paceLength": 10, "percentage": false, + "pluginVersion": "10.3.1", "pointradius": 5, "points": false, "renderer": "flot", @@ -2713,7 +3175,10 @@ "steppedLine": false, "targets": [ { - "expr": "go_goroutines{job=\"argocd-server-metrics\",namespace=~\"$namespace\"}", + "datasource": { + "uid": "$datasource" + }, + "expr": "go_goroutines{job=\"argocd-repo-server-metrics\",namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{pod}}", @@ -2721,9 +3186,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Goroutines", "tooltip": { "shared": true, @@ -2732,33 +3195,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -2766,14 +3220,24 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "unitScale": true + }, + "overrides": [] + }, "fill": 1, + "fillGradient": 0, "gridPos": { "h": 9, "w": 24, "x": 0, - "y": 106 + "y": 29 }, + "hiddenSeries": false, "id": 38, "legend": { "avg": false, @@ -2788,8 +3252,12 @@ "linewidth": 1, "links": [], "nullPointMode": "connected", + "options": { + "alertThreshold": true + }, "paceLength": 10, "percentage": false, + "pluginVersion": "10.3.1", "pointradius": 5, "points": false, "renderer": "flot", @@ -2799,7 +3267,10 @@ "steppedLine": false, "targets": [ { - "expr": "go_gc_duration_seconds{job=\"argocd-server-metrics\", quantile=\"1\", namespace=~\"$namespace\"}", + "datasource": { + "uid": "$datasource" + }, + "expr": "go_gc_duration_seconds{job=\"argocd-repo-server-metrics\", quantile=\"1\", namespace=~\"$namespace\"}", "format": "time_series", "intervalFactor": 2, "legendFormat": "{{pod}}", @@ -2807,9 +3278,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "GC Time Quantiles", "tooltip": { "shared": true, @@ -2818,33 +3287,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -2853,12 +3313,11 @@ "h": 2, "w": 24, "x": 0, - "y": 115 + "y": 38 }, "id": 54, "links": [], "mode": "markdown", - "title": "", "transparent": true, "type": "text" }, @@ -2867,14 +3326,15 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", - "decimals": null, + "datasource": { + "uid": "$datasource" + }, "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 0, - "y": 117 + "y": 40 }, "id": 40, "legend": { @@ -2907,6 +3367,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(grpc_server_handled_total{job=\"argocd-server-metrics\",grpc_service=\"application.ApplicationService\",namespace=~\"$namespace\"}[$interval])) by (grpc_code, grpc_method)", "format": "time_series", "intervalFactor": 1, @@ -2915,9 +3378,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "ApplicationService Requests", "tooltip": { "shared": false, @@ -2926,33 +3387,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -2960,13 +3412,15 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 12, - "y": 117 + "y": 40 }, "id": 42, "legend": { @@ -2997,6 +3451,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(grpc_server_handled_total{job=\"argocd-server-metrics\",grpc_service=\"cluster.ClusterService\",namespace=~\"$namespace\"}[$interval])) by (grpc_code, grpc_method)", "format": "time_series", "intervalFactor": 1, @@ -3005,9 +3462,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "ClusterService Requests", "tooltip": { "shared": false, @@ -3016,33 +3471,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -3050,13 +3496,15 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 0, - "y": 126 + "y": 49 }, "id": 44, "legend": { @@ -3087,6 +3535,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(grpc_server_handled_total{job=\"argocd-server-metrics\",grpc_service=\"project.ProjectService\",namespace=~\"$namespace\"}[$interval])) by (grpc_code, grpc_method)", "format": "time_series", "intervalFactor": 1, @@ -3095,9 +3546,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "ProjectService Requests", "tooltip": { "shared": true, @@ -3106,33 +3555,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -3140,13 +3580,15 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 12, - "y": 126 + "y": 49 }, "id": 46, "legend": { @@ -3176,6 +3618,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(grpc_server_handled_total{job=\"argocd-server-metrics\",grpc_service=\"repository.RepositoryService\",namespace=~\"$namespace\"}[$interval])) by (grpc_code, grpc_method)", "format": "time_series", "intervalFactor": 1, @@ -3192,9 +3637,7 @@ "yaxis": "left" } ], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "RepositoryService Requests", "tooltip": { "shared": true, @@ -3203,33 +3646,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -3237,13 +3671,15 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 0, - "y": 135 + "y": 58 }, "id": 48, "legend": { @@ -3273,6 +3709,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(grpc_server_handled_total{job=\"argocd-server-metrics\",grpc_service=\"session.SessionService\",namespace=~\"$namespace\"}[$interval])) by (grpc_code, grpc_method)", "format": "time_series", "intervalFactor": 1, @@ -3281,9 +3720,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "SessionService Requests", "tooltip": { "shared": true, @@ -3292,33 +3729,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -3326,13 +3754,15 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 12, - "y": 135 + "y": 58 }, "id": 49, "legend": { @@ -3362,6 +3792,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(grpc_server_handled_total{job=\"argocd-server-metrics\",grpc_service=\"version.VersionService\",namespace=~\"$namespace\"}[$interval])) by (grpc_code, grpc_method)", "format": "time_series", "intervalFactor": 1, @@ -3370,9 +3803,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "VersionService Requests", "tooltip": { "shared": true, @@ -3381,33 +3812,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -3415,13 +3837,15 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 0, - "y": 144 + "y": 67 }, "id": 50, "legend": { @@ -3451,6 +3875,9 @@ "steppedLine": false, "targets": [ { + "datasource": { + "uid": "$datasource" + }, "expr": "sum(increase(grpc_server_handled_total{job=\"argocd-server-metrics\",grpc_service=\"account.AccountService\",namespace=~\"$namespace\"}[$interval])) by (grpc_code, grpc_method)", "format": "time_series", "intervalFactor": 1, @@ -3459,9 +3886,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "AccountService Requests", "tooltip": { "shared": true, @@ -3470,33 +3895,24 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -3504,13 +3920,15 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "$datasource", + "datasource": { + "uid": "$datasource" + }, "fill": 1, "gridPos": { "h": 9, "w": 12, "x": 12, - "y": 144 + "y": 67 }, "id": 99, "legend": { @@ -3540,7 +3958,10 @@ "steppedLine": false, "targets": [ { - "expr": "sum(increase(grpc_server_handled_total{job=\"argocd-server-metrics\",grpc_service=\"settings.SettingsService\",namespace=~\"$namespace\"}[$interval])) by (grpc_code, grpc_method)", + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(increase(grpc_server_handled_total{job=\"argocd-server-metrics\",grpc_service=\"cluster.SettingsService\",namespace=~\"$namespace\"}[$interval])) by (grpc_code, grpc_method)", "format": "time_series", "intervalFactor": 1, "legendFormat": "{{grpc_code}},{{grpc_method}}", @@ -3548,9 +3969,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "SettingsService Requests", "tooltip": { "shared": true, @@ -3559,42 +3978,44 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } } ], + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "refId": "A" + } + ], "title": "Server Stats", "type": "row" }, { "collapsed": true, - "datasource": null, + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, "gridPos": { "h": 1, "w": 24, @@ -3608,14 +4029,24 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": null, + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "links": [], + "unitScale": true + }, + "overrides": [] + }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 7, "w": 24, "x": 0, - "y": 9 + "y": 13 }, "hiddenSeries": false, "id": 112, @@ -3632,9 +4063,10 @@ "linewidth": 1, "nullPointMode": "null", "options": { - "dataLinks": [] + "alertThreshold": true }, "percentage": false, + "pluginVersion": "10.3.1", "pointradius": 2, "points": false, "renderer": "flot", @@ -3644,14 +4076,16 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, "expr": "sum(increase(argocd_redis_request_total{namespace=~\"$namespace\"}[$interval])) by (failed)", "refId": "A" } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Requests by result", "tooltip": { "shared": true, @@ -3660,58 +4094,58 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } } ], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "refId": "A" + } + ], "title": "Redis Stats", "type": "row" } ], - "refresh": false, - "schemaVersion": 21, - "style": "dark", + "refresh": "", + "schemaVersion": 39, "tags": [], "templating": { "list": [ { "current": { + "selected": false, "text": "Prometheus", - "value": "Prometheus" + "value": "prometheus" }, "hide": 0, "includeAll": false, - "label": null, "multi": false, "name": "datasource", "options": [], "query": "prometheus", + "queryValue": "", "refresh": 1, "regex": "", "skipUrlSync": false, @@ -3724,11 +4158,13 @@ "text": "All", "value": "$__all" }, - "datasource": "$datasource", + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, "definition": "label_values(kube_pod_info, namespace)", "hide": 0, "includeAll": true, - "label": null, "multi": false, "name": "namespace", "options": [], @@ -3738,7 +4174,6 @@ "skipUrlSync": false, "sort": 0, "tagValuesQuery": "", - "tags": [], "tagsQuery": "", "type": "query", "useTags": false @@ -3753,7 +4188,6 @@ "value": "$__auto_interval_interval" }, "hide": 0, - "label": null, "name": "interval", "options": [ { @@ -3810,13 +4244,12 @@ { "allValue": "", "current": { - "selected": true, + "selected": false, "text": "namespace", "value": "namespace" }, "hide": 0, "includeAll": false, - "label": null, "multi": false, "name": "grouping", "options": [ @@ -3837,6 +4270,7 @@ } ], "query": "namespace,name,project", + "queryValue": "", "skipUrlSync": false, "type": "custom" }, @@ -3847,11 +4281,13 @@ "text": "All", "value": "$__all" }, - "datasource": "$datasource", + "datasource": { + "type": "prometheus", + "uid": "$datasource" + }, "definition": "label_values(argocd_cluster_info, server)", "hide": 0, "includeAll": true, - "label": null, "multi": false, "name": "cluster", "options": [], @@ -3861,7 +4297,6 @@ "skipUrlSync": false, "sort": 1, "tagValuesQuery": "", - "tags": [], "tagsQuery": "", "type": "query", "useTags": false @@ -3869,13 +4304,12 @@ { "allValue": ".*", "current": { - "selected": true, + "selected": false, "text": "All", "value": "$__all" }, "hide": 0, "includeAll": true, - "label": null, "multi": false, "name": "health_status", "options": [ @@ -3922,13 +4356,12 @@ { "allValue": ".*", "current": { - "selected": true, + "selected": false, "text": "All", "value": "$__all" }, "hide": 0, "includeAll": true, - "label": null, "multi": false, "name": "sync_status", "options": [ @@ -3991,5 +4424,6 @@ "timezone": "", "title": "ArgoCD", "uid": "LCAgc9rWz", - "version": 1 + "version": 2, + "weekStart": "" } \ No newline at end of file diff --git a/examples/k8s-rbac/argocd-server-applications/README.md b/examples/k8s-rbac/argocd-server-applications/README.md new file mode 100644 index 0000000000000..a5fc3553f68fe --- /dev/null +++ b/examples/k8s-rbac/argocd-server-applications/README.md @@ -0,0 +1,11 @@ +This folder contains example RBAC for Kubernetes to allow the Argo CD API +Server (`argocd-server`) to perform CRUD operations on `Application` CRs +in all namespaces on the cluster. + +Applying the `ClusterRole` and `ClusterRoleBinding` grant the Argo CD API +server read and write permissions cluster-wide, which may not be what you +want. Handle with care. + +Only apply these if you have installed Argo CD into the default namespace +`argocd`. Otherwise, you need to edit the cluster role binding to bind to +the service account in the correct namespace. \ No newline at end of file diff --git a/examples/k8s-rbac/argocd-server-applications/argocd-notifications-controller-rbac-clusterrole.yaml b/examples/k8s-rbac/argocd-server-applications/argocd-notifications-controller-rbac-clusterrole.yaml new file mode 100644 index 0000000000000..ecbf6de3efb01 --- /dev/null +++ b/examples/k8s-rbac/argocd-server-applications/argocd-notifications-controller-rbac-clusterrole.yaml @@ -0,0 +1,28 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: argocd-notifications-controller-cluster-apps + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: notifications-controller + name: argocd-notifications-controller-cluster-apps +rules: +- apiGroups: + - "argoproj.io" + resources: + - "applications" + verbs: + - get + - list + - watch + - update + - patch +- apiGroups: + - "" + resources: + - secrets + - configmaps + verbs: + - get + - list + - watch \ No newline at end of file diff --git a/examples/k8s-rbac/argocd-server-applications/argocd-notifications-controller-rbac-clusterrolebinding.yaml b/examples/k8s-rbac/argocd-server-applications/argocd-notifications-controller-rbac-clusterrolebinding.yaml new file mode 100644 index 0000000000000..c28ab688c0716 --- /dev/null +++ b/examples/k8s-rbac/argocd-server-applications/argocd-notifications-controller-rbac-clusterrolebinding.yaml @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: argocd-notifications-controller-cluster-apps + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: notifications-controller + name: argocd-notifications-controller-cluster-apps +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: argocd-notifications-controller-cluster-apps +subjects: +- kind: ServiceAccount + name: argocd-notifications-controller + namespace: argocd diff --git a/examples/k8s-rbac/argocd-server-applications/argocd-server-rbac-clusterrole.yaml b/examples/k8s-rbac/argocd-server-applications/argocd-server-rbac-clusterrole.yaml new file mode 100644 index 0000000000000..a8d6c021c4e2e --- /dev/null +++ b/examples/k8s-rbac/argocd-server-applications/argocd-server-rbac-clusterrole.yaml @@ -0,0 +1,24 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: argocd-server-cluster-apps + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: server + name: argocd-server-cluster-apps +rules: +- apiGroups: + - "" + resources: + - events + verbs: + - create +- apiGroups: + - "argoproj.io" + resources: + - "applications" + verbs: + - create + - delete + - update + - patch diff --git a/examples/k8s-rbac/argocd-server-applications/argocd-server-rbac-clusterrolebinding.yaml b/examples/k8s-rbac/argocd-server-applications/argocd-server-rbac-clusterrolebinding.yaml new file mode 100644 index 0000000000000..1e587a3e40a31 --- /dev/null +++ b/examples/k8s-rbac/argocd-server-applications/argocd-server-rbac-clusterrolebinding.yaml @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: argocd-server-cluster-apps + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: server + name: argocd-server-cluster-apps +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: argocd-server-cluster-apps +subjects: +- kind: ServiceAccount + name: argocd-server + namespace: argocd diff --git a/examples/k8s-rbac/argocd-server-applications/kustomization.yaml b/examples/k8s-rbac/argocd-server-applications/kustomization.yaml new file mode 100644 index 0000000000000..330dde4a914d2 --- /dev/null +++ b/examples/k8s-rbac/argocd-server-applications/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- argocd-server-rbac-clusterrole.yaml +- argocd-server-rbac-clusterrolebinding.yaml +- argocd-notifications-controller-rbac-clusterrole.yaml +- argocd-notifications-controller-rbac-clusterrolebinding.yaml diff --git a/examples/known-hosts/kustomization.yaml b/examples/known-hosts/kustomization.yaml index 4baaf5194f6d0..ab6f0d81fd3ea 100644 --- a/examples/known-hosts/kustomization.yaml +++ b/examples/known-hosts/kustomization.yaml @@ -1,11 +1,9 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -bases: -- github.com/argoproj/argo-cd//manifests/cluster-install?ref=stable - -patchesStrategicMerge: -- argocd-known-hosts-mounts.yaml - resources: +- github.com/argoproj/argo-cd//manifests/cluster-install?ref=stable - argocd-known-hosts.yaml + +patches: +- path: argocd-known-hosts-mounts.yaml diff --git a/examples/plugins/helm/argocd-repo-server-deployment-patch.yaml b/examples/plugins/helm/argocd-repo-server-deployment-patch.yaml index aa50ec2181a9f..e933c8021a610 100644 --- a/examples/plugins/helm/argocd-repo-server-deployment-patch.yaml +++ b/examples/plugins/helm/argocd-repo-server-deployment-patch.yaml @@ -12,9 +12,9 @@ spec: - sh - -c - | - wget https://get.helm.sh/helm-v3.8.2-linux-amd64.tar.gz -O - | tar xz && mv linux-amd64/helm /tools/helm && chmod +x /tools/helm + wget https://get.helm.sh/helm-v3.10.3-linux-amd64.tar.gz -O - | tar xz && mv linux-amd64/helm /tools/helm && chmod +x /tools/helm wget https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 -O /tools/jq && chmod +x /tools/jq - wget https://github.com/mikefarah/yq/releases/download/v4.24.5/yq_linux_amd64 -O /tools/yq && chmod +x /tools/yq + wget https://github.com/mikefarah/yq/releases/download/v4.30.6/yq_linux_amd64 -O /tools/yq && chmod +x /tools/yq volumeMounts: - mountPath: /tools name: helm-plugin-tools @@ -40,7 +40,7 @@ spec: subPath: generate.sh name: helm-plugin-config - mountPath: /var/run/argocd/helm-plugin/get-parameters.sh - subPath: generate.sh + subPath: get-parameters.sh name: helm-plugin-config - mountPath: /usr/local/bin name: helm-plugin-tools diff --git a/examples/plugins/helm/get-parameters.sh b/examples/plugins/helm/get-parameters.sh index d89c29c46656e..5e7823a7c8c72 100755 --- a/examples/plugins/helm/get-parameters.sh +++ b/examples/plugins/helm/get-parameters.sh @@ -1,8 +1,8 @@ #!/bin/sh -yq e -o=json values.yaml | jq '{ +yq e -o=json values.yaml | jq '[{ name: "helm-parameters", title: "Helm Parameters", collectionType: "map", map: [leaf_paths as $path | {"key": $path | join("."), "value": getpath($path)|tostring}] | from_entries -}' +}]' diff --git a/examples/plugins/helm/kustomization.yaml b/examples/plugins/helm/kustomization.yaml index 8b6e41b71c294..78da0e841633c 100644 --- a/examples/plugins/helm/kustomization.yaml +++ b/examples/plugins/helm/kustomization.yaml @@ -1,4 +1,4 @@ -bases: +resources: - ../../../manifests/base configMapGenerator: @@ -11,5 +11,5 @@ configMapGenerator: generatorOptions: disableNameSuffixHash: true -patchesStrategicMerge: - - argocd-repo-server-deployment-patch.yaml +patches: + - path: argocd-repo-server-deployment-patch.yaml diff --git a/go.mod b/go.mod index f26a3f505bf6e..dfa17e1ce0d7d 100644 --- a/go.mod +++ b/go.mod @@ -1,240 +1,277 @@ module github.com/argoproj/argo-cd/v2 -go 1.18 +go 1.21 + +toolchain go1.21.0 require ( code.gitea.io/sdk/gitea v0.15.1 + github.com/Azure/kubelogin v0.0.20 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible - github.com/Masterminds/semver/v3 v3.2.0 + github.com/Masterminds/semver/v3 v3.2.1 + github.com/Masterminds/sprig/v3 v3.2.3 github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d - github.com/alicebob/miniredis/v2 v2.23.1 - github.com/argoproj/gitops-engine v0.7.1-0.20221208230615-917f5a0f16d5 - github.com/argoproj/notifications-engine v0.3.1-0.20221203221941-490d98afd1d6 - github.com/argoproj/pkg v0.13.7-0.20221115212233-27bd8ce31415 - github.com/aws/aws-sdk-go v1.44.164 + github.com/alicebob/miniredis/v2 v2.30.4 + github.com/antonmedv/expr v1.15.2 + github.com/argoproj/gitops-engine v0.7.1-0.20240124052710-5fd9f449e757 + github.com/argoproj/notifications-engine v0.4.1-0.20240206192038-2daee6022f41 + github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1 + github.com/aws/aws-sdk-go v1.50.8 + github.com/bmatcuk/doublestar/v4 v4.6.0 github.com/bombsimon/logrusr/v2 v2.0.1 - github.com/bradleyfalzon/ghinstallation/v2 v2.1.0 - github.com/casbin/casbin/v2 v2.60.0 - github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect - github.com/dustin/go-humanize v1.0.0 - github.com/evanphx/json-patch v5.6.0+incompatible + github.com/bradleyfalzon/ghinstallation/v2 v2.6.0 + github.com/casbin/casbin/v2 v2.77.2 + github.com/cespare/xxhash/v2 v2.2.0 + github.com/coreos/go-oidc/v3 v3.6.0 + github.com/cyphar/filepath-securejoin v0.2.4 + github.com/dustin/go-humanize v1.0.1 + github.com/evanphx/json-patch v5.9.0+incompatible + github.com/felixge/httpsnoop v1.0.3 github.com/fsnotify/fsnotify v1.6.0 - github.com/ghodss/yaml v1.0.0 - github.com/go-git/go-git/v5 v5.4.2 - github.com/go-logr/logr v1.2.3 + github.com/gfleury/go-bitbucket-v1 v0.0.0-20220301131131-8e7ed04b843e + github.com/go-git/go-git/v5 v5.11.0 + github.com/go-jose/go-jose/v3 v3.0.3 + github.com/go-logr/logr v1.3.0 github.com/go-openapi/loads v0.21.2 - github.com/go-openapi/runtime v0.25.0 - github.com/go-openapi/spec v0.20.6 // indirect - github.com/go-openapi/validate v0.21.0 // indirect - github.com/go-redis/cache/v8 v8.4.2 - github.com/go-redis/redis/v8 v8.11.5 + github.com/go-openapi/runtime v0.26.0 + github.com/go-playground/webhooks/v6 v6.3.0 + github.com/go-redis/cache/v9 v9.0.0 github.com/gobwas/glob v0.2.3 - github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2 + github.com/gogits/go-gogs-client v0.0.0-20200905025246-8bb8a50cb355 github.com/gogo/protobuf v1.3.2 - github.com/golang-jwt/jwt/v4 v4.4.3 - github.com/golang/protobuf v1.5.2 - github.com/google/go-cmp v0.5.9 + github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/golang/protobuf v1.5.3 + github.com/google/go-cmp v0.6.0 github.com/google/go-github/v35 v35.3.0 - github.com/google/go-jsonnet v0.19.1 + github.com/google/go-jsonnet v0.20.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.3.1 github.com/gorilla/handlers v1.5.1 - github.com/gorilla/mux v1.8.0 - github.com/gorilla/websocket v1.4.2 - github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 + github.com/gorilla/websocket v1.5.0 + github.com/gosimple/slug v1.13.1 + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 - github.com/hashicorp/go-retryablehttp v0.7.0 - github.com/imdario/mergo v0.3.13 - github.com/improbable-eng/grpc-web v0.0.0-20181111100011-16092bd1d58a - github.com/itchyny/gojq v0.12.10 + github.com/hashicorp/go-retryablehttp v0.7.4 + github.com/imdario/mergo v0.3.16 + github.com/improbable-eng/grpc-web v0.15.0 + github.com/itchyny/gojq v0.12.13 github.com/jeremywohl/flatten v1.0.1 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 - github.com/ktrysmt/go-bitbucket v0.9.55 - github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8 // indirect - github.com/mattn/go-isatty v0.0.16 + github.com/ktrysmt/go-bitbucket v0.9.67 + github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-zglob v0.0.4 + github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 github.com/olekukonko/tablewriter v0.0.5 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.14.0 + github.com/prometheus/client_golang v1.16.0 github.com/r3labs/diff v1.1.0 - github.com/rs/cors v1.8.0 // indirect - github.com/sirupsen/logrus v1.9.0 + github.com/redis/go-redis/v9 v9.0.5 + github.com/robfig/cron/v3 v3.0.1 + github.com/sirupsen/logrus v1.9.3 github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c github.com/soheilhy/cmux v0.1.5 - github.com/spf13/cobra v1.6.1 + github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.4 github.com/valyala/fasttemplate v1.2.2 - github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0 - github.com/xanzy/go-gitlab v0.60.0 - github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 - golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa - golang.org/x/net v0.4.0 // indirect - golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 - golang.org/x/term v0.3.0 - google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90 - google.golang.org/grpc v1.51.0 - google.golang.org/protobuf v1.28.1 - gopkg.in/go-playground/webhooks.v5 v5.17.0 + github.com/whilp/git-urls v1.0.0 + github.com/xanzy/go-gitlab v0.91.1 + github.com/yuin/gopher-lua v1.1.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 + go.opentelemetry.io/otel v1.21.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 + go.opentelemetry.io/otel/sdk v1.21.0 + golang.org/x/crypto v0.19.0 + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 + golang.org/x/oauth2 v0.11.0 + golang.org/x/sync v0.3.0 + golang.org/x/term v0.17.0 + google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d + google.golang.org/grpc v1.59.0 + google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.24.2 - k8s.io/apiextensions-apiserver v0.24.2 - k8s.io/apimachinery v0.24.2 - k8s.io/client-go v0.24.2 - k8s.io/code-generator v0.24.2 - k8s.io/klog/v2 v2.70.1 - k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 - k8s.io/kubectl v0.24.2 - k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 + gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.26.11 + k8s.io/apiextensions-apiserver v0.26.10 + k8s.io/apimachinery v0.26.11 + k8s.io/apiserver v0.26.11 + k8s.io/client-go v0.26.11 + k8s.io/code-generator v0.26.11 + k8s.io/klog/v2 v2.100.1 + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f + k8s.io/kubectl v0.26.4 + k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 layeh.com/gopher-json v0.0.0-20190114024228-97fed8db8427 - sigs.k8s.io/controller-runtime v0.11.0 - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 + oras.land/oras-go/v2 v2.3.0 + sigs.k8s.io/controller-runtime v0.14.7 + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 sigs.k8s.io/yaml v1.3.0 ) require ( - github.com/gfleury/go-bitbucket-v1 v0.0.0-20220301131131-8e7ed04b843e - github.com/stretchr/objx v0.5.0 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect - k8s.io/apiserver v0.24.2 + dario.cat/mergo v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2 // indirect + github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect + github.com/aws/aws-sdk-go-v2/config v1.25.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sqs v1.29.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect + github.com/aws/smithy-go v1.19.0 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/google/s2a-go v0.1.4 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect + github.com/tidwall/gjson v1.14.4 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + go.opencensus.io v0.24.0 // indirect + google.golang.org/api v0.132.0 // indirect + google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + gopkg.in/retry.v1 v1.0.3 // indirect + k8s.io/klog v1.0.0 // indirect + nhooyr.io/websocket v1.8.7 // indirect ) require ( - github.com/Masterminds/sprig/v3 v3.2.2 - github.com/antonmedv/expr v1.9.0 - github.com/coreos/go-oidc/v3 v3.4.0 - github.com/gosimple/slug v1.13.1 - github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 - github.com/robfig/cron/v3 v3.0.1 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.31.0 - go.opentelemetry.io/otel v1.11.1 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1 - go.opentelemetry.io/otel/sdk v1.11.1 -) - -require ( - github.com/emicklei/go-restful/v3 v3.8.0 // indirect - github.com/google/go-github/v45 v45.2.0 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/shopspring/decimal v1.2.0 // indirect - github.com/spf13/cast v1.4.1 // indirect -) - -require ( - cloud.google.com/go/compute v1.7.0 // indirect + cloud.google.com/go/compute v1.23.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.18 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect + github.com/Azure/go-autorest/autorest v0.11.27 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect + github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver v1.5.0 // indirect - github.com/Masterminds/sprig v2.22.0+incompatible // indirect - github.com/Microsoft/go-winio v0.4.17 // indirect - github.com/PagerDuty/go-pagerduty v1.6.0 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/PagerDuty/go-pagerduty v1.7.0 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20210112200207-10ab4d695d60 // indirect - github.com/acomagu/bufpipe v1.0.3 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/cloudflare/circl v1.3.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/docker/distribution v2.8.1+incompatible // indirect - github.com/emirpasic/gods v1.12.0 // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/camelcase v1.0.0 // indirect - github.com/felixge/httpsnoop v1.0.3 // indirect - github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/fvbommel/sortorder v1.0.1 // indirect - github.com/go-errors/errors v1.0.1 // indirect - github.com/go-git/gcfg v1.5.0 // indirect - github.com/go-git/go-billy/v5 v5.3.1 // indirect + github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.21.4 // indirect - github.com/go-openapi/errors v0.20.2 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect - github.com/go-openapi/strfmt v0.21.3 // indirect - github.com/go-openapi/swag v0.21.1 // indirect + github.com/go-openapi/errors v0.20.3 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.1 // indirect + github.com/go-openapi/spec v0.20.8 // indirect + github.com/go-openapi/strfmt v0.21.7 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/validate v0.22.1 // indirect github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 // indirect - github.com/golang/glog v1.0.0 // indirect + github.com/golang/glog v1.1.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/btree v1.0.1 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/gnostic v0.6.9 // indirect github.com/google/go-github/v41 v41.0.0 // indirect + github.com/google/go-github/v53 v53.2.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/gofuzz v1.1.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/gosimple/unidecode v1.0.1 // indirect - github.com/gregdel/pushover v1.1.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect + github.com/gregdel/pushover v1.2.1 // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-version v1.2.1 // indirect - github.com/huandu/xstrings v1.3.1 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/huandu/xstrings v1.3.3 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/itchyny/timefmt-go v0.1.5 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect - github.com/klauspost/compress v1.15.9 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/compress v1.16.5 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect + github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc4 // indirect github.com/opsgenie/opsgenie-go-sdk-v2 v1.0.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/russross/blackfriday v1.5.2 // indirect + github.com/prometheus/client_model v0.3.0 + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/rs/cors v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect - github.com/slack-go/slack v0.10.1 // indirect + github.com/shopspring/decimal v1.2.0 // indirect + github.com/skeema/knownhosts v1.2.1 // indirect + github.com/slack-go/slack v0.12.2 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/vmihailenco/go-tinylfu v0.2.1 // indirect + github.com/vmihailenco/go-tinylfu v0.2.2 // indirect github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/xanzy/ssh-agent v0.3.0 // indirect - github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect - go.mongodb.org/mongo-driver v1.10.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 // indirect - go.opentelemetry.io/otel/trace v1.11.1 // indirect - go.opentelemetry.io/proto/otlp v0.19.0 // indirect - go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect - golang.org/x/exp v0.0.0-20210901193431-a062eea981d2 // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect - golang.org/x/tools v0.1.12 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + github.com/xlab/treeprint v1.1.0 // indirect + go.mongodb.org/mongo-driver v1.11.3 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect + go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/net v0.19.0 + golang.org/x/sys v0.17.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.3.0 + golang.org/x/tools v0.13.0 // indirect gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect gomodules.xyz/notify v0.1.1 // indirect @@ -243,16 +280,15 @@ require ( gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/cli-runtime v0.24.2 // indirect - k8s.io/component-base v0.24.2 // indirect - k8s.io/component-helpers v0.24.2 // indirect - k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 // indirect - k8s.io/kube-aggregator v0.24.2 // indirect - k8s.io/kubernetes v1.24.2 // indirect - sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect - sigs.k8s.io/kustomize/api v0.11.4 // indirect - sigs.k8s.io/kustomize/kyaml v0.13.6 // indirect + k8s.io/cli-runtime v0.26.11 // indirect + k8s.io/component-base v0.26.11 // indirect + k8s.io/component-helpers v0.26.11 // indirect + k8s.io/gengo v0.0.0-20220902162205-c0856e24416d // indirect + k8s.io/kube-aggregator v0.26.4 // indirect + k8s.io/kubernetes v1.26.11 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kustomize/api v0.12.1 // indirect + sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect ) replace ( @@ -261,35 +297,41 @@ replace ( github.com/golang/protobuf => github.com/golang/protobuf v1.4.2 github.com/grpc-ecosystem/grpc-gateway => github.com/grpc-ecosystem/grpc-gateway v1.16.0 - github.com/improbable-eng/grpc-web => github.com/improbable-eng/grpc-web v0.0.0-20181111100011-16092bd1d58a + + // Avoid CVE-2022-3064 + gopkg.in/yaml.v2 => gopkg.in/yaml.v2 v2.4.0 // Avoid CVE-2022-28948 gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1 - // https://github.com/kubernetes/kubernetes/issues/79384#issuecomment-505627280 - k8s.io/api => k8s.io/api v0.24.2 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.24.2 - k8s.io/apimachinery => k8s.io/apimachinery v0.24.2 - k8s.io/apiserver => k8s.io/apiserver v0.24.2 - k8s.io/cli-runtime => k8s.io/cli-runtime v0.24.2 - k8s.io/client-go => k8s.io/client-go v0.24.2 - k8s.io/cloud-provider => k8s.io/cloud-provider v0.24.2 - k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.24.2 - k8s.io/code-generator => k8s.io/code-generator v0.24.2 - k8s.io/component-base => k8s.io/component-base v0.24.2 - k8s.io/component-helpers => k8s.io/component-helpers v0.24.2 - k8s.io/controller-manager => k8s.io/controller-manager v0.24.2 - k8s.io/cri-api => k8s.io/cri-api v0.24.2 - k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.24.2 - k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.24.2 - k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.24.2 - k8s.io/kube-proxy => k8s.io/kube-proxy v0.24.2 - k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.24.2 - k8s.io/kubectl => k8s.io/kubectl v0.24.2 - k8s.io/kubelet => k8s.io/kubelet v0.24.2 - k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.24.2 - k8s.io/metrics => k8s.io/metrics v0.24.2 - k8s.io/mount-utils => k8s.io/mount-utils v0.24.2 - k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.24.2 - k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.24.2 + k8s.io/api => k8s.io/api v0.26.11 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.26.11 + k8s.io/apimachinery => k8s.io/apimachinery v0.26.11 + k8s.io/apiserver => k8s.io/apiserver v0.26.11 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.26.11 + k8s.io/client-go => k8s.io/client-go v0.26.11 + k8s.io/cloud-provider => k8s.io/cloud-provider v0.26.11 + k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.26.11 + k8s.io/code-generator => k8s.io/code-generator v0.26.11 + k8s.io/component-base => k8s.io/component-base v0.26.11 + k8s.io/component-helpers => k8s.io/component-helpers v0.26.11 + k8s.io/controller-manager => k8s.io/controller-manager v0.26.11 + k8s.io/cri-api => k8s.io/cri-api v0.26.11 + k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.26.11 + k8s.io/dynamic-resource-allocation => k8s.io/dynamic-resource-allocation v0.26.11 + k8s.io/kms => k8s.io/kms v0.26.11 + k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.26.11 + k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.26.11 + k8s.io/kube-proxy => k8s.io/kube-proxy v0.26.11 + k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.26.11 + k8s.io/kubectl => k8s.io/kubectl v0.26.11 + k8s.io/kubelet => k8s.io/kubelet v0.26.11 + k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.26.11 + k8s.io/metrics => k8s.io/metrics v0.26.11 + k8s.io/mount-utils => k8s.io/mount-utils v0.26.11 + k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.26.11 + k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.26.11 + k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.26.11 + k8s.io/sample-controller => k8s.io/sample-controller v0.26.11 + ) diff --git a/go.sum b/go.sum index d67a1250468cf..d2e8f3c56535a 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,9 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bitbucket.org/bertimus9/systemstat v0.0.0-20180207000608-0eeff89b0690/go.mod h1:Ulb78X89vxKYgdL24HMTiXYHlyHEvruOj1ZPlqeNEZM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -17,6 +16,7 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -28,94 +28,637 @@ cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+Y cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= +cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= +cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= +cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= +cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= +cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= +cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= +cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= +cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= +cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= +cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= +cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= +cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= +cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= +cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= +cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= +cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= +cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= +cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= +cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= +cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= +cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= +cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= +cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= +cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= +cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= +cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= +cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= +cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= +cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= +cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= +cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= +cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= +cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= +cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= +cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= +cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= +cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= +cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= +cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= +cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= +cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= +cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= +cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= +cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= +cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= +cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= +cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= +cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= +cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= +cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= +cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= +cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= +cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= +cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= +cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= +cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= +cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= +cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= +cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= +cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= +cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= +cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= +cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= +cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= +cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= +cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= +cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= +cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= +cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= +cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= +cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= code.gitea.io/sdk/gitea v0.15.1 h1:WJreC7YYuxbn0UDaPuWIe/mtiNKTvLN8MLkaw71yx/M= code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go v55.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 h1:tz19qLF65vuu2ibfTqGVJxG/zZAI27NEIIbvAOQwYbw= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= +github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= +github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= +github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= +github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= +github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/Azure/kubelogin v0.0.20 h1:pDJhxzUWk2f/wjYQJFb0Vet7OYrcg6DLx1qj+sbXY70= +github.com/Azure/kubelogin v0.0.20/go.mod h1:QNuYUuwM2lqho9ovG5U/yv3/ZmFbEru3Jluw2ZeKcSk= +github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2 h1:BGX4OiGP9htYSd6M3pAZctcUUSruhIAUVkv2X0Cn9yE= +github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/GoogleCloudPlatform/k8s-cloud-provider v1.16.1-0.20210702024009-ea6160c1d0e3/go.mod h1:8XasY4ymP2V/tn2OOV9ZadmiTE1FIB/h3W+yNlPttKw= -github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA= github.com/Jeffail/gabs v1.4.0 h1://5fYRRTq1edjfIrQGvdkcd22pkYUrHZ5YC/H2GJVAo= github.com/Jeffail/gabs v1.4.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= -github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= -github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= -github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17 h1:iT12IBVClFevaf8PuVyi3UmZOVh4OqnaLxDTW2O6j3w= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/hcsshim v0.8.22/go.mod h1:91uVCVzvX2QD16sMCenoxxXo6L1wJnLMX2PSufFMtF0= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PagerDuty/go-pagerduty v1.6.0 h1:am81SzvG5Pw+s3JZ5yEy6kGvsXXklTNRrGr3d8WKpsU= -github.com/PagerDuty/go-pagerduty v1.6.0/go.mod h1:7eaBLzsDpK7VUvU0SJ5mohczQkoWrrr5CjDaw5gh1as= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/PagerDuty/go-pagerduty v1.7.0 h1:S1NcMKECxT5hJwV4VT+QzeSsSiv4oWl1s2821dUqG/8= +github.com/PagerDuty/go-pagerduty v1.7.0/go.mod h1:PuFyJKRz1liIAH4h5KVXVD18Obpp1ZXRdxHvmGXooro= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20210112200207-10ab4d695d60 h1:prBTRx78AQnXzivNT9Crhu564W/zPPr3ibSlpT9xKcE= @@ -125,10 +668,11 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d h1:WtAMR0fPCOfK7TPGZ8ZpLLY18HRvL7XJ3xcs0wnREgo= github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d/go.mod h1:WML6KOYjeU8N6YyusMjj2qRvaPNUEvrQvaxuFcMRFJY= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= -github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -136,25 +680,27 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis/v2 v2.23.1 h1:jR6wZggBxwWygeXcdNyguCOCIjPsZyNUNlAkTx2fu0U= -github.com/alicebob/miniredis/v2 v2.23.1/go.mod h1:84TWKZlxYkfgMucPBf5SOQBYJceZeQRFIaQgNMiCX6Q= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/alicebob/miniredis/v2 v2.30.4 h1:8S4/o1/KoUArAGbGwPxcwf0krlzceva2XVOSchFS7Eo= +github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= -github.com/antonmedv/expr v1.9.0 h1:j4HI3NHEdgDnN9p6oI6Ndr0G5QryMY0FNxT4ONrFDGU= -github.com/antonmedv/expr v1.9.0/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmHhwGEk8= +github.com/antonmedv/expr v1.15.2 h1:afFXpDWIC2n3bF+kTZE1JvFo+c34uaM3sTqh8z0xfdU= +github.com/antonmedv/expr v1.15.2/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= +github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/appscode/go v0.0.0-20191119085241-0887d8ec2ecc/go.mod h1:OawnOmAL4ZX3YaPdN+8HTNwBveT1jMsqP74moa9XUbE= -github.com/argoproj/gitops-engine v0.7.1-0.20221208230615-917f5a0f16d5 h1:iRpHi7X3q9G55KTaMjxKicgNnS2blFHaEfOOgsmP8lE= -github.com/argoproj/gitops-engine v0.7.1-0.20221208230615-917f5a0f16d5/go.mod h1:WpA/B7tgwfz+sdNE3LqrTrb7ArEY1FOPI2pAGI0hfPc= -github.com/argoproj/notifications-engine v0.3.1-0.20221203221941-490d98afd1d6 h1:b92Xft7MQv/SP56FW08zt5CMTE1rySH8UPDKOAgSzOM= -github.com/argoproj/notifications-engine v0.3.1-0.20221203221941-490d98afd1d6/go.mod h1:pgPU59KCsBOMhyw9amRWPoSuBmUWvx3Xsc5r0mUriLg= -github.com/argoproj/pkg v0.13.7-0.20221115212233-27bd8ce31415 h1:/5UtDHntvwPxbe/j2+xmQgvG83PQueGHko+9sf8+FA0= -github.com/argoproj/pkg v0.13.7-0.20221115212233-27bd8ce31415/go.mod h1:vqTRUU8ATWVtKog5bVg0zDrKxEjUaFnObZaqpY0oprQ= +github.com/argoproj/gitops-engine v0.7.1-0.20240124052710-5fd9f449e757 h1:5fKAhTQcTBom0vin56cz/UTPx2GMuvdb+lJRAUOPbHA= +github.com/argoproj/gitops-engine v0.7.1-0.20240124052710-5fd9f449e757/go.mod h1:gWE8uROi7hIkWGNAVM+8FWkMfo0vZ03SLx/aFw/DBzg= +github.com/argoproj/notifications-engine v0.4.1-0.20240206192038-2daee6022f41 h1:PQE8LbcbRHdtnQzeEWwVU2QHXACKOA30yS3No5HSoTQ= +github.com/argoproj/notifications-engine v0.4.1-0.20240206192038-2daee6022f41/go.mod h1:TsyusmXQWIL0ST7YMRG/ered7WlWDmbmnPpXnS2LJmM= +github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1 h1:qsHwwOJ21K2Ao0xPju1sNuqphyMnMYkyB3ZLoLtxWpo= +github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1/go.mod h1:CZHlkyAD1/+FbEn6cB2DQTj48IoLGvEYsWEvtzP3238= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -162,148 +708,155 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/auth0/go-jwt-middleware v1.0.1/go.mod h1:YSeUX3z6+TF2H+7padiEqNJ73Zy9vXW72U//IgN0BIM= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= -github.com/aws/aws-sdk-go v1.38.49/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.129/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.44.164 h1:qDj0RutF2Ut0HZYyUJxFdReLxpYrjupsu2JmDIgCvX8= -github.com/aws/aws-sdk-go v1.44.164/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.289/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.50.8 h1:gY0WoOW+/Wz6XmYSgDH9ge3wnAevYDSQWPxxJvqAkP4= +github.com/aws/aws-sdk-go v1.50.8/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= +github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= +github.com/aws/aws-sdk-go-v2/config v1.25.12 h1:mF4cMuNh/2G+d19nWnm1vJ/ak0qK6SbqF0KtSX9pxu0= +github.com/aws/aws-sdk-go-v2/config v1.25.12/go.mod h1:lOvvqtZP9p29GIjOTuA/76HiVk0c/s8qRcFRq2+E2uc= +github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8= +github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino= +github.com/aws/aws-sdk-go-v2/service/sqs v1.29.7 h1:tRNrFDGRm81e6nTX5Q4CFblea99eAfm0dxXazGpLceU= +github.com/aws/aws-sdk-go-v2/service/sqs v1.29.7/go.mod h1:8GWUDux5Z2h6z2efAtr54RdHXtLm8sq7Rg85ZNY/CZM= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= +github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= +github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U= +github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= +github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc= +github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bombsimon/logrusr/v2 v2.0.1 h1:1VgxVNQMCvjirZIYaT9JYn6sAVGVEcNtRE0y4mvaOAM= github.com/bombsimon/logrusr/v2 v2.0.1/go.mod h1:ByVAX+vHdLGAfdroiMg6q0zgq2FODY2lc5YJvzmOJio= -github.com/bradleyfalzon/ghinstallation/v2 v2.1.0 h1:5+NghM1Zred9Z078QEZtm28G/kfDfZN/92gkDlLwGVA= -github.com/bradleyfalzon/ghinstallation/v2 v2.1.0/go.mod h1:Xg3xPRN5Mcq6GDqeUVhFbjEWMb4JHCyWEeeBGEYQoTU= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/bradleyfalzon/ghinstallation/v2 v2.6.0 h1:IRY7Xy588KylkoycsUhFpW7cdGpy5Y5BPsz4IfuJtGk= +github.com/bradleyfalzon/ghinstallation/v2 v2.6.0/go.mod h1:oQ3etOwN3TRH4EwgW5/7MxSVMGlMlzG/O8TU7eYdoSk= +github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= +github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= +github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/casbin/casbin/v2 v2.60.0 h1:ZmC0/t4wolfEsDpDxTEsu2z6dfbMNpc11F52ceLs2Eo= -github.com/casbin/casbin/v2 v2.60.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= +github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3jM= +github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= -github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 h1:HD4PLRzjuCVW79mQ0/pdsalOLHJ+FaEoqJLxfltpb2U= -github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= +github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313/go.mod h1:P1wt9Z3DP8O6W3rvwCt0REIlshg1InHImaLW0t3ObY0= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE= -github.com/container-storage-interface/spec v1.5.0/go.mod h1:8K96oQNkJ7pFcC2R9Z1ynGGBB1I93kcS6PGg3SsOk8s= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.12/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/coredns/caddy v1.1.0/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= -github.com/coredns/corefile-migration v1.0.14/go.mod h1:XnhgULOEouimnzgn0t4WPuFDN2/PJQcTxdWKC5eXNGE= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-oidc/v3 v3.4.0 h1:xz7elHb/LDwm/ERpwHd+5nb7wFHL32rsr6bBOgaeu6g= -github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw= +github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o= +github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -314,13 +867,20 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= +github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= +github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= @@ -329,113 +889,127 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+ne github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= -github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/gfleury/go-bitbucket-v1 v0.0.0-20220301131131-8e7ed04b843e h1:C3DkNr9pxqXqCrmRHO7s3XgZS3zpi9GEA01GuWZODfo= github.com/gfleury/go-bitbucket-v1 v0.0.0-20220301131131-8e7ed04b843e/go.mod h1:LB3osS9X2JMYmTzcCArHHLrndBAfcVLQAvUddfs+ONs= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= -github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= -github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= -github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= -github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= -github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= -github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= +github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= +github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.0.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= -github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= +github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8= github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= +github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= +github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= -github.com/go-openapi/runtime v0.25.0 h1:7yQTCdRbWhX8vnIjdzU8S00tBYf7Sg71EBeorlPHvhc= -github.com/go-openapi/runtime v0.25.0/go.mod h1:Ux6fikcHXyyob6LNWxtE96hWwjBPYF0DXgVFuMTneOs= +github.com/go-openapi/runtime v0.26.0 h1:HYOFtG00FM1UvqrcxbEJg/SwvDRvYLQKGhw2zaQjTcc= +github.com/go-openapi/runtime v0.26.0/go.mod h1:QgRGeZwrUcSHdeh4Ka9Glvo0ug1LC5WyE+EV88plZrQ= github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ= github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU= +github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= +github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= +github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/validate v0.21.0 h1:+Wqk39yKOhfpLqNLEC0/eViCkzM5FVXVqrvt526+wcI= -github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= -github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= -github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= -github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= -github.com/go-redis/cache/v8 v8.4.2 h1:8YbsmnU1Ws3TKS6T+qALzYE/MlGE+A/lrlx1XTA3p6M= -github.com/go-redis/cache/v8 v8.4.2/go.mod h1:X7Jjd69Ssbrf3xBQLtIDE0g3WcSbFoQiSGeb8QfEJ+g= -github.com/go-redis/redis/v8 v8.11.3/go.mod h1:xNJ9xDG09FsIPwh3bWdk+0oDWHbtF9rPN0F/oD9XeKc= -github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= +github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/webhooks/v6 v6.3.0 h1:zBLUxK1Scxwi97TmZt5j/B/rLlard2zY7P77FHg58FE= +github.com/go-playground/webhooks/v6 v6.3.0/go.mod h1:GCocmfMtpJdkEOM1uG9p2nXzg1kY5X/LtvQgtPHUaaA= +github.com/go-redis/cache/v9 v9.0.0 h1:0thdtFo0xJi0/WXbRVu8B066z8OvVymXTJGaXrVWnN0= +github.com/go-redis/cache/v9 v9.0.0/go.mod h1:cMwi1N8ASBOufbIvk7cdXe2PbPjK/WMRL95FFHWsSgI= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -469,26 +1043,34 @@ github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/V github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2 h1:BbwX8wsMRDZRdNYxAna+4ls3wvMKJyn4PT6Zk1CPxP4= -github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2/go.mod h1:cY2AIrMgHm6oOHmR7jY+9TtjzSjQ3iG7tURJG3Y6XH0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gogits/go-gogs-client v0.0.0-20200905025246-8bb8a50cb355 h1:HTVNOdTWO/gHYeFnr/HwpYwY6tgMcYd+Rgf1XrHnORY= +github.com/gogits/go-gogs-client v0.0.0-20200905025246-8bb8a50cb355/go.mod h1:cY2AIrMgHm6oOHmR7jY+9TtjzSjQ3iG7tURJG3Y6XH0= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= -github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -511,17 +1093,18 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cadvisor v0.44.1/go.mod h1:GQ9KQfz0iNHQk3D6ftzJWK4TXabfIgM10Oy3FkR+Gzg= -github.com/google/cel-go v0.10.1/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= -github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -535,26 +1118,29 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v35 v35.3.0 h1:fU+WBzuukn0VssbayTT+Zo3/ESKX9JYWjbZTLOTEyho= github.com/google/go-github/v35 v35.3.0/go.mod h1:yWB7uCcVWaUbUP74Aq3whuMySRMatyRmq5U9FTNlbio= github.com/google/go-github/v41 v41.0.0 h1:HseJrM2JFf2vfiZJ8anY2hqBjdfY1Vlj/K27ueww4gg= github.com/google/go-github/v41 v41.0.0/go.mod h1:XgmCA5H323A9rtgExdTcnDkcqp6S30AVACCBDOonIxg= -github.com/google/go-github/v45 v45.2.0 h1:5oRLszbrkvxDDqBCNj2hjDZMKmvexaZ1xw/FCD+K3FI= -github.com/google/go-github/v45 v45.2.0/go.mod h1:FObaZJEDSTa/WGCzZ2Z3eoCDXWJKMenWWTrd8jrta28= -github.com/google/go-jsonnet v0.19.1 h1:MORxkrG0elylUqh36R4AcSPX0oZQa9hvI3lroN+kDhs= -github.com/google/go-jsonnet v0.19.1/go.mod h1:5JVT33JVCoehdTj5Z2KJq1eIdt3Nb8PCmZ+W5D8U350= +github.com/google/go-github/v53 v53.2.0 h1:wvz3FyF53v4BK+AsnvCmeNhf8AkTaeh2SoYu/XUvTtI= +github.com/google/go-github/v53 v53.2.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao= +github.com/google/go-jsonnet v0.20.0 h1:WG4TTSARuV7bSm4PMB4ohjxe33IHT5WVTrJSU33uT4g= +github.com/google/go-jsonnet v0.20.0/go.mod h1:VbgWF9JX7ztlv770x/TolZNGGFfiHEVx9G6ca2eUmeA= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -564,20 +1150,33 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= +github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= +github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -585,47 +1184,53 @@ github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0 github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= +github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbVkFXCXHz1QPhVXcHnQKAzBTPfQo= github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= -github.com/gregdel/pushover v1.1.0 h1:dwHyvrcpZCOS9V1fAnKPaGRRI5OC55cVaKhMybqNsKQ= -github.com/gregdel/pushover v1.1.0/go.mod h1:EcaO66Nn1StkpEm1iKtBTV3d2A16SoMsVER1PthX7to= +github.com/gregdel/pushover v1.2.1 h1:IPPJCdzXz60gMqnlzS0ZAW5z5aS1gI4nU+YM0Pe+ssA= +github.com/gregdel/pushover v1.2.1/go.mod h1:EcaO66Nn1StkpEm1iKtBTV3d2A16SoMsVER1PthX7to= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -638,8 +1243,8 @@ github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-retryablehttp v0.5.1/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= -github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -651,34 +1256,30 @@ github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/heketi/heketi v10.3.0+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o= -github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4= github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= -github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/improbable-eng/grpc-web v0.0.0-20181111100011-16092bd1d58a h1:RweVA0vnEyStwtAelyGmnU8ENDnwd1Q7pQr7U3J/rXo= -github.com/improbable-eng/grpc-web v0.0.0-20181111100011-16092bd1d58a/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= +github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/ishidawataru/sctp v0.0.0-20190723014705-7c296d48a2b5/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8= -github.com/itchyny/gojq v0.12.10 h1:6TcS0VYWS6wgntpF/4tnrzwdCMjiTxRAxIqZWfDsDQU= -github.com/itchyny/gojq v0.12.10/go.mod h1:o3FT8Gkbg/geT4pLI0tF3hvip5F3Y/uskjRz9OYa38g= +github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU= +github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOdnu/Sf4= github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= @@ -687,7 +1288,6 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i github.com/jeremywohl/flatten v1.0.1 h1:LrsxmB3hfwJuE+ptGOijix1PIfOoKLJ3Uee/mzbgtrs= github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -703,8 +1303,8 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -712,27 +1312,30 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -740,34 +1343,33 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/ktrysmt/go-bitbucket v0.9.55 h1:eOrF7wWmG4wz5iPr7ymgyWLoti2OfmrhU2tmT6yhAu8= -github.com/ktrysmt/go-bitbucket v0.9.55/go.mod h1:y5wrrDHCGUFAtuC43GyLBeFigq7rwrh4HqeDOOyZT+A= -github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= -github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc= +github.com/ktrysmt/go-bitbucket v0.9.67 h1:pFQs95TTgrwd3I9gKnas8zTYMVUOId0ZI4N0yqqMEVQ= +github.com/ktrysmt/go-bitbucket v0.9.67/go.mod h1:g4i0XvhrK5dQ+RIZAJmF0XfBvhBEn3Ibt/6YbEyXkXw= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= -github.com/lpabon/godbc v0.1.1/go.mod h1:Jo9QV0cf3U6jZABgiJ2skINAXb9j8m51r07g4KI92ZA= -github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= -github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5/go.mod h1:c2mYKRyMb1BPkO5St0c/ps62L4S0W2NAkaTXj9qEI+0= github.com/lusis/slack-test v0.0.0-20190426140909-c40012f20018/go.mod h1:sFlOUpQL1YcjhFVXhg1CG8ZASEs/Mf1oVb6H75JL/zg= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailgun/mailgun-go v2.0.0+incompatible/go.mod h1:NWTyU+O4aczg/nsGhQnvHL6v2n5Gy6Sv5tNDVvC6FbU= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= @@ -775,65 +1377,55 @@ github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8 h1:A6SLdFpRzUUF5v github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8/go.mod h1:UtpLyb/EupVKXF/N0b4NRe1DNg+QYJsnsHQ038romhM= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= -github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-zglob v0.0.4 h1:LQi2iOm0/fGgu80AioIJ/1j9w9Oh+9DZ39J4VAGzHQM= github.com/mattn/go-zglob v0.0.4/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 h1:YH424zrwLTlyHSH/GzLMJeu5zhYVZSx5RQxGKm1h96s= github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5/go.mod h1:PoGiBqKSQK1vIfQ+yVaFcGjDySHvym6FM1cNYnwzbrY= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.43/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= +github.com/minio/minio-go/v7 v7.0.58/go.mod h1:NUDy4A4oXPq1l2yK6LTSvCEzAMeIcoz9lcj5dbzSrRE= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hxQ= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/mountinfo v0.6.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= +github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -841,20 +1433,18 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= @@ -874,38 +1464,46 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod h1:eqOVx5Vwu4gd2mmMZvVZsgIqNSaW3xxRThUJ0k/TPk4= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= +github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow= +github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= +github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= +github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runc v1.1.1/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= +github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -921,35 +1519,39 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -958,59 +1560,55 @@ github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/quobyte/api v0.1.8/go.mod h1:jL7lIHrmqQ7yh05OJ+eEEdHr0u/kmT1Ff9iHd+4H6VI= +github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/r3labs/diff v1.1.0 h1:V53xhrbTHrWFWq3gI4b94AjgEJOerO1+1l0xyHOBi8M= github.com/r3labs/diff v1.1.0/go.mod h1:7WjXasNzi0vJetRcB/RqNl5dlIsmXcTTLmF5IoH6Xig= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= -github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/redis/go-redis/v9 v9.0.0-rc.4/go.mod h1:Vo3EsyWnicKnSKCA7HhgnvnyA74wOA69Cd2Meli5mmA= +github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= +github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/clock v0.0.0-20190514195947-2896927a307a h1:3QH7VyOaaiUHNrA9Se4YQIRkDTCw1EJls9xTUCaCeRM= +github.com/rogpeppe/clock v0.0.0-20190514195947-2896927a307a/go.mod h1:4r5QyqhjIWCcK8DO4KMclc5Iknq5qVBAlbYYzAbUScQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/cors v1.8.0 h1:P2KMzcFwrPoSjkF1WLRPsp3UMLyql8L4v9hQpVeK5so= -github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= -github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= +github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= @@ -1023,14 +1621,16 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= +github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c h1:fyKiXKO1/I/B6Y2U8T7WdQGWzwehOuGIrljPtt7YTTI= github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= -github.com/slack-go/slack v0.10.1 h1:BGbxa0kMsGEvLOEoZmYs8T1wWfoZXwmQFBb6FgYCXUA= -github.com/slack-go/slack v0.10.1/go.mod h1:wWL//kk0ho+FcQXcBTmEafUI5dz4qz5f4mMk8oIkioQ= +github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ= +github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -1040,43 +1640,30 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM= github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/storageos/go-api v2.2.0+incompatible/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1085,51 +1672,50 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= +github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vmihailenco/go-tinylfu v0.2.1 h1:78/wH+STtgM8+fN2GdjvvKoxF3mkdzoOoKQTchQRj+g= -github.com/vmihailenco/go-tinylfu v0.2.1/go.mod h1:CutYi2Q9puTxfcolkliPq4npPuofg9N9t8JVrjzwa3Q= +github.com/vmihailenco/go-tinylfu v0.2.2 h1:H1eiG6HM36iniK6+21n9LLpzx1G9R3DJa2UjUjbynsI= +github.com/vmihailenco/go-tinylfu v0.2.2/go.mod h1:CutYi2Q9puTxfcolkliPq4npPuofg9N9t8JVrjzwa3Q= github.com/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2ellBfvnqc= github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= -github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0 h1:qqllXPzXh+So+mmANlX/gCJrgo+1kQyshMoQ+NASzm0= -github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0/go.mod h1:2rx5KE5FLD0HRfkkpyn8JwbVLBdhgeiOb2D2D9LLKM4= -github.com/xanzy/go-gitlab v0.60.0 h1:HaIlc14k4t9eJjAhY0Gmq2fBHgKd1MthBn3+vzDtsbA= -github.com/xanzy/go-gitlab v0.60.0/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM= -github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= -github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= +github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= +github.com/xanzy/go-gitlab v0.91.1 h1:gnV57IPGYywWer32oXKBcdmc8dVxeKl3AauV8Bu17rw= +github.com/xanzy/go-gitlab v0.91.1/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= +github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1138,26 +1724,17 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ= -github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE= +github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46OtKyd3Q= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mongodb.org/mongo-driver v1.10.0 h1:UtV6N5k14upNp4LTduX0QCufG124fSu25Wz9tu94GLg= go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= +go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y= +go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1167,63 +1744,50 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.31.0 h1:li8u9OSMvLau7rMs8bmiL82OazG6MAkwPz2i6eS8TBQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.31.0/go.mod h1:SY9qHHUES6W3oZnO1H2W8NvsSovIoXRg/A1AH9px8+I= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.6.1/go.mod h1:blzUabWHkX6LJewxvadmzafgh/wnvBSDBdOuwkAtrWQ= -go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= -go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 h1:X2GndnMCsUPh6CiY2a+frAbNsXaPLbB0soHRYhAZ5Ig= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1/go.mod h1:i8vjiSzbiUC7wOQplijSXMYUpNM93DtlS5CbUT+C6oQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 h1:MEQNafcNCB0uQIti/oHgU7CZpUMYQ7qigBwMVKycHvc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1/go.mod h1:19O5I2U5iys38SsmT2uDJja/300woyzE1KPIQxEUBUc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1 h1:LYyG/f1W/jzAix16jbksJfMQFpOH/Ma6T639pVPMgfI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1/go.mod h1:QrRRQiY3kzAoYPNLP0W/Ikg0gR6V3LMc+ODSxr7yyvg= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZpKxs= -go.opentelemetry.io/otel/sdk v1.11.1/go.mod h1:/l3FE4SupHJ12TduVjUkZtlfFqDCQJlOlithYrdktys= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.6.1/go.mod h1:RkFRM1m0puWIq10oxImnGEduNBzxiN7TXluRBtE+5j0= -go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= -go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd h1:Uo/x0Ir5vQJ+683GXB9Ug+4fcjsbp7z7Ul8UaZbhsRM= +go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1233,38 +1797,56 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20210220032938-85be41e4509f/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4= -golang.org/x/exp v0.0.0-20210901193431-a062eea981d2 h1:Or4Ra3AAlhUlNn8WmIzw2Yq2vUHSkrP6E2e/FIESpF8= -golang.org/x/exp v0.0.0-20210901193431-a062eea981d2/go.mod h1:a3o/VtDNHN+dCVLEpzjjUHOzR+Ln3DHX056ZPzoZGGA= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1279,23 +1861,25 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1309,7 +1893,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1325,6 +1908,7 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -1343,13 +1927,12 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -1358,13 +1941,28 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1376,8 +1974,6 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -1387,8 +1983,18 @@ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= +golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1402,20 +2008,22 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1427,20 +2035,13 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1448,15 +2049,14 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1466,8 +2066,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1476,60 +2074,84 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220406155245-289d7a0edf71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1539,21 +2161,30 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1574,11 +2205,11 @@ golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1587,7 +2218,6 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1611,20 +2241,29 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1632,6 +2271,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45 h1:juzzlx91nWAOsHuOVfXZPMXHtJEKouZvY9bBbwlOeYs= gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45/go.mod h1:41y72mzHT7+jFNgyBpJRrZWuZJcLmLrTpq6iGgOFJMQ= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= @@ -1640,11 +2280,13 @@ gomodules.xyz/notify v0.1.1 h1:1tTuoyswmPvzqPCTEDQK8SZ3ukCxLsonAAwst2+y1a0= gomodules.xyz/notify v0.1.1/go.mod h1:QgQyU4xEA/plJcDeT66J2Go2V7U4c0pD9wjo7HfFil4= gomodules.xyz/version v0.1.0/go.mod h1:Y8xuV02mL/45psyPKG3NCVOwvAOy6T5Kx0l3rCjKSjU= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= -gonum.org/v1/gonum v0.6.2/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -1667,8 +2309,6 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.46.0/go.mod h1:ceL4oozhkAiTID8XMmJBsIxID/9wMXJVVFXPg4ylg3I= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= @@ -1684,10 +2324,33 @@ google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/S google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= +google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E= +google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= +google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= +google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.132.0 h1:8t2/+qZ26kAOGSmOiHwVycqVaDg7q3JDILrNi/Z6rvc= +google.golang.org/api v0.132.0/go.mod h1:AeTBC6GpJnJSRJjktDcPX0QwtS8pGYZOV6MSuSCusw0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1711,7 +2374,6 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -1732,18 +2394,19 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= @@ -1772,17 +2435,86 @@ google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2 google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90 h1:4SPz2GL2CXJt28MTF8V6Ap/9ZiVbQlJeGSd9qtA7DLs= google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1801,6 +2533,7 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= @@ -1820,20 +2553,31 @@ google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11 google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= @@ -1846,36 +2590,18 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= -gopkg.in/go-playground/webhooks.v5 v5.17.0 h1:truBced5ZmkiNKK47cM8bMe86wUSjNks7SFMuNKwzlc= -gopkg.in/go-playground/webhooks.v5 v5.17.0/go.mod h1:LZbya/qLVdbqDR1aKrGuWV6qbia2zCYSR5dpom2SInQ= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/retry.v1 v1.0.3 h1:a9CArYczAVv6Qs6VGoLMio99GEs7kY9UzSF9+LD+iGs= +gopkg.in/retry.v1 v1.0.3/go.mod h1:FJkXmWiMaAo7xB+xhvDF59zhfjDWyzmyAxiT4dB688g= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= @@ -1892,90 +2618,105 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI= -k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg= -k8s.io/apiextensions-apiserver v0.24.2 h1:/4NEQHKlEz1MlaK/wHT5KMKC9UKYz6NZz6JE6ov4G6k= -k8s.io/apiextensions-apiserver v0.24.2/go.mod h1:e5t2GMFVngUEHUd0wuCJzw8YDwZoqZfJiGOW6mm2hLQ= -k8s.io/apimachinery v0.24.2 h1:5QlH9SL2C8KMcrNJPor+LbXVTaZRReml7svPEh4OKDM= -k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apiserver v0.24.2 h1:orxipm5elPJSkkFNlwH9ClqaKEDJJA3yR2cAAlCnyj4= -k8s.io/apiserver v0.24.2/go.mod h1:pSuKzr3zV+L+MWqsEo0kHHYwCo77AT5qXbFXP2jbvFI= -k8s.io/cli-runtime v0.24.2 h1:KxY6tSgPGsahA6c1/dmR3uF5jOxXPx2QQY6C5ZrLmtE= -k8s.io/cli-runtime v0.24.2/go.mod h1:1LIhKL2RblkhfG4v5lZEt7FtgFG5mVb8wqv5lE9m5qY= -k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA= -k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30= -k8s.io/cloud-provider v0.24.2/go.mod h1:a7jyWjizk+IKbcIf8+mX2cj3NvpRv9ZyGdXDyb8UEkI= -k8s.io/cluster-bootstrap v0.24.2/go.mod h1:eIHV338K03vBm3u/ROZiNXxWJ4AJRoTR9PEUhcTvYkg= -k8s.io/code-generator v0.24.2 h1:EGeRWzJrpwi6T6CvoNl0spM6fnAnOdCr0rz7H4NU1rk= -k8s.io/code-generator v0.24.2/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= -k8s.io/component-base v0.24.2 h1:kwpQdoSfbcH+8MPN4tALtajLDfSfYxBDYlXobNWI6OU= -k8s.io/component-base v0.24.2/go.mod h1:ucHwW76dajvQ9B7+zecZAP3BVqvrHoOxm8olHEg0nmM= -k8s.io/component-helpers v0.24.2 h1:gtXmI/TjVINtkAdZn7m5p8+Vd0Mk4d1q8kwJMMLBdwY= -k8s.io/component-helpers v0.24.2/go.mod h1:TRQPBQKfmqkmV6c0HAmUs8cXVNYYYLsXy4zu8eODi9g= -k8s.io/controller-manager v0.24.2/go.mod h1:hpwCof4KxP4vrw/M5QiVxU6Zmmggmr1keGXtjGHF+vc= -k8s.io/cri-api v0.24.2/go.mod h1:t3tImFtGeStN+ES69bQUX9sFg67ek38BM9YIJhMmuig= -k8s.io/csi-translation-lib v0.24.2/go.mod h1:pdHc2CYLViQYYsOqOp79hjKYi8J4NZ7vpiVzn1SqBrg= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/api v0.26.11 h1:hLhTZRdYc3vBBOY4wbEyTLWgMyieOAk2Ws9NG57QqO4= +k8s.io/api v0.26.11/go.mod h1:bSr/A0TKRt5W2OMDdexkM/ER1NxOxiQqNNFXW2nMZrM= +k8s.io/apiextensions-apiserver v0.26.11 h1:6/T0Jm9c+Aw1AYUflPOz2sAsty304/DDSkciTr8+HuE= +k8s.io/apiextensions-apiserver v0.26.11/go.mod h1:xMqWxAB+AvSTdmFRVWlpavY9bJl/3g6yWiPn/fwZbT0= +k8s.io/apimachinery v0.26.11 h1:w//840HHdwSRKqD15j9YX9HLlU6RPlfrvW0xEhLk2+0= +k8s.io/apimachinery v0.26.11/go.mod h1:2/HZp0l6coXtS26du1Bk36fCuAEr/lVs9Q9NbpBtd1Y= +k8s.io/apiserver v0.26.11 h1:JcrlATLu5xQVLV7/rfRFFl9ivvNLmZH0dM3DFFdFp+w= +k8s.io/apiserver v0.26.11/go.mod h1:htEG/Q3sI3+6Is3Z26QzBjaCGICsz/kFj+IhIP4oJuE= +k8s.io/cli-runtime v0.26.11 h1:HO3Sgf06XkT8/8wWnhskfz4+LMKrChRz+A13vDJSQrE= +k8s.io/cli-runtime v0.26.11/go.mod h1:D98GjQtDmqn7WDuKBgWivd6R8qEs3yzT19EmCM5pqBs= +k8s.io/client-go v0.26.11 h1:RjfZr5+vQjjTRmk4oCqHyC0cgrZXPjw+X+ge35sk4GI= +k8s.io/client-go v0.26.11/go.mod h1:+emNszw9va/uRJIM5ALTBtFnlZMTjwBrNjRfEh0iuw8= +k8s.io/code-generator v0.26.11 h1:S0PJxapUhG6LWYezYB/FVE5Gf4BxGY0fCwnLrwfQ/70= +k8s.io/code-generator v0.26.11/go.mod h1:Hjxj7hpvSxcNnYIWzCSuEdwN0/9aHlezQRKJXr0Kv8U= +k8s.io/component-base v0.26.11 h1:1/JmB6fexefGByfFyIK6aHksZZVtaDskttzXOzmZ6zA= +k8s.io/component-base v0.26.11/go.mod h1:jYNisnoM6iWFRUg51pxaQabzL5fBYTr5CMpsLjUYGp0= +k8s.io/component-helpers v0.26.11 h1:XD2/2lik/5n1WFepDvgHzIGL0tix/EU3GaxGJHdsgkA= +k8s.io/component-helpers v0.26.11/go.mod h1:lw3bchkI0NHMPmb+CE73GznPW0Mvqd/Y9UVMEqBkysE= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 h1:TT1WdmqqXareKxZ/oNXEUSwKlLiHzPMyB0t8BaFeBYI= -k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20220902162205-c0856e24416d h1:U9tB195lKdzwqicbJvyJeOXV7Klv+wNAWENRnXEGi08= +k8s.io/gengo v0.0.0-20220902162205-c0856e24416d/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.5.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= -k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-aggregator v0.24.2 h1:vaKw45vFA5fIT0wdSehPIL7idjVxgLqz6iedOHedLG4= -k8s.io/kube-aggregator v0.24.2/go.mod h1:Ju2jNDixn+vqeeKEBfjfpc204bO1pbdXX0N9knCxeMQ= -k8s.io/kube-controller-manager v0.24.2/go.mod h1:KDE0yqiEvxYiO0WRpPA4rVx8AcK1vsWydUF37AJ9lTI= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 h1:yEQKdMCjzAOvGeiTwG4hO/hNVNtDOuUFvMUZ0OlaIzs= -k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8/go.mod h1:mbJ+NSUoAhuR14N0S63bPkh8MGVSo3VYSGZtH/mfMe0= -k8s.io/kube-proxy v0.24.2/go.mod h1:bozS2ufl/Ns6s40Ue34eV7rqyLVygi5usSmCgW7rFU8= -k8s.io/kube-scheduler v0.24.2/go.mod h1:DRa+aeXKSYUUOHHIc/9EcaO9+FW5FydaOfPSvaSW5Ko= -k8s.io/kubectl v0.24.2 h1:+RfQVhth8akUmIc2Ge8krMl/pt66V7210ka3RE/p0J4= -k8s.io/kubectl v0.24.2/go.mod h1:+HIFJc0bA6Tzu5O/YcuUt45APAxnNL8LeMuXwoiGsPg= -k8s.io/kubelet v0.24.2/go.mod h1:Xm9DkWQjwOs+uGOUIIGIPMvvmenvj0lDVOErvIKOOt0= -k8s.io/kubernetes v1.24.2 h1:AyjtHzSysliKR04Km91njmk2yaKmOa3ZISQZCIGUnVI= -k8s.io/kubernetes v1.24.2/go.mod h1:8e8maMiZzBR2/8Po5Uulx+MXZUYJuN3vtKwD4Ct1Xi0= -k8s.io/legacy-cloud-providers v0.24.2/go.mod h1:sgkasgIP2ZOew8fzoOq0mQLVXJ4AmB57IUbFUjzPWEo= -k8s.io/metrics v0.24.2/go.mod h1:5NWURxZ6Lz5gj8TFU83+vdWIVASx7W8lwPpHYCqopMo= -k8s.io/mount-utils v0.24.2/go.mod h1:XrSqB3a2e8sq+aU+rlbcBtQ3EgcuDk5RP9ZsGxjoDrI= -k8s.io/pod-security-admission v0.24.2/go.mod h1:znnuDHWWWvh/tpbYYPwTsd4y//qHi3cOX+wGxET/mMI= -k8s.io/sample-apiserver v0.24.2/go.mod h1:mf8qgDdu450wqpCJOkSAmoTgU4PIMAcfa5uTBwmJekE= -k8s.io/system-validators v1.7.0/go.mod h1:gP1Ky+R9wtrSiFbrpEPwWMeYz9yqyy1S/KOh0Vci7WI= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-aggregator v0.26.11 h1:P46aQPWOE+8bTbK2cqxUFP1XwH4ShZEHnlk1T5QFT8U= +k8s.io/kube-aggregator v0.26.11/go.mod h1:XNGLFzn4Ex7qFVqpCnvLUr354EM4QhMFuFSoB6JHmL4= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= +k8s.io/kubectl v0.26.11 h1:cVPzYA4HKefU3tPiVK7hZpJ+5Lm04XoyvCCY5ODznpQ= +k8s.io/kubectl v0.26.11/go.mod h1:xjEX/AHtEQrGj2AGqVopyHr/JU1hLy1k7Yn48JuK9LQ= +k8s.io/kubernetes v1.26.11 h1:g3r1IAUqsaHnOG2jdpoagJ5W9UCXkR2ljQ/7BmCzPNg= +k8s.io/kubernetes v1.26.11/go.mod h1:z1URAaBJ+XnOTr3Q/l4umxRUxn/OyD2fbkUgS0Bl7u4= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk= +k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= layeh.com/gopher-json v0.0.0-20190114024228-97fed8db8427 h1:RZkKxMR3jbQxdCEcglq3j7wY3PRJIopAwBlx1RE71X0= layeh.com/gopher-json v0.0.0-20190114024228-97fed8db8427/go.mod h1:ivKkcY8Zxw5ba0jldhZCYYQfGdb2K6u9tbYK1AwMIBc= -modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= +modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +oras.land/oras-go/v2 v2.3.0 h1:lqX1aXdN+DAmDTKjiDyvq85cIaI4RkIKp/PghWlAGIU= +oras.land/oras-go/v2 v2.3.0/go.mod h1:GeAwLuC4G/JpNwkd+bSZ6SkDMGaaYglt6YK2WvZP7uQ= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= -sigs.k8s.io/controller-runtime v0.11.0 h1:DqO+c8mywcZLFJWILq4iktoECTyn30Bkj0CwgqMpZWQ= -sigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= -sigs.k8s.io/kustomize/api v0.11.4 h1:/0Mr3kfBBNcNPOW5Qwk/3eb8zkswCwnqQxxKtmrTkRo= -sigs.k8s.io/kustomize/api v0.11.4/go.mod h1:k+8RsqYbgpkIrJ4p9jcdPqe8DprLxFUUO0yNOq8C+xI= -sigs.k8s.io/kustomize/cmd/config v0.10.6/go.mod h1:/S4A4nUANUa4bZJ/Edt7ZQTyKOY9WCER0uBS1SW2Rco= -sigs.k8s.io/kustomize/kustomize/v4 v4.5.4/go.mod h1:Zo/Xc5FKD6sHl0lilbrieeGeZHVYCA4BzxeAaLI05Bg= -sigs.k8s.io/kustomize/kyaml v0.13.6 h1:eF+wsn4J7GOAXlvajv6OknSunxpcOBQQqsnPxObtkGs= -sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/controller-runtime v0.14.7 h1:Vrnm2vk9ZFlRkXATHz0W0wXcqNl7kPat8q2JyxVy0Q8= +sigs.k8s.io/controller-runtime v0.14.7/go.mod h1:ErTs3SJCOujNUnTz4AS+uh8hp6DHMo1gj6fFndJT1X8= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= +sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= +sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= +sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/hack/dev-mounter/main.go b/hack/dev-mounter/main.go index bd01ae939e5b9..61988b2daa275 100644 --- a/hack/dev-mounter/main.go +++ b/hack/dev-mounter/main.go @@ -97,12 +97,15 @@ func newCommand() *cobra.Command { kubeClient := kubernetes.NewForConfigOrDie(config) factory := informers.NewSharedInformerFactoryWithOptions(kubeClient, 1*time.Minute, informers.WithNamespace(ns)) informer := factory.Core().V1().ConfigMaps().Informer() - informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + _, err = informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: handledConfigMap, UpdateFunc: func(oldObj, newObj interface{}) { handledConfigMap(newObj) }, }) + if err != nil { + log.Error(err) + } informer.Run(context.Background().Done()) }, } diff --git a/hack/gen-catalog/main.go b/hack/gen-catalog/main.go index 05f27406a9a25..486327e33ee6e 100644 --- a/hack/gen-catalog/main.go +++ b/hack/gen-catalog/main.go @@ -16,12 +16,12 @@ import ( "github.com/argoproj/notifications-engine/pkg/services" "github.com/argoproj/notifications-engine/pkg/triggers" "github.com/argoproj/notifications-engine/pkg/util/misc" - "github.com/ghodss/yaml" "github.com/olekukonko/tablewriter" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" ) func main() { @@ -150,7 +150,7 @@ func generateBuiltInTriggersDocs(out io.Writer, triggers map[string][]triggers.C } func generateCommandsDocs(out io.Writer) error { - // create parents so that CommandPath() is correctly resolved + // create parents so that CommandPath() is correctly resolved mainCmd := &cobra.Command{Use: "argocd"} adminCmd := &cobra.Command{Use: "admin"} toolCmd := admin.NewNotificationsCommand() diff --git a/hack/gen-crd-spec/main.go b/hack/gen-crd-spec/main.go index f69e3b17e7534..e7dcd658ef26a 100644 --- a/hack/gen-crd-spec/main.go +++ b/hack/gen-crd-spec/main.go @@ -10,9 +10,9 @@ import ( "github.com/argoproj/argo-cd/v2/pkg/apis/application" "github.com/argoproj/gitops-engine/pkg/utils/kube" - "github.com/ghodss/yaml" extensionsobj "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/yaml" ) var ( diff --git a/hack/gen-docs/main.go b/hack/gen-docs/main.go index 180b02fffec4f..b076224a0aaee 100644 --- a/hack/gen-docs/main.go +++ b/hack/gen-docs/main.go @@ -25,7 +25,7 @@ func generateNotificationsDocs() { log.Fatal(err) } if files != nil { - if e := updateMkDocsNav("Operator Manual", "Notification", "Notification Services", files); e != nil { + if e := updateMkDocsNav("Operator Manual", "Notifications", "Notification Services", files); e != nil { log.Fatal(e) } } @@ -49,6 +49,9 @@ func updateMkDocsNav(parent string, child string, subchild string, files []strin } rootnavitemmap := rootitem.(map[interface{}]interface{}) childnav, _ := findNavItem(rootnavitemmap[parent].([]interface{}), child) + if childnav == nil { + return fmt.Errorf("Can't find '%s' chile item under '%s' parent item in mkdoc.yml", child, parent) + } childnavmap := childnav.(map[interface{}]interface{}) childnavitems := childnavmap[child].([]interface{}) diff --git a/hack/gen-resources/generators/application_generator.go b/hack/gen-resources/generators/application_generator.go index b19280ae688b9..9e78299d979b0 100644 --- a/hack/gen-resources/generators/application_generator.go +++ b/hack/gen-resources/generators/application_generator.go @@ -31,8 +31,8 @@ func NewApplicationGenerator(argoClientSet *appclientset.Clientset, clientSet *k } func (pg *ApplicationGenerator) buildRandomSource(repositories []*v1alpha1.Repository) (*v1alpha1.ApplicationSource, error) { - rand.Seed(time.Now().Unix()) - repoNumber := rand.Int() % len(repositories) + seed := rand.New(rand.NewSource(time.Now().Unix())) + repoNumber := seed.Int() % len(repositories) return &v1alpha1.ApplicationSource{ RepoURL: repositories[repoNumber].Repo, Path: "helm-guestbook", @@ -49,8 +49,8 @@ func (ag *ApplicationGenerator) buildSource(opts *util.GenerateOpts, repositorie } func (pg *ApplicationGenerator) buildRandomDestination(opts *util.GenerateOpts, clusters []v1alpha1.Cluster) (*v1alpha1.ApplicationDestination, error) { - rand.Seed(time.Now().Unix()) - clusterNumber := rand.Int() % len(clusters) + seed := rand.New(rand.NewSource(time.Now().Unix())) + clusterNumber := seed.Int() % len(clusters) return &v1alpha1.ApplicationDestination{ Namespace: opts.Namespace, Name: clusters[clusterNumber].Name, diff --git a/hack/gen-resources/generators/cluster_generator.go b/hack/gen-resources/generators/cluster_generator.go index eec1cdfe3cc2e..6f125723c35ef 100644 --- a/hack/gen-resources/generators/cluster_generator.go +++ b/hack/gen-resources/generators/cluster_generator.go @@ -99,7 +99,7 @@ func (cg *ClusterGenerator) getClusterCredentials(namespace string, releaseSuffi return nil, nil, nil, err } - err = exec.Stream(remotecommand.StreamOptions{ + err = exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{ Stdin: &stdin, Stdout: &stdout, Stderr: &stderr, diff --git a/hack/gen-resources/generators/project_generator.go b/hack/gen-resources/generators/project_generator.go index 9f07cea1de9a1..7eee295af7f07 100644 --- a/hack/gen-resources/generators/project_generator.go +++ b/hack/gen-resources/generators/project_generator.go @@ -2,9 +2,9 @@ package generator import ( "context" - "log" "fmt" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "log" "github.com/argoproj/argo-cd/v2/hack/gen-resources/util" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" diff --git a/hack/gen-resources/generators/repo_generator.go b/hack/gen-resources/generators/repo_generator.go index 390dd27052069..11cc47f023d1a 100644 --- a/hack/gen-resources/generators/repo_generator.go +++ b/hack/gen-resources/generators/repo_generator.go @@ -33,7 +33,7 @@ func NewRepoGenerator(clientSet *kubernetes.Clientset) Generator { func fetchRepos(token string, page int) ([]Repo, error) { client := &http.Client{} - req, _ := http.NewRequest("GET", fmt.Sprintf("https://api.github.com/repos/argoproj/argocd-example-apps/forks?per_page=100&page=%v", page), nil) + req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("https://api.github.com/repos/argoproj/argocd-example-apps/forks?per_page=100&page=%v", page), nil) req.Header.Set("Authorization", token) resp, err := client.Do(req) if err != nil { diff --git a/hack/gen-resources/util/gen_options_parser.go b/hack/gen-resources/util/gen_options_parser.go index b352305f55437..08fb37ab9b653 100644 --- a/hack/gen-resources/util/gen_options_parser.go +++ b/hack/gen-resources/util/gen_options_parser.go @@ -1,8 +1,8 @@ package util import ( - "os" "gopkg.in/yaml.v2" + "os" ) type SourceOpts struct { @@ -33,7 +33,7 @@ type ClusterOpts struct { ValuesFilePath string `yaml:"valuesFilePath"` DestinationNamespace string `yaml:"destinationNamespace"` ClusterNamePrefix string `yaml:"clusterNamePrefix"` - Concurrency int `yaml:"parallel"` + Concurrency int `yaml:"parallel"` } type GenerateOpts struct { diff --git a/hack/gen-resources/util/sizedwaitgroup.go b/hack/gen-resources/util/sizedwaitgroup.go index b43011ff802c9..6c6d4fb1c5fff 100644 --- a/hack/gen-resources/util/sizedwaitgroup.go +++ b/hack/gen-resources/util/sizedwaitgroup.go @@ -24,7 +24,7 @@ // Based upon sync.WaitGroup, SizedWaitGroup allows to start multiple // routines and to wait for their end using the simple API. -// SizedWaitGroup adds the feature of limiting the maximum number of +// Package util SizedWaitGroup adds the feature of limiting the maximum number of // concurrently started routines. It could for example be used to start // multiples routines querying a database but without sending too much // queries in order to not overload the given database. diff --git a/hack/generate-proto.sh b/hack/generate-proto.sh index d544bd045904f..fa5d7322c7f81 100755 --- a/hack/generate-proto.sh +++ b/hack/generate-proto.sh @@ -10,9 +10,13 @@ set -o nounset set -o pipefail # shellcheck disable=SC2128 -PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE}")"/..; pwd) +PROJECT_ROOT=$( + cd "$(dirname "${BASH_SOURCE}")"/.. + pwd +) PATH="${PROJECT_ROOT}/dist:${PATH}" GOPATH=$(go env GOPATH) +GOPATH_PROJECT_ROOT="${GOPATH}/src/github.com/argoproj/argo-cd" # output tool versions go version @@ -41,6 +45,7 @@ APIMACHINERY_PKGS=( export GO111MODULE=on [ -e ./v2 ] || ln -s . v2 +[ -e "${GOPATH_PROJECT_ROOT}" ] || (mkdir -p "$(dirname "${GOPATH_PROJECT_ROOT}")" && ln -s "${PROJECT_ROOT}" "${GOPATH_PROJECT_ROOT}") # protoc_include is the include directory containing the .proto files distributed with protoc binary if [ -d /dist/protoc-include ]; then @@ -53,10 +58,17 @@ fi go-to-protobuf \ --go-header-file="${PROJECT_ROOT}"/hack/custom-boilerplate.go.txt \ - --packages="$(IFS=, ; echo "${PACKAGES[*]}")" \ - --apimachinery-packages="$(IFS=, ; echo "${APIMACHINERY_PKGS[*]}")" \ - --proto-import=./vendor \ - --proto-import="${protoc_include}" + --packages="$( + IFS=, + echo "${PACKAGES[*]}" + )" \ + --apimachinery-packages="$( + IFS=, + echo "${APIMACHINERY_PKGS[*]}" + )" \ + --proto-import="${PROJECT_ROOT}"/vendor \ + --proto-import="${protoc_include}" \ + --output-base="${GOPATH}/src/" # Either protoc-gen-go, protoc-gen-gofast, or protoc-gen-gogofast can be used to build # server/*/.pb.go from .proto files. golang/protobuf and gogo/protobuf can be used @@ -86,19 +98,20 @@ for i in ${PROTO_FILES}; do --${GOPROTOBINARY}_out=plugins=grpc:"$GOPATH"/src \ --grpc-gateway_out=logtostderr=true:"$GOPATH"/src \ --swagger_out=logtostderr=true:. \ - $i + "$i" done -[ -e ./v2 ] && rm -rf v2 + +[ -L "${GOPATH_PROJECT_ROOT}" ] && rm -rf "${GOPATH_PROJECT_ROOT}" +[ -L ./v2 ] && rm -rf v2 # collect_swagger gathers swagger files into a subdirectory collect_swagger() { SWAGGER_ROOT="$1" - EXPECTED_COLLISIONS="$2" SWAGGER_OUT="${PROJECT_ROOT}/assets/swagger.json" PRIMARY_SWAGGER=$(mktemp) COMBINED_SWAGGER=$(mktemp) - cat < "${PRIMARY_SWAGGER}" + cat <"${PRIMARY_SWAGGER}" { "swagger": "2.0", "info": { @@ -112,8 +125,19 @@ EOF rm -f "${SWAGGER_OUT}" - find "${SWAGGER_ROOT}" -name '*.swagger.json' -exec swagger mixin -c "${EXPECTED_COLLISIONS}" "${PRIMARY_SWAGGER}" '{}' \+ > "${COMBINED_SWAGGER}" - jq -r 'del(.definitions[].properties[]? | select(."$ref"!=null and .description!=null).description) | del(.definitions[].properties[]? | select(."$ref"!=null and .title!=null).title)' "${COMBINED_SWAGGER}" > "${SWAGGER_OUT}" + find "${SWAGGER_ROOT}" -name '*.swagger.json' -exec swagger mixin --ignore-conflicts "${PRIMARY_SWAGGER}" '{}' \+ >"${COMBINED_SWAGGER}" + jq -r 'del(.definitions[].properties[]? | select(."$ref"!=null and .description!=null).description) | del(.definitions[].properties[]? | select(."$ref"!=null and .title!=null).title) | + # The "array" and "map" fields have custom unmarshaling. Modify the swagger to reflect this. + .definitions.v1alpha1ApplicationSourcePluginParameter.properties.array = {"description":"Array is the value of an array type parameter.","type":"array","items":{"type":"string"}} | + del(.definitions.v1alpha1OptionalArray) | + .definitions.v1alpha1ApplicationSourcePluginParameter.properties.map = {"description":"Map is the value of a map type parameter.","type":"object","additionalProperties":{"type":"string"}} | + del(.definitions.v1alpha1OptionalMap) | + # Output for int64 is incorrect, because it is based on proto definitions, where int64 is a string. In our JSON API, we expect int64 to be an integer. https://github.com/grpc-ecosystem/grpc-gateway/issues/219 + (.definitions[]?.properties[]? | select(.type == "string" and .format == "int64")) |= (.type = "integer") + ' "${COMBINED_SWAGGER}" | + jq '.definitions.v1Time.type = "string" | .definitions.v1Time.format = "date-time" | del(.definitions.v1Time.properties)' | + jq '.definitions.v1alpha1ResourceNode.allOf = [{"$ref": "#/definitions/v1alpha1ResourceRef"}] | del(.definitions.v1alpha1ResourceNode.properties.resourceRef) ' \ + >"${SWAGGER_OUT}" /bin/rm "${PRIMARY_SWAGGER}" "${COMBINED_SWAGGER}" } @@ -124,9 +148,7 @@ clean_swagger() { find "${SWAGGER_ROOT}" -name '*.swagger.json' -delete } -echo "If additional types are added, the number of expected collisions may need to be increased" -EXPECTED_COLLISION_COUNT=95 -collect_swagger server ${EXPECTED_COLLISION_COUNT} +collect_swagger server clean_swagger server clean_swagger reposerver clean_swagger controller diff --git a/hack/installers/checksums/add-helm-checksums.sh b/hack/installers/checksums/add-helm-checksums.sh index 47292390d8789..95bf2b2566b69 100755 --- a/hack/installers/checksums/add-helm-checksums.sh +++ b/hack/installers/checksums/add-helm-checksums.sh @@ -3,7 +3,10 @@ # Usage: ./add-helm-checksums.sh 3.9.4 # use the desired version set -e - for arch in amd64 arm64 ppc64le s390x; do wget "https://get.helm.sh/helm-v$1-linux-$arch.tar.gz.sha256sum" -O "helm-v$1-linux-$arch.tar.gz.sha256" done + +for arch in amd64 arm64; do + wget "https://get.helm.sh/helm-v$1-darwin-$arch.tar.gz.sha256sum" -O "helm-v$1-darwin-$arch.tar.gz.sha256" +done \ No newline at end of file diff --git a/hack/installers/checksums/helm-v3.11.1-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.11.1-linux-amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..f1b701654f838 --- /dev/null +++ b/hack/installers/checksums/helm-v3.11.1-linux-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +0b1be96b66fab4770526f136f5f1a385a47c41923d33aab0dcb500e0f6c1bf7c helm-v3.11.1-linux-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.11.1-linux-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.11.1-linux-arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..ea0e0cc0b50ca --- /dev/null +++ b/hack/installers/checksums/helm-v3.11.1-linux-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +919173e8fb7a3b54d76af9feb92e49e86d5a80c5185020bae8c393fa0f0de1e8 helm-v3.11.1-linux-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.11.1-linux-ppc64le.tar.gz.sha256 b/hack/installers/checksums/helm-v3.11.1-linux-ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..bc06e69aac086 --- /dev/null +++ b/hack/installers/checksums/helm-v3.11.1-linux-ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +6ab8f2e253c115b17eda1e10e96d1637047efd315e9807bcb1d0d0bcad278ab7 helm-v3.11.1-linux-ppc64le.tar.gz diff --git a/hack/installers/checksums/helm-v3.11.1-linux-s390x.tar.gz.sha256 b/hack/installers/checksums/helm-v3.11.1-linux-s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..b0403aa122856 --- /dev/null +++ b/hack/installers/checksums/helm-v3.11.1-linux-s390x.tar.gz.sha256 @@ -0,0 +1 @@ +ab133e6b709c8107dc4f8f62838947350adb8e23d76b8c2c592ff4c09bc956ef helm-v3.11.1-linux-s390x.tar.gz diff --git a/hack/installers/checksums/helm-v3.11.2-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.11.2-linux-amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..56e53bec70d0e --- /dev/null +++ b/hack/installers/checksums/helm-v3.11.2-linux-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +781d826daec584f9d50a01f0f7dadfd25a3312217a14aa2fbb85107b014ac8ca helm-v3.11.2-linux-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.11.2-linux-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.11.2-linux-arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..c88970e874f59 --- /dev/null +++ b/hack/installers/checksums/helm-v3.11.2-linux-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +0a60baac83c3106017666864e664f52a4e16fbd578ac009f9a85456a9241c5db helm-v3.11.2-linux-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.11.2-linux-ppc64le.tar.gz.sha256 b/hack/installers/checksums/helm-v3.11.2-linux-ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..ddcdbc4de06dd --- /dev/null +++ b/hack/installers/checksums/helm-v3.11.2-linux-ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +04cbb8d053f2d8023e5cc6b771e9fa384fdd341eb7193a0fb592b7e2a036bf3d helm-v3.11.2-linux-ppc64le.tar.gz diff --git a/hack/installers/checksums/helm-v3.11.2-linux-s390x.tar.gz.sha256 b/hack/installers/checksums/helm-v3.11.2-linux-s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..b888b1a2166c9 --- /dev/null +++ b/hack/installers/checksums/helm-v3.11.2-linux-s390x.tar.gz.sha256 @@ -0,0 +1 @@ +9793b80711c2fd82dec6f9742415fffb762d41ca20033d4413364d372517f958 helm-v3.11.2-linux-s390x.tar.gz diff --git a/hack/installers/checksums/helm-v3.12.0-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.12.0-linux-amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..7462379983ea9 --- /dev/null +++ b/hack/installers/checksums/helm-v3.12.0-linux-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +da36e117d6dbc57c8ec5bab2283222fbd108db86c83389eebe045ad1ef3e2c3b helm-v3.12.0-linux-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.12.0-linux-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.12.0-linux-arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..3c6199cc3751f --- /dev/null +++ b/hack/installers/checksums/helm-v3.12.0-linux-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +658839fed8f9be2169f5df68e55cb2f0aa731a50df454caf183186766800bbd0 helm-v3.12.0-linux-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.12.0-linux-ppc64le.tar.gz.sha256 b/hack/installers/checksums/helm-v3.12.0-linux-ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..cae34431dba6d --- /dev/null +++ b/hack/installers/checksums/helm-v3.12.0-linux-ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +252d952b0e1b4ed2013710ddedf687ed5545d9f95a4fd72de0ff9617ff69155c helm-v3.12.0-linux-ppc64le.tar.gz diff --git a/hack/installers/checksums/helm-v3.12.0-linux-s390x.tar.gz.sha256 b/hack/installers/checksums/helm-v3.12.0-linux-s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..3dd96d5670d5d --- /dev/null +++ b/hack/installers/checksums/helm-v3.12.0-linux-s390x.tar.gz.sha256 @@ -0,0 +1 @@ +727474fb1684aa2349a77c54340c11ff09b19862d972c2403185fb163fec13ae helm-v3.12.0-linux-s390x.tar.gz diff --git a/hack/installers/checksums/helm-v3.12.1-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.12.1-linux-amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..9a9fdc145bb6a --- /dev/null +++ b/hack/installers/checksums/helm-v3.12.1-linux-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +1a7074f58ef7190f74ce6db5db0b70e355a655e2013c4d5db2317e63fa9e3dea helm-v3.12.1-linux-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.12.1-linux-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.12.1-linux-arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..a0c153758e688 --- /dev/null +++ b/hack/installers/checksums/helm-v3.12.1-linux-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +50548d4fedef9d8d01d1ed5a2dd5c849271d1017127417dc4c7ef6777ae68f7e helm-v3.12.1-linux-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.12.1-linux-ppc64le.tar.gz.sha256 b/hack/installers/checksums/helm-v3.12.1-linux-ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..2d51832d19aff --- /dev/null +++ b/hack/installers/checksums/helm-v3.12.1-linux-ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +32b25dba14549a4097bf3dd62221cf6df06279ded391f7479144e3a215982aaf helm-v3.12.1-linux-ppc64le.tar.gz diff --git a/hack/installers/checksums/helm-v3.12.1-linux-s390x.tar.gz.sha256 b/hack/installers/checksums/helm-v3.12.1-linux-s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..053b446be9913 --- /dev/null +++ b/hack/installers/checksums/helm-v3.12.1-linux-s390x.tar.gz.sha256 @@ -0,0 +1 @@ +f243b564cf7e4081fffdfe5a39487f6442fc439586a1f50cc59dd801c3e636a5 helm-v3.12.1-linux-s390x.tar.gz diff --git a/hack/installers/checksums/helm-v3.13.1-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.13.1-linux-amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..752a8c186935c --- /dev/null +++ b/hack/installers/checksums/helm-v3.13.1-linux-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +98c363564d00afd0cc3088e8f830f2a0eeb5f28755b3d8c48df89866374a1ed0 helm-v3.13.1-linux-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.13.1-linux-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.13.1-linux-arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..16904cec9ea94 --- /dev/null +++ b/hack/installers/checksums/helm-v3.13.1-linux-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +8c4a0777218b266a7b977394aaf0e9cef30ed2df6e742d683e523d75508d6efe helm-v3.13.1-linux-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.13.1-linux-ppc64le.tar.gz.sha256 b/hack/installers/checksums/helm-v3.13.1-linux-ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..3f1e79193a05e --- /dev/null +++ b/hack/installers/checksums/helm-v3.13.1-linux-ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +f0d4ae95b4db25d03ced987e30d424564bd4727af6a4a0b7fca41f14203306fb helm-v3.13.1-linux-ppc64le.tar.gz diff --git a/hack/installers/checksums/helm-v3.13.1-linux-s390x.tar.gz.sha256 b/hack/installers/checksums/helm-v3.13.1-linux-s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..493db677b1cf2 --- /dev/null +++ b/hack/installers/checksums/helm-v3.13.1-linux-s390x.tar.gz.sha256 @@ -0,0 +1 @@ +b657b72b34f568527093dede148ae72fcbc1f2e67d3fd6f2ffa1095637fbddb6 helm-v3.13.1-linux-s390x.tar.gz diff --git a/hack/installers/checksums/helm-v3.13.2-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.13.2-linux-amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..8908445e50510 --- /dev/null +++ b/hack/installers/checksums/helm-v3.13.2-linux-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +55a8e6dce87a1e52c61e0ce7a89bf85b38725ba3e8deb51d4a08ade8a2c70b2d helm-v3.13.2-linux-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.13.2-linux-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.13.2-linux-arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..cf6b333b8d98b --- /dev/null +++ b/hack/installers/checksums/helm-v3.13.2-linux-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +f5654aaed63a0da72852776e1d3f851b2ea9529cb5696337202703c2e1ed2321 helm-v3.13.2-linux-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.13.2-linux-ppc64le.tar.gz.sha256 b/hack/installers/checksums/helm-v3.13.2-linux-ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..696df1bb8df5e --- /dev/null +++ b/hack/installers/checksums/helm-v3.13.2-linux-ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +11d96134cc4ec106c23cd8c163072e9aed6cd73e36a3da120e5876d426203f37 helm-v3.13.2-linux-ppc64le.tar.gz diff --git a/hack/installers/checksums/helm-v3.13.2-linux-s390x.tar.gz.sha256 b/hack/installers/checksums/helm-v3.13.2-linux-s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..f539faf320db7 --- /dev/null +++ b/hack/installers/checksums/helm-v3.13.2-linux-s390x.tar.gz.sha256 @@ -0,0 +1 @@ +3ffc5b4a041e5306dc00905ebe5dfea449e34ada268a713d34c69709afd6a9a2 helm-v3.13.2-linux-s390x.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.0-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.0-linux-amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..6f9aaf5a270d5 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.0-linux-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +f43e1c3387de24547506ab05d24e5309c0ce0b228c23bd8aa64e9ec4b8206651 helm-v3.14.0-linux-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.0-linux-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.0-linux-arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..d0e09bd4b41f7 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.0-linux-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +b29e61674731b15f6ad3d1a3118a99d3cc2ab25a911aad1b8ac8c72d5a9d2952 helm-v3.14.0-linux-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.0-linux-ppc64le.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.0-linux-ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..d179322b99dd5 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.0-linux-ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +f1f9d3561724863edd4c06d89acb2e2fd8ae0f1b72058ceb891fa1c346ce5dbc helm-v3.14.0-linux-ppc64le.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.0-linux-s390x.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.0-linux-s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..31ff04397b29e --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.0-linux-s390x.tar.gz.sha256 @@ -0,0 +1 @@ +82298ef39936f1bef848959a29f77bff92d1309d8646657e3a7733702e81288c helm-v3.14.0-linux-s390x.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.1-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.1-linux-amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..cc06e12986311 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.1-linux-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +75496ea824f92305ff7d28af37f4af57536bf5138399c824dff997b9d239dd42 helm-v3.14.1-linux-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.1-linux-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.1-linux-arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..63f791b234ec4 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.1-linux-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +f865b8ad4228fd0990bbc5b50615eb6cb9eb31c9a9ca7238401ed897bbbe9033 helm-v3.14.1-linux-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.1-linux-ppc64le.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.1-linux-ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..17b9b1e625fac --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.1-linux-ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +4d853ab8fe3462287c7272fbadd5f73531ecdd6fa0db37d31630e41ae1ae21de helm-v3.14.1-linux-ppc64le.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.1-linux-s390x.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.1-linux-s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..232ec10e03fc6 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.1-linux-s390x.tar.gz.sha256 @@ -0,0 +1 @@ +19bf07999c7244bfeb0fd27152919b9faa1148cf43910edbb98efa9150058a98 helm-v3.14.1-linux-s390x.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.2-darwin-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.2-darwin-amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..8c2cdef022af2 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.2-darwin-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +64c633ae194bde77b7e7b7936a2814a7417817dc8b7bb7d270bd24a7a17b8d12 helm-v3.14.2-darwin-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.2-darwin-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.2-darwin-arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..a81e6ce01561f --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.2-darwin-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +ff502fd39b06497fa3d5a51ec2ced02b9fcfdb0e9a948d315fb1b2f13ddc39fb helm-v3.14.2-darwin-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.2-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.2-linux-amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..22049267fd24e --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.2-linux-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +0885a501d586c1e949e9b113bf3fb3290b0bbf74db9444a1d8c2723a143006a5 helm-v3.14.2-linux-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.2-linux-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.2-linux-arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..17320419ee7e6 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.2-linux-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +c65d6a9557bb359abc2c0d26670de850b52327dc3976ad6f9e14c298ea3e1b61 helm-v3.14.2-linux-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.2-linux-ppc64le.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.2-linux-ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..8ffe4ebe40e62 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.2-linux-ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +f3bc8582ff151e619cd285d9cdf9fef1c5733ee5522d8bed2ef680ef07f87223 helm-v3.14.2-linux-ppc64le.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.2-linux-s390x.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.2-linux-s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..d14a74799e6a2 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.2-linux-s390x.tar.gz.sha256 @@ -0,0 +1 @@ +7bda34aa26638e5116b31385f3b781172572175bf4c1ae00c87d8b154458ed94 helm-v3.14.2-linux-s390x.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.3-darwin-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.3-darwin-amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..5e2a74f27b822 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.3-darwin-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +4d5d01a94c7d6b07e71690dc1988bf3229680284c87f4242d28c6f1cc99653be helm-v3.14.3-darwin-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.3-darwin-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.3-darwin-arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..bcd34d12bb3ac --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.3-darwin-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +dff794152b62b7c1a9ff615d510f8657bcd7a3727c668e0d9d4955f70d5f7573 helm-v3.14.3-darwin-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.3-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.3-linux-amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..03d2c21b76f0d --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.3-linux-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +3c90f24e180f8c207b8a18e5ec82cb0fa49858a7a0a86e4ed52a98398681e00b helm-v3.14.3-linux-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.3-linux-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.3-linux-arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..fd99cd4e7e2d7 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.3-linux-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +85e1573e76fa60af14ba7e9ec75db2129b6884203be866893fa0b3f7e41ccd5e helm-v3.14.3-linux-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.3-linux-ppc64le.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.3-linux-ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..1b6a9770e6310 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.3-linux-ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +aab121ca470e2a502cda849a9b3e92eeb9a32e213b0f0a79a95a04e375d26ce7 helm-v3.14.3-linux-ppc64le.tar.gz diff --git a/hack/installers/checksums/helm-v3.14.3-linux-s390x.tar.gz.sha256 b/hack/installers/checksums/helm-v3.14.3-linux-s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..4ec7daaa0cd19 --- /dev/null +++ b/hack/installers/checksums/helm-v3.14.3-linux-s390x.tar.gz.sha256 @@ -0,0 +1 @@ +d64fa8aced3244b549377741dc4e2db8109e5270c0723c11b547a9da5f99ad43 helm-v3.14.3-linux-s390x.tar.gz diff --git a/hack/installers/checksums/kustomize_5.0.1_darwin_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.0.1_darwin_amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..ea459765dff6b --- /dev/null +++ b/hack/installers/checksums/kustomize_5.0.1_darwin_amd64.tar.gz.sha256 @@ -0,0 +1 @@ +4a2b9f7fad0355c8bea08da6dd9c48e790df5f357983280998d80b8dc7ad3def kustomize_5.0.1_darwin_amd64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.0.1_darwin_arm64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.0.1_darwin_arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..170c7942be3d4 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.0.1_darwin_arm64.tar.gz.sha256 @@ -0,0 +1 @@ +b264fe931e85d8ca7c7ac47872695b1fa39fe2b73cfc0d58cbdca0bde69d0bc0 kustomize_5.0.1_darwin_arm64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.0.1_linux_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.0.1_linux_amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..2af67b55d8ee4 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.0.1_linux_amd64.tar.gz.sha256 @@ -0,0 +1 @@ +dca623b36aef84fbdf28f79d02e9b3705ff641424ac1f872d5420dadb12fb78d kustomize_5.0.1_linux_amd64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.0.1_linux_arm64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.0.1_linux_arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..621146c893ea2 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.0.1_linux_arm64.tar.gz.sha256 @@ -0,0 +1 @@ +c6e036c5c7eee4c15f7544e441ced5cb6cf9eba24a011c25008df5617cd2fb85 kustomize_5.0.1_linux_arm64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.0.1_linux_ppc64le.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.0.1_linux_ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..27cd2529d5228 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.0.1_linux_ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +75df309f1efa92c57ccd15603be4fb6806e5b62f3e882d892ad34a4ee3c293f4 kustomize_5.0.1_linux_ppc64le.tar.gz diff --git a/hack/installers/checksums/kustomize_5.0.1_linux_s390x.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.0.1_linux_s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..2ef2cab8bdd37 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.0.1_linux_s390x.tar.gz.sha256 @@ -0,0 +1 @@ +1c67b27bf72a1c5615bd901b1971a88d920da8255038f13af7bb61695b08fda5 kustomize_5.0.1_linux_s390x.tar.gz diff --git a/hack/installers/checksums/kustomize_5.1.0_darwin_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.1.0_darwin_amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..946ee03acae12 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.1.0_darwin_amd64.tar.gz.sha256 @@ -0,0 +1 @@ +08664a17820138a68b7cbe302b1b63f4ec19c6e0838385f789ee0470f026ba25 kustomize_5.1.0_darwin_amd64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.1.0_linux_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.1.0_linux_amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..2eb13d31cfd88 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.1.0_linux_amd64.tar.gz.sha256 @@ -0,0 +1 @@ +52f4cf1ba34d38fd55a9bef819e329c9a4561f5f57f8f539346038ab5026dda8 kustomize_5.1.0_linux_amd64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.1.0_linux_arm64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.1.0_linux_arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..a5040fa6fd9a1 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.1.0_linux_arm64.tar.gz.sha256 @@ -0,0 +1 @@ +4e333ccf092bb72ef5d6bfd3e1f8abb161b5540ce47a53474d70c58eeb99f0a9 kustomize_5.1.0_linux_arm64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.1.0_linux_ppc64le.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.1.0_linux_ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..3f7eda534c9c9 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.1.0_linux_ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +83abeb44857db6a06f9dd35c5c40282b3b9ae4ca73e6306c52be604668964f0e kustomize_5.1.0_linux_ppc64le.tar.gz diff --git a/hack/installers/checksums/kustomize_5.1.0_linux_s390x.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.1.0_linux_s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..ded7a81927051 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.1.0_linux_s390x.tar.gz.sha256 @@ -0,0 +1 @@ +acff69cfb8e11b7df73c71610379ad76c003d638294f9bc98ceb0b68655e953e kustomize_5.1.0_linux_s390x.tar.gz diff --git a/hack/installers/checksums/kustomize_5.1.1_darwin_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.1.1_darwin_amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..09a5d2486e71c --- /dev/null +++ b/hack/installers/checksums/kustomize_5.1.1_darwin_amd64.tar.gz.sha256 @@ -0,0 +1 @@ +94047e967028b2849f9be1988f0cc084187ee3b77a1a0d88ede3979894da4af4 kustomize_5.1.1_darwin_amd64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.1.1_linux_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.1.1_linux_amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..79e38f6c825b7 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.1.1_linux_amd64.tar.gz.sha256 @@ -0,0 +1 @@ +3b30477a7ff4fb6547fa77d8117e66d995c2bdd526de0dafbf8b7bcb9556c85d kustomize_5.1.1_linux_amd64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.1.1_linux_arm64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.1.1_linux_arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..5a5da060b3d58 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.1.1_linux_arm64.tar.gz.sha256 @@ -0,0 +1 @@ +a1bfb5d919c84817b8265d661fb99aae8176bcfe0b9df92651de93304cae953d kustomize_5.1.1_linux_arm64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.1.1_linux_ppc64le.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.1.1_linux_ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..de21b4f3fd6d7 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.1.1_linux_ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +d9437fcadb9f3ff321ed83c8b485a7066cb7274971ee2e599be238c08be88493 kustomize_5.1.1_linux_ppc64le.tar.gz diff --git a/hack/installers/checksums/kustomize_5.1.1_linux_s390x.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.1.1_linux_s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..86e92abf259ae --- /dev/null +++ b/hack/installers/checksums/kustomize_5.1.1_linux_s390x.tar.gz.sha256 @@ -0,0 +1 @@ +24712149a2ebf38b854918988314df7d3255f738c8f1875c9823dd2e6aa07a60 kustomize_5.1.1_linux_s390x.tar.gz diff --git a/hack/installers/checksums/kustomize_5.2.1_darwin_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.2.1_darwin_amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..655910d278d31 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.2.1_darwin_amd64.tar.gz.sha256 @@ -0,0 +1 @@ +b7aba749da75d33e6fea49a5098747d379abc45583ff5cd16e2356127a396549 kustomize_5.2.1_darwin_amd64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.2.1_darwin_arm64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.2.1_darwin_arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..55f753b7cb4a5 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.2.1_darwin_arm64.tar.gz.sha256 @@ -0,0 +1 @@ +f6a5f3cffd45bac585a0c80b5ed855c2b72d932a1d6e8e7c87aae3be4eba5750 kustomize_5.2.1_darwin_arm64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.2.1_linux_amd64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.2.1_linux_amd64.tar.gz.sha256 new file mode 100644 index 0000000000000..a9cb3b79c77e8 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.2.1_linux_amd64.tar.gz.sha256 @@ -0,0 +1 @@ +88346543206b889f9287c0b92c70708040ecd5aad54dd33019c4d6579cd24de8 kustomize_5.2.1_linux_amd64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.2.1_linux_arm64.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.2.1_linux_arm64.tar.gz.sha256 new file mode 100644 index 0000000000000..ff4078ddd85f3 --- /dev/null +++ b/hack/installers/checksums/kustomize_5.2.1_linux_arm64.tar.gz.sha256 @@ -0,0 +1 @@ +5566f7badece5a72d42075d8dffa6296a228966dd6ac2390de7afbb9675c3aaa kustomize_5.2.1_linux_arm64.tar.gz diff --git a/hack/installers/checksums/kustomize_5.2.1_linux_ppc64le.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.2.1_linux_ppc64le.tar.gz.sha256 new file mode 100644 index 0000000000000..b5b6c7e9f077c --- /dev/null +++ b/hack/installers/checksums/kustomize_5.2.1_linux_ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +82d732cf624b6fa67dfabe751e9a1510e2d08605996b1b130b4c0f5b835b130e kustomize_5.2.1_linux_ppc64le.tar.gz diff --git a/hack/installers/checksums/kustomize_5.2.1_linux_s390x.tar.gz.sha256 b/hack/installers/checksums/kustomize_5.2.1_linux_s390x.tar.gz.sha256 new file mode 100644 index 0000000000000..565fb1df10d8e --- /dev/null +++ b/hack/installers/checksums/kustomize_5.2.1_linux_s390x.tar.gz.sha256 @@ -0,0 +1 @@ +d94cb97a2776b4685ab41233dfd5f0b426f399d2fce87d2b69e1ce4907f3aad2 kustomize_5.2.1_linux_s390x.tar.gz diff --git a/hack/installers/install-codegen-go-tools.sh b/hack/installers/install-codegen-go-tools.sh index c6ebfc8902cee..6c9775ff46274 100755 --- a/hack/installers/install-codegen-go-tools.sh +++ b/hack/installers/install-codegen-go-tools.sh @@ -26,7 +26,7 @@ mkdir -p $GOBIN #go_mod_install github.com/gogo/protobuf/protoc-gen-gogo go_mod_install github.com/gogo/protobuf/protoc-gen-gogofast -# protoc-gen-grpc-gateway is used to build .pb.gw.go files from from .proto files +# protoc-gen-grpc-gateway is used to build .pb.gw.go files from .proto files go_mod_install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway # # protoc-gen-swagger is used to build swagger.json diff --git a/hack/installers/install-gotestsum.sh b/hack/installers/install-gotestsum.sh new file mode 100755 index 0000000000000..27b497696ccdc --- /dev/null +++ b/hack/installers/install-gotestsum.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -eux -o pipefail + +# Code from: https://github.com/argoproj/argo-rollouts/blob/f650a1fd0ba7beb2125e1598410515edd572776f/hack/installers/install-dev-tools.sh + +PROJECT_ROOT=$(cd $(dirname ${BASH_SOURCE})/../..; pwd) +DIST_PATH="${PROJECT_ROOT}/dist" +PATH="${DIST_PATH}:${PATH}" + +mkdir -p ${DIST_PATH} + +gotestsum_version=1.11.0 + +OS=$(go env GOOS) +ARCH=$(go env GOARCH) + +export TARGET_FILE=gotestsum_${gotestsum_version}_${OS}_${ARCH}.tar.gz +temp_path="/tmp/${TARGET_FILE}" +url=https://github.com/gotestyourself/gotestsum/releases/download/v${gotestsum_version}/gotestsum_${gotestsum_version}_${OS}_${ARCH}.tar.gz +[ -e ${temp_path} ] || curl -sLf --retry 3 -o ${temp_path} ${url} + +mkdir -p /tmp/gotestsum-${gotestsum_version} +tar -xvzf ${temp_path} -C /tmp/gotestsum-${gotestsum_version} +cp /tmp/gotestsum-${gotestsum_version}/gotestsum ${DIST_PATH}/gotestsum +chmod +x ${DIST_PATH}/gotestsum +gotestsum --version diff --git a/hack/installers/install-helm-linux.sh b/hack/installers/install-helm.sh similarity index 63% rename from hack/installers/install-helm-linux.sh rename to hack/installers/install-helm.sh index 6371fd452c204..ef3882fdaf688 100755 --- a/hack/installers/install-helm-linux.sh +++ b/hack/installers/install-helm.sh @@ -3,10 +3,10 @@ set -eux -o pipefail . $(dirname $0)/../tool-versions.sh -export TARGET_FILE=helm-v${helm3_version}-linux-${ARCHITECTURE}.tar.gz +export TARGET_FILE=helm-v${helm3_version}-${INSTALL_OS}-${ARCHITECTURE}.tar.gz -[ -e $DOWNLOADS/${TARGET_FILE} ] || curl -sLf --retry 3 -o $DOWNLOADS/${TARGET_FILE} https://get.helm.sh/helm-v${helm3_version}-linux-$ARCHITECTURE.tar.gz +[ -e $DOWNLOADS/${TARGET_FILE} ] || curl -sLf --retry 3 -o $DOWNLOADS/${TARGET_FILE} https://get.helm.sh/helm-v${helm3_version}-$INSTALL_OS-$ARCHITECTURE.tar.gz $(dirname $0)/compare-chksum.sh mkdir -p /tmp/helm && tar -C /tmp/helm -xf $DOWNLOADS/${TARGET_FILE} -sudo install -m 0755 /tmp/helm/linux-$ARCHITECTURE/helm $BIN/helm +sudo install -m 0755 /tmp/helm/$INSTALL_OS-$ARCHITECTURE/helm $BIN/helm helm version --client diff --git a/hack/installers/install-kustomize.sh b/hack/installers/install-kustomize.sh index 7a8f2ed3e0efc..3457d1613243f 100755 --- a/hack/installers/install-kustomize.sh +++ b/hack/installers/install-kustomize.sh @@ -8,7 +8,7 @@ INSTALL_PATH="${INSTALL_PATH:-$PROJECT_ROOT/dist}" PATH="${INSTALL_PATH}:${PATH}" [ -d $INSTALL_PATH ] || mkdir -p $INSTALL_PATH -KUSTOMIZE_VERSION=${KUSTOMIZE_VERSION:-$kustomize4_version} +KUSTOMIZE_VERSION=${KUSTOMIZE_VERSION:-$kustomize5_version} if [ -z $INSTALL_OS ]; then echo "install kustomize error: unsupported operating system" diff --git a/hack/installers/install-lint-tools.sh b/hack/installers/install-lint-tools.sh index a77e67c6fb0cc..b4f68e464b15b 100755 --- a/hack/installers/install-lint-tools.sh +++ b/hack/installers/install-lint-tools.sh @@ -1,4 +1,4 @@ #!/bin/bash set -eux -o pipefail -GO111MODULE=on go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.2 +GO111MODULE=on go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54.0 diff --git a/hack/known_types/main.go b/hack/known_types/main.go index a2e4665a0eb1a..be8bcfdc7b50c 100644 --- a/hack/known_types/main.go +++ b/hack/known_types/main.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "go/importer" + "go/token" "go/types" "os" "strings" @@ -36,15 +37,16 @@ func newCommand() *cobra.Command { packagePath := args[1] outputPath := args[2] - // nolint:staticcheck - imprt := importer.For("source", nil) + if !strings.HasPrefix(packagePath, packagePrefix) { + return fmt.Errorf("package must be under %s", packagePrefix) + } + + imprt := importer.ForCompiler(token.NewFileSet(), "source", nil) pkg, err := imprt.Import(packagePath) if err != nil { return err } - if !strings.HasPrefix(packagePath, packagePrefix) { - return fmt.Errorf("package must be under %s", packagePrefix) - } + shortPackagePath := strings.TrimPrefix(packagePath, packagePrefix) var mapItems []string @@ -82,7 +84,7 @@ func init() {%s } } - return os.WriteFile(outputPath, []byte(res), 0644) + return os.WriteFile(outputPath, []byte(res+"\n"), 0644) }, } command.Flags().StringVar(&docsOutputPath, "docs", "", "Docs output file path") diff --git a/hack/ssh_known_hosts b/hack/ssh_known_hosts index 31a7bae3fce5d..39d09f58685c2 100644 --- a/hack/ssh_known_hosts +++ b/hack/ssh_known_hosts @@ -1,6 +1,13 @@ -# This file was automatically generated. DO NOT EDIT -bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw== -github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== +# This file was automatically generated by hack/update-ssh-known-hosts.sh. DO NOT EDIT +[ssh.github.com]:443 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= +[ssh.github.com]:443 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl +[ssh.github.com]:443 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= +bitbucket.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIQmuzMBuKdWeF4+a2sjSSpBK0iqitSQ+5BM9KhpexuGt20JpTVM7u5BDZngncgrqDMbWdxMWWOGtZ9UgbqgZE= +bitbucket.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIazEu89wgQZ4bqs3d63QSMzYVa0MuJ2e2gKTKqu+UUO +bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQeJzhupRu0u0cdegZIa8e86EG2qOCsIsD1Xw0xSeiPDlCr7kq97NLmMbpKTX6Esc30NuoqEEHCuc7yWtwp8dI76EEEB1VqY9QJq6vk+aySyboD5QF61I/1WeTwu+deCbgKMGbUijeXhtfbxSxm6JwGrXrhBdofTsbKRUsrN1WoNgUa8uqN1Vx6WAJw1JHPhglEGGHea6QICwJOAr/6mrui/oB7pkaWKHj3z7d1IC4KWLtY47elvjbaTlkN04Kc/5LFEirorGYVbt15kAUlqGM65pk6ZBxtaO3+30LVlORZkxOh+LKL/BvbZ/iRNhItLqNyieoQj/uh/7Iv4uyH/cV/0b4WDSd3DptigWq84lJubb9t/DnZlrJazxyDCulTmKdOR7vs9gMTo+uoIrPSb8ScTtvw65+odKAlBj59dhnVp9zd7QUojOpXlL62Aw56U4oO+FALuevvMjiWeavKhJqlR7i5n9srYcrNV7ttmDw7kf/97P5zauIhxcjX+xHv4M= +github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= +github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl +github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 diff --git a/hack/test.sh b/hack/test.sh index 1ca29343be2b2..454a58d749291 100755 --- a/hack/test.sh +++ b/hack/test.sh @@ -15,12 +15,4 @@ fi mkdir -p $TEST_RESULTS -report() { - set -eux -o pipefail - - go-junit-report < $TEST_RESULTS/test.out > $TEST_RESULTS/junit.xml -} - -trap 'report' EXIT - -go test $TEST_FLAGS -failfast $* 2>&1 | tee $TEST_RESULTS/test.out +GODEBUG="tarinsecurepath=0,zipinsecurepath=0" ${DIST_DIR}/gotestsum --rerun-fails-report=rerunreport.txt --junitfile=$TEST_RESULTS/junit.xml --format=testname --rerun-fails="$RERUN_FAILS" --packages="$PACKAGES" -- $TEST_FLAGS $* diff --git a/hack/tool-versions.sh b/hack/tool-versions.sh index dd342c1e2af6c..e87dc54590afd 100644 --- a/hack/tool-versions.sh +++ b/hack/tool-versions.sh @@ -11,8 +11,8 @@ # Use ./hack/installers/checksums/add-helm-checksums.sh and # add-kustomize-checksums.sh to help download checksums. ############################################################################### -helm3_version=3.10.3 +helm3_version=3.14.3 kubectl_version=1.17.8 kubectx_version=0.6.3 -kustomize4_version=4.5.7 +kustomize5_version=5.2.1 protoc_version=3.17.3 diff --git a/hack/trigger-release.sh b/hack/trigger-release.sh index 508ecfa0c141f..b7f843fad7fd6 100755 --- a/hack/trigger-release.sh +++ b/hack/trigger-release.sh @@ -4,33 +4,11 @@ NEW_TAG="${1}" GIT_REMOTE="${2}" -COMMIT_MSG="${3}" -origToken="" set -ue -restoreToken() { - if test "$origToken" != ""; then - echo ">> Restoring original Git comment char" - git config core.commentChar "$origToken" - fi -} - -cleanLocalTriggerTag() { - if test "$TRIGGER_TAG" != ""; then - echo ">> Remove trigger tag '${TRIGGER_TAG}' from local repository." - git tag -d $TRIGGER_TAG - fi -} - -cleanup() { - restoreToken - cleanLocalTriggerTag -} - if test "${NEW_TAG}" = "" -o "${GIT_REMOTE}" = ""; then - echo "!! Usage: $0 [path to release notes file]" >&2 - echo "You can use generate-release-notes.sh to generate the release notes file." >&2 + echo "!! Usage: $0 " >&2 exit 1 fi @@ -41,8 +19,6 @@ if ! echo "${NEW_TAG}" | egrep -q '^v[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)*$'; then exit 1 fi -TRIGGER_TAG="release-${NEW_TAG}" - # Check whether we are in correct branch of local repository RELEASE_BRANCH="${NEW_TAG%\.[0-9]*}" RELEASE_BRANCH="release-${RELEASE_BRANCH#*v}" @@ -55,18 +31,9 @@ fi echo ">> Working in release branch '${RELEASE_BRANCH}'" -# Check for trigger tag existing in local repo -if git tag -l | grep -q -E "^${TRIGGER_TAG}$"; then - echo "!! Release tag '${TRIGGER_TAG}' already exists in local repository" >&2 - exit 1 -fi - -# Check for trigger tag existing in remote repo -if git ls-remote ${GIT_REMOTE} refs/tags/${TRIGGER_TAG} | grep -q -E "^${NEW_TAG}$"; then - echo "!! Target trigger tag '${TRIGGER_TAG}' already exists in remote '${GIT_REMOTE}'" >&2 - echo "!! Another operation currently in progress?" >&2 - exit 1 -fi +echo ">> Ensuring release branch is up to date." +# make sure release branch is up to date +git pull ${GIT_REMOTE} ${RELEASE_BRANCH} # Check for target (version) tag in local repo if git tag -l | grep -q -E "^${NEW_TAG}$"; then @@ -75,35 +42,15 @@ if git tag -l | grep -q -E "^${NEW_TAG}$"; then fi # Check for target (version) tag in remote repo -if git ls-remote ${GIT_REMOTE} refs/tags/${NEW_TAG} | grep -q -E "^${NEW_TAG}$"; then +if git ls-remote ${GIT_REMOTE} refs/tags/${NEW_TAG} | grep -q -E "${NEW_TAG}$"; then echo "!! Target version tag '${NEW_TAG}' already exists in remote '${GIT_REMOTE}'" >&2 exit 1 fi -echo ">> Creating new release '${NEW_TAG}' by pushing '${TRIGGER_TAG}' to '${GIT_REMOTE}'" - -GIT_ARGS="" -if test "${COMMIT_MSG}" != ""; then - if ! test -f "${COMMIT_MSG}"; then - echo "!! Release notes at '${COMMIT_MSG}' do not exist or are not readable." >&2 - exit 1 - fi - GIT_ARGS="-F ${COMMIT_MSG}" -fi - -# We need different git comment char than '#', because markdown makes extensive -# use of '#' - we chose ';' for our operation. -origToken=$(git config core.commentChar || echo '#') -echo ">> Saving original Git comment char '${origToken}' and setting it to ';' for this run" -if ! git config core.commentChar ';'; then - echo "!! Could not set git config commentChar ';'" >&2 - exit 1 -fi - -trap cleanup SIGINT EXIT +echo ">> Creating new release '${NEW_TAG}' by pushing '${NEW_TAG}' to '${GIT_REMOTE}'" -# Create trigger tag in local repository -git tag -a ${GIT_ARGS} ${TRIGGER_TAG} +# Create new tag in local repository +git tag ${NEW_TAG} -# Push the trigger tag to remote repository -git push ${GIT_REMOTE} ${TRIGGER_TAG} +# Push the new tag to remote repository +git push ${GIT_REMOTE} ${NEW_TAG} diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index abee0493ead86..9f6d15524d04d 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -19,21 +19,31 @@ set -o errexit set -o nounset set -o pipefail -PROJECT_ROOT=$(cd $(dirname ${BASH_SOURCE})/..; pwd) +PROJECT_ROOT=$( + cd $(dirname ${BASH_SOURCE})/.. + pwd +) PATH="${PROJECT_ROOT}/dist:${PATH}" +GOPATH=$(go env GOPATH) +GOPATH_PROJECT_ROOT="${GOPATH}/src/github.com/argoproj/argo-cd" TARGET_SCRIPT=/tmp/generate-groups.sh # codegen utilities are installed outside of generate-groups.sh so remove the `go install` step in the script. -sed -e '/go install/d' ${PROJECT_ROOT}/vendor/k8s.io/code-generator/generate-groups.sh > ${TARGET_SCRIPT} +sed -e '/go install/d' ${PROJECT_ROOT}/vendor/k8s.io/code-generator/generate-groups.sh >${TARGET_SCRIPT} # generate-groups.sh assumes codegen utilities are installed to GOBIN, but we just ensure the CLIs # are in the path and invoke them without assumption of their location sed -i.bak -e 's#${gobin}/##g' ${TARGET_SCRIPT} [ -e ./v2 ] || ln -s . v2 +[ -e "${GOPATH_PROJECT_ROOT}" ] || (mkdir -p "$(dirname "${GOPATH_PROJECT_ROOT}")" && ln -s "${PROJECT_ROOT}" "${GOPATH_PROJECT_ROOT}") + bash -x ${TARGET_SCRIPT} "deepcopy,client,informer,lister" \ github.com/argoproj/argo-cd/v2/pkg/client github.com/argoproj/argo-cd/v2/pkg/apis \ "application:v1alpha1" \ - --go-header-file ${PROJECT_ROOT}/hack/custom-boilerplate.go.txt -[ -e ./v2 ] && rm -rf v2 \ No newline at end of file + --go-header-file "${PROJECT_ROOT}/hack/custom-boilerplate.go.txt" \ + --output-base "${GOPATH}/src" + +[ -L "${GOPATH_PROJECT_ROOT}" ] && rm -rf "${GOPATH_PROJECT_ROOT}" +[ -L ./v2 ] && rm -rf v2 diff --git a/hack/update-kubernetes-version.sh b/hack/update-kubernetes-version.sh new file mode 100755 index 0000000000000..8d52033a601fc --- /dev/null +++ b/hack/update-kubernetes-version.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ -z "${1:-}" ]; then + echo "Example usage: ./hack/update-kubernetes-version.sh v1.26.11" + exit 1 +fi +VERSION=${1#"v"} +MODS=($( + curl -sS https://raw.githubusercontent.com/kubernetes/kubernetes/v${VERSION}/go.mod | + sed -n 's|.*k8s.io/\(.*\) => ./staging/src/k8s.io/.*|k8s.io/\1|p' +)) +for MOD in "${MODS[@]}"; do + echo "Updating $MOD..." >&2 + V=$( + go mod download -json "${MOD}@kubernetes-${VERSION}" | + sed -n 's|.*"Version": "\(.*\)".*|\1|p' + ) + go mod edit "-replace=${MOD}=${MOD}@${V}" +done +go get "k8s.io/kubernetes@v${VERSION}" +go mod tidy diff --git a/hack/update-openapi.sh b/hack/update-openapi.sh index 2db84ed5f6242..0250ed45b93ac 100755 --- a/hack/update-openapi.sh +++ b/hack/update-openapi.sh @@ -5,20 +5,30 @@ set -o errexit set -o nounset set -o pipefail -PROJECT_ROOT=$(cd $(dirname "$0")/.. ; pwd) +PROJECT_ROOT=$( + cd $(dirname "$0")/.. + pwd +) PATH="${PROJECT_ROOT}/dist:${PATH}" +GOPATH=$(go env GOPATH) +GOPATH_PROJECT_ROOT="${GOPATH}/src/github.com/argoproj/argo-cd" + VERSION="v1alpha1" - + [ -e ./v2 ] || ln -s . v2 +[ -e "${GOPATH_PROJECT_ROOT}" ] || (mkdir -p "$(dirname "${GOPATH_PROJECT_ROOT}")" && ln -s "${PROJECT_ROOT}" "${GOPATH_PROJECT_ROOT}") + openapi-gen \ --go-header-file ${PROJECT_ROOT}/hack/custom-boilerplate.go.txt \ --input-dirs github.com/argoproj/argo-cd/v2/pkg/apis/application/${VERSION} \ --output-package github.com/argoproj/argo-cd/v2/pkg/apis/application/${VERSION} \ --report-filename pkg/apis/api-rules/violation_exceptions.list \ + --output-base "${GOPATH}/src" \ $@ -[ -e ./v2 ] && rm -rf v2 + +[ -L "${GOPATH_PROJECT_ROOT}" ] && rm -rf "${GOPATH_PROJECT_ROOT}" +[ -L ./v2 ] && rm -rf v2 export GO111MODULE=on -go build -o ./dist/gen-crd-spec ${PROJECT_ROOT}/hack/gen-crd-spec +go build -o ./dist/gen-crd-spec "${PROJECT_ROOT}/hack/gen-crd-spec" ./dist/gen-crd-spec - diff --git a/hack/update-ssh-known-hosts.sh b/hack/update-ssh-known-hosts.sh index aa74c6489addd..5f2bedeb5b8ff 100755 --- a/hack/update-ssh-known-hosts.sh +++ b/hack/update-ssh-known-hosts.sh @@ -3,22 +3,33 @@ set -e KNOWN_HOSTS_FILE=$(dirname "$0")/ssh_known_hosts -HEADER="# This file was automatically generated. DO NOT EDIT" +HEADER="# This file was automatically generated by hack/update-ssh-known-hosts.sh. DO NOT EDIT" echo "$HEADER" > $KNOWN_HOSTS_FILE -ssh-keyscan github.com gitlab.com bitbucket.org ssh.dev.azure.com vs-ssh.visualstudio.com | sort -u >> $KNOWN_HOSTS_FILE +{ \ + ssh-keyscan github.com gitlab.com bitbucket.org ssh.dev.azure.com vs-ssh.visualstudio.com && \ + ssh-keyscan -p 443 ssh.github.com ; \ +} | sort -u >> $KNOWN_HOSTS_FILE chmod 0644 $KNOWN_HOSTS_FILE # Public SSH keys can be verified at the following URLs: # - github.com: https://help.github.com/articles/github-s-ssh-key-fingerprints/ +# - ssh.github.com: https://docs.github.com/en/authentication/troubleshooting-ssh/using-ssh-over-the-https-port#updating-known-hosts # - gitlab.com: https://docs.gitlab.com/ee/user/gitlab_com/#ssh-host-keys-fingerprints # - bitbucket.org: https://confluence.atlassian.com/bitbucket/ssh-keys-935365775.html # - ssh.dev.azure.com, vs-ssh.visualstudio.com: https://docs.microsoft.com/en-us/azure/devops/repos/git/use-ssh-keys-to-authenticate?view=azure-devops diff - <(ssh-keygen -l -f $KNOWN_HOSTS_FILE | sort -k 3) < /dev/null || exit 1 + line=$(yq '.jobs["test-e2e"].strategy.matrix["k3s-version"][]' .github/workflows/ci-build.yaml | \ + jq --arg minor_version "$minor_version" --raw-input --slurp --raw-output \ + 'split("\n")[:-1] | map(sub("\\.[0-9]+$"; "")) | join(", ") | "| \($minor_version) | \(.) |"') + out+="$line\n" +done + +git checkout "release-$argocd_minor_version" + + +printf "$out" > docs/operator-manual/tested-kubernetes-versions.md diff --git a/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml b/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml new file mode 100644 index 0000000000000..68dd75de2f47f --- /dev/null +++ b/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml @@ -0,0 +1,252 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: argocd-application-controller + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: application-controller + name: argocd-application-controller +spec: + selector: + matchLabels: + app.kubernetes.io/name: argocd-application-controller + replicas: 1 + template: + metadata: + labels: + app.kubernetes.io/name: argocd-application-controller + spec: + containers: + - args: + - /usr/local/bin/argocd-application-controller + env: + - name: ARGOCD_RECONCILIATION_TIMEOUT + valueFrom: + configMapKeyRef: + name: argocd-cm + key: timeout.reconciliation + optional: true + - name: ARGOCD_HARD_RECONCILIATION_TIMEOUT + valueFrom: + configMapKeyRef: + name: argocd-cm + key: timeout.hard.reconciliation + optional: true + - name: ARGOCD_RECONCILIATION_JITTER + valueFrom: + configMapKeyRef: + key: timeout.reconciliation.jitter + name: argocd-cm + optional: true + - name: ARGOCD_REPO_ERROR_GRACE_PERIOD_SECONDS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.error.grace.period.seconds + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: repo.server + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.server.timeout.seconds + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_STATUS_PROCESSORS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.status.processors + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OPERATION_PROCESSORS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.operation.processors + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_LOGFORMAT + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.log.format + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_LOGLEVEL + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.log.level + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_METRICS_CACHE_EXPIRATION + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.metrics.cache.expiration + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.self.heal.timeout.seconds + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.server.plaintext + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.server.strict.tls + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.resource.health.persist + optional: true + - name: ARGOCD_APP_STATE_CACHE_EXPIRATION + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.app.state.cache.expiration + optional: true + - name: REDIS_SERVER + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: redis.server + optional: true + - name: REDIS_COMPRESSION + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: redis.compression + optional: true + - name: REDISDB + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: redis.db + optional: true + - name: ARGOCD_DEFAULT_CACHE_EXPIRATION + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.default.cache.expiration + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.address + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.insecure + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.headers + optional: true + - name: ARGOCD_APPLICATION_NAMESPACES + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: application.namespaces + optional: true + - name: ARGOCD_CONTROLLER_SHARDING_ALGORITHM + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.sharding.algorithm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.kubectl.parallelism.limit + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_MAX + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.k8sclient.retry.max + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.k8sclient.retry.base.backoff + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.diff.server.side + optional: true + image: quay.io/argoproj/argocd:latest + imagePullPolicy: Always + name: argocd-application-controller + ports: + - containerPort: 8082 + readinessProbe: + httpGet: + path: /healthz + port: 8082 + initialDelaySeconds: 5 + periodSeconds: 10 + securityContext: + runAsNonRoot: true + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + workingDir: /home/argocd + volumeMounts: + - name: argocd-repo-server-tls + mountPath: /app/config/controller/tls + - name: argocd-home + mountPath: /home/argocd + serviceAccountName: argocd-application-controller + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/name: argocd-application-controller + topologyKey: kubernetes.io/hostname + - weight: 5 + podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/part-of: argocd + topologyKey: kubernetes.io/hostname + volumes: + - emptyDir: {} + name: argocd-home + - name: argocd-repo-server-tls + secret: + secretName: argocd-repo-server-tls + optional: true + items: + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key + - key: ca.crt + path: ca.crt diff --git a/manifests/base/application-controller-deployment/argocd-application-controller-service.yaml b/manifests/base/application-controller-deployment/argocd-application-controller-service.yaml new file mode 100644 index 0000000000000..a769e75468483 --- /dev/null +++ b/manifests/base/application-controller-deployment/argocd-application-controller-service.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: argocd-application-controller + app.kubernetes.io/name: argocd-application-controller + app.kubernetes.io/part-of: argocd + name: argocd-application-controller +spec: + ports: + - name: application-controller + protocol: TCP + port: 8082 + targetPort: 8082 + - name: metrics + protocol: TCP + port: 8084 + targetPort: 8084 + selector: + app.kubernetes.io/name: argocd-application-controller \ No newline at end of file diff --git a/manifests/base/application-controller-deployment/argocd-application-controller-statefulset.yaml b/manifests/base/application-controller-deployment/argocd-application-controller-statefulset.yaml new file mode 100644 index 0000000000000..10e4ea2ac7e3e --- /dev/null +++ b/manifests/base/application-controller-deployment/argocd-application-controller-statefulset.yaml @@ -0,0 +1,15 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: argocd-application-controller +spec: + replicas: 0 + template: + spec: + containers: + - name: argocd-application-controller + args: + - /usr/local/bin/argocd-application-controller + env: + - name: ARGOCD_CONTROLLER_REPLICAS + value: "0" \ No newline at end of file diff --git a/manifests/base/application-controller-deployment/kustomization.yaml b/manifests/base/application-controller-deployment/kustomization.yaml new file mode 100644 index 0000000000000..733a378e013e0 --- /dev/null +++ b/manifests/base/application-controller-deployment/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../application-controller-roles +- argocd-application-controller-service.yaml +- argocd-application-controller-statefulset.yaml +- argocd-application-controller-deployment.yaml + diff --git a/manifests/base/application-controller/argocd-application-controller-role.yaml b/manifests/base/application-controller-roles/argocd-application-controller-role.yaml similarity index 87% rename from manifests/base/application-controller/argocd-application-controller-role.yaml rename to manifests/base/application-controller-roles/argocd-application-controller-role.yaml index 27e0bc7bfe9cb..a672268eb1dd9 100644 --- a/manifests/base/application-controller/argocd-application-controller-role.yaml +++ b/manifests/base/application-controller-roles/argocd-application-controller-role.yaml @@ -36,3 +36,11 @@ rules: verbs: - create - list +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch diff --git a/manifests/base/application-controller/argocd-application-controller-rolebinding.yaml b/manifests/base/application-controller-roles/argocd-application-controller-rolebinding.yaml similarity index 100% rename from manifests/base/application-controller/argocd-application-controller-rolebinding.yaml rename to manifests/base/application-controller-roles/argocd-application-controller-rolebinding.yaml diff --git a/manifests/base/application-controller/argocd-application-controller-sa.yaml b/manifests/base/application-controller-roles/argocd-application-controller-sa.yaml similarity index 100% rename from manifests/base/application-controller/argocd-application-controller-sa.yaml rename to manifests/base/application-controller-roles/argocd-application-controller-sa.yaml diff --git a/manifests/base/application-controller-roles/kustomization.yaml b/manifests/base/application-controller-roles/kustomization.yaml new file mode 100644 index 0000000000000..f834d2ef3dbc4 --- /dev/null +++ b/manifests/base/application-controller-roles/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- argocd-application-controller-sa.yaml +- argocd-application-controller-role.yaml +- argocd-application-controller-rolebinding.yaml diff --git a/manifests/base/application-controller/argocd-application-controller-statefulset.yaml b/manifests/base/application-controller/argocd-application-controller-statefulset.yaml index d0c9ed68d7f1a..d974edffdd618 100644 --- a/manifests/base/application-controller/argocd-application-controller-statefulset.yaml +++ b/manifests/base/application-controller/argocd-application-controller-statefulset.yaml @@ -18,8 +18,8 @@ spec: app.kubernetes.io/name: argocd-application-controller spec: containers: - - command: - - argocd-application-controller + - args: + - /usr/local/bin/argocd-application-controller env: - name: ARGOCD_CONTROLLER_REPLICAS value: "1" @@ -35,24 +35,36 @@ spec: name: argocd-cm key: timeout.hard.reconciliation optional: true + - name: ARGOCD_RECONCILIATION_JITTER + valueFrom: + configMapKeyRef: + key: timeout.reconciliation.jitter + name: argocd-cm + optional: true + - name: ARGOCD_REPO_ERROR_GRACE_PERIOD_SECONDS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.error.grace.period.seconds + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: repo.server - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: repo.server + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.repo.server.timeout.seconds - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.server.timeout.seconds + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_STATUS_PROCESSORS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.status.processors - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.status.processors + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_OPERATION_PROCESSORS valueFrom: configMapKeyRef: @@ -79,22 +91,22 @@ spec: optional: true - name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.self.heal.timeout.seconds - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.self.heal.timeout.seconds + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.repo.server.plaintext - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.server.plaintext + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.repo.server.strict.tls - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.server.strict.tls + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH valueFrom: configMapKeyRef: @@ -103,16 +115,16 @@ spec: optional: true - name: ARGOCD_APP_STATE_CACHE_EXPIRATION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.app.state.cache.expiration - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.app.state.cache.expiration + optional: true - name: REDIS_SERVER valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: redis.server - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: redis.server + optional: true - name: REDIS_COMPRESSION valueFrom: configMapKeyRef: @@ -121,28 +133,70 @@ spec: optional: true - name: REDISDB valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: redis.db - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: redis.db + optional: true - name: ARGOCD_DEFAULT_CACHE_EXPIRATION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.default.cache.expiration - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.default.cache.expiration + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: otlp.address - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.address + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.insecure + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.headers + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: application.namespaces - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: application.namespaces + optional: true + - name: ARGOCD_CONTROLLER_SHARDING_ALGORITHM + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.sharding.algorithm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.kubectl.parallelism.limit + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_MAX + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.k8sclient.retry.max + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.k8sclient.retry.base.backoff + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.diff.server.side + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller diff --git a/manifests/base/application-controller/kustomization.yaml b/manifests/base/application-controller/kustomization.yaml index 9a801ad877bd2..616977fb9b08b 100644 --- a/manifests/base/application-controller/kustomization.yaml +++ b/manifests/base/application-controller/kustomization.yaml @@ -2,9 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- argocd-application-controller-sa.yaml -- argocd-application-controller-role.yaml -- argocd-application-controller-rolebinding.yaml +- ../application-controller-roles - argocd-application-controller-statefulset.yaml - argocd-metrics.yaml - argocd-application-controller-network-policy.yaml \ No newline at end of file diff --git a/manifests/base/applicationset-controller/argocd-applicationset-controller-deployment.yaml b/manifests/base/applicationset-controller/argocd-applicationset-controller-deployment.yaml index da67ac8433e84..b24124ccb833f 100644 --- a/manifests/base/applicationset-controller/argocd-applicationset-controller-deployment.yaml +++ b/manifests/base/applicationset-controller/argocd-applicationset-controller-deployment.yaml @@ -3,8 +3,8 @@ kind: Deployment metadata: labels: app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset - app.kubernetes.io/component: controller + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: applicationset-controller name: argocd-applicationset-controller spec: selector: @@ -16,114 +16,192 @@ spec: app.kubernetes.io/name: argocd-applicationset-controller spec: containers: - - command: - - entrypoint.sh - - argocd-applicationset-controller + - args: + - /usr/local/bin/argocd-applicationset-controller image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-applicationset-controller ports: - - containerPort: 7000 - name: webhook - - containerPort: 8080 - name: metrics + - containerPort: 7000 + name: webhook + - containerPort: 8080 + name: metrics env: - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_LEADER_ELECTION - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.enable.leader.election - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACE - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.namespace - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER - valueFrom: - configMapKeyRef: - key: repo.server - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_POLICY - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.policy - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.debug - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_LOGFORMAT - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.log.format - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_LOGLEVEL - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.log.level - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_DRY_RUN - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.dryrun - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_GIT_MODULES_ENABLED - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.enable.git.submodule - name: argocd-cmd-params-cm - optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_ROLLOUTS - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.enable.progressive.rollouts - name: argocd-cmd-params-cm - optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_ANNOTATIONS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.global.preserved.annotations + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_LABELS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.global.preserved.labels + name: argocd-cmd-params-cm + optional: true + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_LEADER_ELECTION + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.leader.election + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER + valueFrom: + configMapKeyRef: + key: repo.server + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_POLICY + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.policy + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_POLICY_OVERRIDE + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.policy.override + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.debug + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_LOGFORMAT + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.log.format + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_LOGLEVEL + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.log.level + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_DRY_RUN + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.dryrun + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_GIT_MODULES_ENABLED + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.git.submodule + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.progressive.syncs + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.new.git.file.globbing + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_PLAINTEXT + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: applicationsetcontroller.repo.server.plaintext + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_STRICT_TLS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: applicationsetcontroller.repo.server.strict.tls + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: applicationsetcontroller.repo.server.timeout.seconds + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_CONCURRENT_RECONCILIATIONS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: applicationsetcontroller.concurrent.reconciliations.max + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.namespaces + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.scm.root.ca.path + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: applicationsetcontroller.allowed.scm.providers + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: applicationsetcontroller.enable.scm.providers + optional: true volumeMounts: - - mountPath: /app/config/ssh - name: ssh-known-hosts - - mountPath: /app/config/tls - name: tls-certs - - mountPath: /app/config/gpg/source - name: gpg-keys - - mountPath: /app/config/gpg/keys - name: gpg-keyring - - mountPath: /tmp - name: tmp + - mountPath: /app/config/ssh + name: ssh-known-hosts + - mountPath: /app/config/tls + name: tls-certs + - mountPath: /app/config/gpg/source + name: gpg-keys + - mountPath: /app/config/gpg/keys + name: gpg-keyring + - mountPath: /tmp + name: tmp + - name: argocd-repo-server-tls + mountPath: /app/config/reposerver/tls securityContext: capabilities: drop: - - ALL + - ALL allowPrivilegeEscalation: false - readOnlyRootFilesystem: true + readOnlyRootFilesystem: true runAsNonRoot: true seccompProfile: type: RuntimeDefault serviceAccountName: argocd-applicationset-controller volumes: - - configMap: - name: argocd-ssh-known-hosts-cm - name: ssh-known-hosts - - configMap: - name: argocd-tls-certs-cm - name: tls-certs - - configMap: - name: argocd-gpg-keys-cm - name: gpg-keys - - emptyDir: {} - name: gpg-keyring - - emptyDir: {} - name: tmp + - configMap: + name: argocd-ssh-known-hosts-cm + name: ssh-known-hosts + - configMap: + name: argocd-tls-certs-cm + name: tls-certs + - configMap: + name: argocd-gpg-keys-cm + name: gpg-keys + - emptyDir: { } + name: gpg-keyring + - emptyDir: { } + name: tmp + - name: argocd-repo-server-tls + secret: + secretName: argocd-repo-server-tls + optional: true + items: + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key + - key: ca.crt + path: ca.crt diff --git a/manifests/base/applicationset-controller/argocd-applicationset-controller-role.yaml b/manifests/base/applicationset-controller/argocd-applicationset-controller-role.yaml index 0e729346ada5e..47c7ac9805a50 100644 --- a/manifests/base/applicationset-controller/argocd-applicationset-controller-role.yaml +++ b/manifests/base/applicationset-controller/argocd-applicationset-controller-role.yaml @@ -3,8 +3,8 @@ kind: Role metadata: labels: app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset - app.kubernetes.io/component: controller + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: applicationset-controller name: argocd-applicationset-controller rules: - apiGroups: diff --git a/manifests/base/applicationset-controller/argocd-applicationset-controller-rolebinding.yaml b/manifests/base/applicationset-controller/argocd-applicationset-controller-rolebinding.yaml index 4d647ee29246c..90bc99bbbaa8a 100644 --- a/manifests/base/applicationset-controller/argocd-applicationset-controller-rolebinding.yaml +++ b/manifests/base/applicationset-controller/argocd-applicationset-controller-rolebinding.yaml @@ -3,8 +3,8 @@ kind: RoleBinding metadata: labels: app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset - app.kubernetes.io/component: controller + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: applicationset-controller name: argocd-applicationset-controller roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/manifests/base/applicationset-controller/argocd-applicationset-controller-sa.yaml b/manifests/base/applicationset-controller/argocd-applicationset-controller-sa.yaml index 0366d09ce05b2..89c6a85fc7c7b 100644 --- a/manifests/base/applicationset-controller/argocd-applicationset-controller-sa.yaml +++ b/manifests/base/applicationset-controller/argocd-applicationset-controller-sa.yaml @@ -4,6 +4,6 @@ kind: ServiceAccount metadata: labels: app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset - app.kubernetes.io/component: controller + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: applicationset-controller name: argocd-applicationset-controller \ No newline at end of file diff --git a/manifests/base/applicationset-controller/argocd-applicationset-controller-service.yaml b/manifests/base/applicationset-controller/argocd-applicationset-controller-service.yaml index 6ce17e46e1c9b..48a5fcd040f1e 100644 --- a/manifests/base/applicationset-controller/argocd-applicationset-controller-service.yaml +++ b/manifests/base/applicationset-controller/argocd-applicationset-controller-service.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: Service metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller spec: ports: diff --git a/manifests/base/config/argocd-ssh-known-hosts-cm.yaml b/manifests/base/config/argocd-ssh-known-hosts-cm.yaml index 9c810567b2654..0f30fa5671662 100644 --- a/manifests/base/config/argocd-ssh-known-hosts-cm.yaml +++ b/manifests/base/config/argocd-ssh-known-hosts-cm.yaml @@ -7,12 +7,18 @@ metadata: name: argocd-ssh-known-hosts-cm data: ssh_known_hosts: | - bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw== - github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + # This file was automatically generated by hack/update-ssh-known-hosts.sh. DO NOT EDIT + [ssh.github.com]:443 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + [ssh.github.com]:443 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + [ssh.github.com]:443 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + bitbucket.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIQmuzMBuKdWeF4+a2sjSSpBK0iqitSQ+5BM9KhpexuGt20JpTVM7u5BDZngncgrqDMbWdxMWWOGtZ9UgbqgZE= + bitbucket.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIazEu89wgQZ4bqs3d63QSMzYVa0MuJ2e2gKTKqu+UUO + bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQeJzhupRu0u0cdegZIa8e86EG2qOCsIsD1Xw0xSeiPDlCr7kq97NLmMbpKTX6Esc30NuoqEEHCuc7yWtwp8dI76EEEB1VqY9QJq6vk+aySyboD5QF61I/1WeTwu+deCbgKMGbUijeXhtfbxSxm6JwGrXrhBdofTsbKRUsrN1WoNgUa8uqN1Vx6WAJw1JHPhglEGGHea6QICwJOAr/6mrui/oB7pkaWKHj3z7d1IC4KWLtY47elvjbaTlkN04Kc/5LFEirorGYVbt15kAUlqGM65pk6ZBxtaO3+30LVlORZkxOh+LKL/BvbZ/iRNhItLqNyieoQj/uh/7Iv4uyH/cV/0b4WDSd3DptigWq84lJubb9t/DnZlrJazxyDCulTmKdOR7vs9gMTo+uoIrPSb8ScTtvw65+odKAlBj59dhnVp9zd7QUojOpXlL62Aw56U4oO+FALuevvMjiWeavKhJqlR7i5n9srYcrNV7ttmDw7kf/97P5zauIhxcjX+xHv4M= + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H - github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= - github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl \ No newline at end of file diff --git a/manifests/base/dex/argocd-dex-server-deployment.yaml b/manifests/base/dex/argocd-dex-server-deployment.yaml index dd2d37fea62e8..7ff5985f44a90 100644 --- a/manifests/base/dex/argocd-dex-server-deployment.yaml +++ b/manifests/base/dex/argocd-dex-server-deployment.yaml @@ -20,7 +20,7 @@ spec: - name: copyutil image: quay.io/argoproj/argocd:latest imagePullPolicy: Always - command: [cp, -n, /usr/local/bin/argocd, /shared/argocd-dex] + command: [/bin/cp, -n, /usr/local/bin/argocd, /shared/argocd-dex] volumeMounts: - mountPath: /shared name: static-files @@ -37,7 +37,7 @@ spec: type: RuntimeDefault containers: - name: dex - image: ghcr.io/dexidp/dex:v2.35.3 + image: ghcr.io/dexidp/dex:v2.38.0 imagePullPolicy: Always command: [/shared/argocd-dex, rundex] env: diff --git a/manifests/base/dex/argocd-dex-server-service.yaml b/manifests/base/dex/argocd-dex-server-service.yaml index 7aeabca1e674e..cffbf006ae624 100644 --- a/manifests/base/dex/argocd-dex-server-service.yaml +++ b/manifests/base/dex/argocd-dex-server-service.yaml @@ -9,6 +9,7 @@ metadata: spec: ports: - name: http + appProtocol: TCP protocol: TCP port: 5556 targetPort: 5556 diff --git a/manifests/base/notification/argocd-notifications-cm.yaml b/manifests/base/notification/argocd-notifications-cm.yaml index 8139efd7e701a..c022fa50ded35 100644 --- a/manifests/base/notification/argocd-notifications-cm.yaml +++ b/manifests/base/notification/argocd-notifications-cm.yaml @@ -1,4 +1,8 @@ apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-cm diff --git a/manifests/base/notification/argocd-notifications-controller-deployment.yaml b/manifests/base/notification/argocd-notifications-controller-deployment.yaml index 94d01e7343f8a..876a207c16e42 100644 --- a/manifests/base/notification/argocd-notifications-controller-deployment.yaml +++ b/manifests/base/notification/argocd-notifications-controller-deployment.yaml @@ -1,6 +1,10 @@ apiVersion: apps/v1 kind: Deployment metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller spec: strategy: @@ -29,8 +33,33 @@ spec: - key: ca.crt path: ca.crt containers: - - command: - - argocd-notifications + - args: + - /usr/local/bin/argocd-notifications + env: + - name: ARGOCD_NOTIFICATIONS_CONTROLLER_LOGFORMAT + valueFrom: + configMapKeyRef: + key: notificationscontroller.log.format + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_NOTIFICATIONS_CONTROLLER_LOGLEVEL + valueFrom: + configMapKeyRef: + key: notificationscontroller.log.level + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_NAMESPACES + valueFrom: + configMapKeyRef: + key: application.namespaces + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED + valueFrom: + configMapKeyRef: + key: notificationscontroller.selfservice.enabled + name: argocd-cmd-params-cm + optional: true workingDir: /app livenessProbe: tcpSocket: diff --git a/manifests/base/notification/argocd-notifications-controller-metrics-service.yaml b/manifests/base/notification/argocd-notifications-controller-metrics-service.yaml index c5ec6160cb310..2126735c7d274 100644 --- a/manifests/base/notification/argocd-notifications-controller-metrics-service.yaml +++ b/manifests/base/notification/argocd-notifications-controller-metrics-service.yaml @@ -2,7 +2,9 @@ apiVersion: v1 kind: Service metadata: labels: + app.kubernetes.io/component: notifications-controller app.kubernetes.io/name: argocd-notifications-controller-metrics + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller-metrics spec: ports: diff --git a/manifests/base/notification/argocd-notifications-controller-network-policy.yaml b/manifests/base/notification/argocd-notifications-controller-network-policy.yaml index d7bb5d0679c2a..54a820c8b3243 100644 --- a/manifests/base/notification/argocd-notifications-controller-network-policy.yaml +++ b/manifests/base/notification/argocd-notifications-controller-network-policy.yaml @@ -1,6 +1,10 @@ apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller-network-policy spec: podSelector: diff --git a/manifests/base/notification/argocd-notifications-controller-role.yaml b/manifests/base/notification/argocd-notifications-controller-role.yaml index 9d0ff3b78ac74..11d561f4292ee 100644 --- a/manifests/base/notification/argocd-notifications-controller-role.yaml +++ b/manifests/base/notification/argocd-notifications-controller-role.yaml @@ -1,6 +1,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller rules: - apiGroups: diff --git a/manifests/base/notification/argocd-notifications-controller-rolebinding.yaml b/manifests/base/notification/argocd-notifications-controller-rolebinding.yaml index 0cacec4fd2820..d10904b3daa75 100644 --- a/manifests/base/notification/argocd-notifications-controller-rolebinding.yaml +++ b/manifests/base/notification/argocd-notifications-controller-rolebinding.yaml @@ -1,6 +1,10 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/manifests/base/notification/argocd-notifications-secret.yaml b/manifests/base/notification/argocd-notifications-secret.yaml index a58f865becffa..fc54cfc18adfa 100644 --- a/manifests/base/notification/argocd-notifications-secret.yaml +++ b/manifests/base/notification/argocd-notifications-secret.yaml @@ -1,5 +1,9 @@ apiVersion: v1 kind: Secret metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-secret type: Opaque diff --git a/manifests/base/redis/argocd-redis-deployment.yaml b/manifests/base/redis/argocd-redis-deployment.yaml index 02c18192a76ed..6fc776785185f 100644 --- a/manifests/base/redis/argocd-redis-deployment.yaml +++ b/manifests/base/redis/argocd-redis-deployment.yaml @@ -23,7 +23,7 @@ spec: serviceAccountName: argocd-redis containers: - name: redis - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: Always args: - "--save" @@ -33,6 +33,7 @@ spec: ports: - containerPort: 6379 securityContext: + readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: diff --git a/manifests/base/redis/argocd-redis-rolebinding.yaml b/manifests/base/redis/argocd-redis-rolebinding.yaml deleted file mode 100644 index 87caaa2cd6f57..0000000000000 --- a/manifests/base/redis/argocd-redis-rolebinding.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - app.kubernetes.io/component: redis - app.kubernetes.io/name: argocd-redis - app.kubernetes.io/part-of: argocd - name: argocd-redis -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: argocd-redis -subjects: -- kind: ServiceAccount - name: argocd-redis diff --git a/manifests/base/redis/kustomization.yaml b/manifests/base/redis/kustomization.yaml index 71a6ec42240db..4a0b64c4da6a8 100644 --- a/manifests/base/redis/kustomization.yaml +++ b/manifests/base/redis/kustomization.yaml @@ -3,16 +3,6 @@ kind: Kustomization resources: - argocd-redis-deployment.yaml -- argocd-redis-rolebinding.yaml - argocd-redis-sa.yaml - argocd-redis-service.yaml - argocd-redis-network-policy.yaml - -vars: -- name: ARGOCD_REDIS_SERVICE - objref: - kind: Service - name: argocd-redis - apiVersion: v1 - fieldref: - fieldpath: metadata.name \ No newline at end of file diff --git a/manifests/base/repo-server/argocd-repo-server-deployment.yaml b/manifests/base/repo-server/argocd-repo-server-deployment.yaml index 4966ff9e65ae8..2c30c8ad1d71b 100644 --- a/manifests/base/repo-server/argocd-repo-server-deployment.yaml +++ b/manifests/base/repo-server/argocd-repo-server-deployment.yaml @@ -21,7 +21,8 @@ spec: - name: argocd-repo-server image: quay.io/argoproj/argocd:latest imagePullPolicy: Always - command: [ "sh", "-c", "entrypoint.sh argocd-repo-server --redis $(ARGOCD_REDIS_SERVICE):6379"] + args: + - /usr/local/bin/argocd-repo-server env: - name: ARGOCD_RECONCILIATION_TIMEOUT valueFrom: @@ -47,6 +48,18 @@ spec: name: argocd-cmd-params-cm key: reposerver.parallelism.limit optional: true + - name: ARGOCD_REPO_SERVER_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: reposerver.listen.address + optional: true + - name: ARGOCD_REPO_SERVER_LISTEN_METRICS_ADDRESS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: reposerver.metrics.listen.address + optional: true - name: ARGOCD_REPO_SERVER_DISABLE_TLS valueFrom: configMapKeyRef: @@ -107,6 +120,18 @@ spec: name: argocd-cmd-params-cm key: otlp.address optional: true + - name: ARGOCD_REPO_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.insecure + optional: true + - name: ARGOCD_REPO_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.headers + optional: true - name: ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE valueFrom: configMapKeyRef: @@ -137,12 +162,42 @@ spec: key: reposerver.streamed.manifest.max.extracted.size name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE + valueFrom: + configMapKeyRef: + key: reposerver.helm.manifest.max.extracted.size + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: reposerver.disable.helm.manifest.max.extracted.size + optional: true + - name: ARGOCD_REVISION_CACHE_LOCK_TIMEOUT + valueFrom: + configMapKeyRef: + key: reposerver.revision.cache.lock.timeout + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_GIT_MODULES_ENABLED valueFrom: configMapKeyRef: key: reposerver.enable.git.submodule name: argocd-cmd-params-cm optional: true + - name: ARGOCD_GIT_LS_REMOTE_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + key: reposerver.git.lsremote.parallelism.limit + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_GIT_REQUEST_TIMEOUT + valueFrom: + configMapKeyRef: + key: reposerver.git.request.timeout + name: argocd-cmd-params-cm + optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME @@ -194,7 +249,7 @@ spec: name: plugins initContainers: - command: - - cp + - /bin/cp - -n - /usr/local/bin/argocd - /var/run/argocd/argocd-cmp-server diff --git a/manifests/base/repo-server/argocd-repo-server-network-policy.yaml b/manifests/base/repo-server/argocd-repo-server-network-policy.yaml index 2b3fb72b557b5..267dfa81adc49 100644 --- a/manifests/base/repo-server/argocd-repo-server-network-policy.yaml +++ b/manifests/base/repo-server/argocd-repo-server-network-policy.yaml @@ -7,18 +7,21 @@ spec: matchLabels: app.kubernetes.io/name: argocd-repo-server policyTypes: - - Ingress + - Ingress ingress: - from: - - podSelector: - matchLabels: - app.kubernetes.io/name: argocd-server - - podSelector: - matchLabels: - app.kubernetes.io/name: argocd-application-controller - - podSelector: - matchLabels: - app.kubernetes.io/name: argocd-notifications-controller + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-server + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-application-controller + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-notifications-controller + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-applicationset-controller ports: - protocol: TCP port: 8081 diff --git a/manifests/base/server/argocd-server-deployment.yaml b/manifests/base/server/argocd-server-deployment.yaml index b5a9e405bd4d0..0ebeb70e08531 100644 --- a/manifests/base/server/argocd-server-deployment.yaml +++ b/manifests/base/server/argocd-server-deployment.yaml @@ -20,140 +20,141 @@ spec: - name: argocd-server image: quay.io/argoproj/argocd:latest imagePullPolicy: Always - command: [argocd-server] + args: + - /usr/local/bin/argocd-server env: - name: ARGOCD_SERVER_INSECURE valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.insecure - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.insecure + optional: true - name: ARGOCD_SERVER_BASEHREF valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.basehref - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.basehref + optional: true - name: ARGOCD_SERVER_ROOTPATH valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.rootpath - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.rootpath + optional: true - name: ARGOCD_SERVER_LOGFORMAT valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.log.format - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.log.format + optional: true - name: ARGOCD_SERVER_LOG_LEVEL valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.log.level - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.log.level + optional: true - name: ARGOCD_SERVER_REPO_SERVER valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: repo.server - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: repo.server + optional: true - name: ARGOCD_SERVER_DEX_SERVER valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.dex.server - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.dex.server + optional: true - name: ARGOCD_SERVER_DISABLE_AUTH valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.disable.auth - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.disable.auth + optional: true - name: ARGOCD_SERVER_ENABLE_GZIP valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.enable.gzip - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.enable.gzip + optional: true - name: ARGOCD_SERVER_REPO_SERVER_TIMEOUT_SECONDS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.repo.server.timeout.seconds - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.repo.server.timeout.seconds + optional: true - name: ARGOCD_SERVER_X_FRAME_OPTIONS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.x.frame.options - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.x.frame.options + optional: true - name: ARGOCD_SERVER_CONTENT_SECURITY_POLICY valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.content.security.policy - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.content.security.policy + optional: true - name: ARGOCD_SERVER_REPO_SERVER_PLAINTEXT valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.repo.server.plaintext - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.repo.server.plaintext + optional: true - name: ARGOCD_SERVER_REPO_SERVER_STRICT_TLS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.repo.server.strict.tls - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.repo.server.strict.tls + optional: true - name: ARGOCD_SERVER_DEX_SERVER_PLAINTEXT valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.dex.server.plaintext - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.dex.server.plaintext + optional: true - name: ARGOCD_SERVER_DEX_SERVER_STRICT_TLS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.dex.server.strict.tls - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.dex.server.strict.tls + optional: true - name: ARGOCD_TLS_MIN_VERSION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.tls.minversion - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.tls.minversion + optional: true - name: ARGOCD_TLS_MAX_VERSION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.tls.maxversion - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.tls.maxversion + optional: true - name: ARGOCD_TLS_CIPHERS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.tls.ciphers - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.tls.ciphers + optional: true - name: ARGOCD_SERVER_CONNECTION_STATUS_CACHE_EXPIRATION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.connection.status.cache.expiration - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.connection.status.cache.expiration + optional: true - name: ARGOCD_SERVER_OIDC_CACHE_EXPIRATION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.oidc.cache.expiration - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.oidc.cache.expiration + optional: true - name: ARGOCD_SERVER_LOGIN_ATTEMPTS_EXPIRATION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.login.attempts.expiration - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.login.attempts.expiration + optional: true - name: ARGOCD_SERVER_STATIC_ASSETS valueFrom: configMapKeyRef: @@ -162,16 +163,16 @@ spec: optional: true - name: ARGOCD_APP_STATE_CACHE_EXPIRATION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.app.state.cache.expiration - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.app.state.cache.expiration + optional: true - name: REDIS_SERVER valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: redis.server - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: redis.server + optional: true - name: REDIS_COMPRESSION valueFrom: configMapKeyRef: @@ -180,40 +181,82 @@ spec: optional: true - name: REDISDB valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: redis.db - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: redis.db + optional: true - name: ARGOCD_DEFAULT_CACHE_EXPIRATION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.default.cache.expiration - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.default.cache.expiration + optional: true - name: ARGOCD_MAX_COOKIE_NUMBER valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.http.cookie.maxnumber - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.http.cookie.maxnumber + optional: true + - name: ARGOCD_SERVER_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.listen.address + optional: true + - name: ARGOCD_SERVER_METRICS_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.metrics.listen.address + optional: true - name: ARGOCD_SERVER_OTLP_ADDRESS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: otlp.address - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.address + optional: true + - name: ARGOCD_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.insecure + optional: true + - name: ARGOCD_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.headers + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: application.namespaces - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: application.namespaces + optional: true - name: ARGOCD_SERVER_ENABLE_PROXY_EXTENSION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: server.enable.proxy.extension - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.enable.proxy.extension + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_MAX + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.k8sclient.retry.max + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.k8sclient.retry.base.backoff + optional: true + - name: ARGOCD_API_CONTENT_TYPES + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: server.api.content.types + optional: true volumeMounts: - name: ssh-known-hosts mountPath: /app/config/ssh diff --git a/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrole.yaml b/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrole.yaml new file mode 100644 index 0000000000000..259a48e7aee9e --- /dev/null +++ b/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrole.yaml @@ -0,0 +1,88 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: argocd-applicationset-controller + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: applicationset-controller + name: argocd-applicationset-controller +rules: +- apiGroups: + - argoproj.io + resources: + - applications + - applicationsets + - applicationsets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - argoproj.io + resources: + - applicationsets/status + verbs: + - get + - patch + - update +- apiGroups: + - argoproj.io + resources: + - appprojects + verbs: + - get +- apiGroups: + - "" + resources: + - events + verbs: + - create + - get + - list + - patch + - watch +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - update + - delete + - get + - list + - patch + - watch +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch +- apiGroups: + - apps + - extensions + resources: + - deployments + verbs: + - get + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrolebinding.yaml b/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrolebinding.yaml new file mode 100644 index 0000000000000..820f16f472e4e --- /dev/null +++ b/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrolebinding.yaml @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: argocd-applicationset-controller + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: applicationset-controller + name: argocd-applicationset-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: argocd-applicationset-controller +subjects: +- kind: ServiceAccount + name: argocd-applicationset-controller + namespace: argocd diff --git a/manifests/cluster-rbac/applicationset-controller/kustomization.yaml b/manifests/cluster-rbac/applicationset-controller/kustomization.yaml new file mode 100644 index 0000000000000..b8f18c57a14f7 --- /dev/null +++ b/manifests/cluster-rbac/applicationset-controller/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- argocd-applicationset-controller-clusterrole.yaml +- argocd-applicationset-controller-clusterrolebinding.yaml diff --git a/manifests/cluster-rbac/kustomization.yaml b/manifests/cluster-rbac/kustomization.yaml index 7f791905b661b..55e6e2d72df9e 100644 --- a/manifests/cluster-rbac/kustomization.yaml +++ b/manifests/cluster-rbac/kustomization.yaml @@ -3,4 +3,5 @@ kind: Kustomization resources: - ./application-controller +- ./applicationset-controller - ./server diff --git a/manifests/cluster-rbac/server/argocd-server-clusterrole.yaml b/manifests/cluster-rbac/server/argocd-server-clusterrole.yaml index 779e674a608c9..b33820950fcb6 100644 --- a/manifests/cluster-rbac/server/argocd-server-clusterrole.yaml +++ b/manifests/cluster-rbac/server/argocd-server-clusterrole.yaml @@ -32,7 +32,20 @@ rules: - "argoproj.io" resources: - "applications" + - "applicationsets" verbs: - get - list - watch +- apiGroups: + - batch + resources: + - jobs + verbs: + - create # supports triggering jobs from UI +- apiGroups: + - argoproj.io + resources: + - workflows + verbs: + - create # supports triggering workflows from UI diff --git a/manifests/core-install.yaml b/manifests/core-install.yaml index 304e5b2a0f06e..05f1deaad58fe 100644 --- a/manifests/core-install.yaml +++ b/manifests/core-install.yaml @@ -287,8 +287,15 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be + passed to helm template, defined as a map. This takes + precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -303,12 +310,22 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources for @@ -326,6 +343,10 @@ spec: definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether to + apply common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -334,6 +355,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -549,8 +624,15 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be + passed to helm template, defined as a map. This takes + precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -565,12 +647,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -589,6 +682,10 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -597,6 +694,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -718,7 +869,8 @@ spec: properties: name: description: Name is an alternate way of specifying the target - cluster by its symbolic name + cluster by its symbolic name. This must be set if Server is + not set. type: string namespace: description: Namespace specifies the target namespace for the @@ -726,8 +878,9 @@ spec: namespace-scoped resources that have not set a value for .metadata.namespace type: string server: - description: Server specifies the URL of the target cluster and - must be set to the Kubernetes control plane API + description: Server specifies the URL of the target cluster's + Kubernetes control plane API. This must be set if Name is not + set. type: string type: object ignoreDifferences: @@ -927,8 +1080,15 @@ spec: type: array values: description: Values specifies Helm values to be passed to - helm template, typically defined as a block + helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be passed + to helm template, defined as a map. This takes precedence + over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -943,12 +1103,22 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether to + apply env variables substitution for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize components + to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize apps @@ -965,6 +1135,10 @@ spec: definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether to apply + common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -973,6 +1147,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize adds + to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas override + specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -1180,8 +1408,15 @@ spec: type: array values: description: Values specifies Helm values to be passed to - helm template, typically defined as a block + helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be passed + to helm template, defined as a map. This takes precedence + over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -1196,12 +1431,22 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize components + to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize @@ -1219,6 +1464,10 @@ spec: definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether to apply + common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -1227,6 +1476,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas override + specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -1319,7 +1622,7 @@ spec: as part of automated sync (default: false)' type: boolean selfHeal: - description: 'SelfHeal specifes whether to revert resources + description: 'SelfHeal specifies whether to revert resources back to their desired state upon modification in the cluster (default: false)' type: boolean @@ -1383,7 +1686,7 @@ spec: conditions items: description: ApplicationCondition contains details about an application - condition, which is usally an error or warning + condition, which is usually an error or warning properties: lastTransitionTime: description: LastTransitionTime is the time the condition was @@ -1402,6 +1705,10 @@ spec: - type type: object type: array + controllerNamespace: + description: ControllerNamespace indicates the namespace in which + the application controller is located + type: string health: description: Health contains information about the application's current health status @@ -1435,6 +1742,19 @@ spec: description: ID is an auto incrementing identifier of the RevisionHistory format: int64 type: integer + initiatedBy: + description: InitiatedBy contains information about who initiated + the operations + properties: + automated: + description: Automated is set to true if operation was initiated + automatically by the application controller. + type: boolean + username: + description: Username contains the name of a user who started + operation + type: string + type: object revision: description: Revision holds the revision the sync was performed against @@ -1581,8 +1901,15 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be + passed to helm template, defined as a map. This takes + precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -1597,12 +1924,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -1621,6 +1959,10 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -1629,6 +1971,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -1846,8 +2242,16 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. + ValuesObject takes precedence over Values, so use + one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to + be passed to helm template, defined as a map. This + takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -1862,12 +2266,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -1886,6 +2301,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -1894,6 +2314,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -2256,8 +2730,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined as - a block + a block. ValuesObject takes precedence over + Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as a + map. This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -2272,12 +2753,24 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution + for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before + building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations @@ -2296,6 +2789,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors + or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -2304,14 +2802,68 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string - version: - description: Version controls which version of - Kustomize to use for rendering manifests + namespace: + description: Namespace sets the namespace that + Kustomize adds to all resources type: string - type: object - path: - description: Path is a directory path within the Git - repository, and is only valid for applications sourced + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array + version: + description: Version controls which version of + Kustomize to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git + repository, and is only valid for applications sourced from Git. type: string plugin: @@ -2537,8 +3089,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined - as a block + as a block. ValuesObject takes precedence + over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as + a map. This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -2555,12 +3114,24 @@ spec: additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution + for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of + kustomize components to add to the kustomization + before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations @@ -2579,6 +3150,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies + whether to apply common labels to resource + selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -2587,6 +3163,61 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that + Kustomize adds to all resources + type: string + patches: + description: Patches is a list of Kustomize + patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize + Replicas override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -2720,6 +3351,19 @@ spec: syncResult: description: SyncResult is the result of a Sync operation properties: + managedNamespaceMetadata: + description: ManagedNamespaceMetadata contains the current + sync state of managed namespace metadata + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object resources: description: Resources contains a list of sync result items for each individual resource in a sync operation @@ -2922,8 +3566,16 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. + ValuesObject takes precedence over Values, so use + one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to + be passed to helm template, defined as a map. This + takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -2938,12 +3590,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -2962,6 +3625,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -2970,6 +3638,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -3198,8 +3920,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined as - a block + a block. ValuesObject takes precedence over Values, + so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as a map. + This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -3214,12 +3943,24 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution for + annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before + building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -3238,6 +3979,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -3246,6 +3992,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -3428,7 +4228,8 @@ spec: properties: name: description: Name is an alternate way of specifying the - target cluster by its symbolic name + target cluster by its symbolic name. This must be set + if Server is not set. type: string namespace: description: Namespace specifies the target namespace @@ -3437,10 +4238,47 @@ spec: not set a value for .metadata.namespace type: string server: - description: Server specifies the URL of the target cluster - and must be set to the Kubernetes control plane API + description: Server specifies the URL of the target cluster's + Kubernetes control plane API. This must be set if Name + is not set. type: string type: object + ignoreDifferences: + description: IgnoreDifferences is a reference to the application's + ignored differences used for comparison + items: + description: ResourceIgnoreDifferences contains resource + filter and list of json paths which should be ignored + during comparison with live state. + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + description: ManagedFieldsManagers is a list of trusted + managers. Fields mutated by those managers will take + precedence over the desired state defined in the SCM + and won't be displayed in diffs + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array source: description: Source is a reference to the application's source used for comparison @@ -3579,8 +4417,16 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. + ValuesObject takes precedence over Values, so use + one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to + be passed to helm template, defined as a map. This + takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -3595,12 +4441,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -3619,6 +4476,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -3627,6 +4489,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -3855,8 +4771,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined as - a block + a block. ValuesObject takes precedence over Values, + so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as a map. + This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -3871,12 +4794,24 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution for + annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before + building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -3895,6 +4830,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -3903,33 +4843,87 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string - version: - description: Version controls which version of Kustomize - to use for rendering manifests + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources type: string - type: object - path: - description: Path is a directory path within the Git - repository, and is only valid for applications sourced - from Git. - type: string - plugin: - description: Plugin holds config management plugin specific - options - properties: - env: - description: Env is a list of environment variable - entries + patches: + description: Patches is a list of Kustomize patches items: - description: EnvEntry represents an entry in the - application's environment properties: - name: - description: Name is the name of the variable, - usually expressed in uppercase + options: + additionalProperties: + type: boolean + type: object + patch: type: string - value: - description: Value is the value of the variable + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array + version: + description: Version controls which version of Kustomize + to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git + repository, and is only valid for applications sourced + from Git. + type: string + plugin: + description: Plugin holds config management plugin specific + options + properties: + env: + description: Env is a list of environment variable + entries + items: + description: EnvEntry represents an entry in the + application's environment + properties: + name: + description: Name is the name of the variable, + usually expressed in uppercase + type: string + value: + description: Value is the value of the variable type: string required: - name @@ -4043,6 +5037,8 @@ spec: type: object spec: properties: + applyNestedSelectors: + type: boolean generators: items: properties: @@ -4238,6 +5234,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4247,10 +5246,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4259,10 +5264,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4396,6 +5450,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4405,10 +5462,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4417,10 +5480,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4713,6 +5825,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4722,10 +5837,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4734,10 +5855,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4871,6 +6041,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4880,10 +6053,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4892,10 +6071,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5192,6 +6420,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5201,10 +6432,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5213,10 +6450,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5350,6 +6636,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5359,10 +6648,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5371,25 +6666,74 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string - version: + namespace: type: string - type: object - path: - type: string - plugin: - properties: - env: + patches: items: properties: - name: + options: + additionalProperties: + type: boolean + type: object + patch: type: string - value: + path: type: string - required: + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: - name - value type: object @@ -5475,6 +6819,10 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object required: - repoURL - revision @@ -5485,6 +6833,8 @@ spec: items: x-kubernetes-preserve-unknown-fields: true type: array + elementsYaml: + type: string template: properties: metadata: @@ -5645,6 +6995,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5654,10 +7007,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5666,10 +7025,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5803,6 +7211,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5812,10 +7223,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5824,10 +7241,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5928,8 +7394,6 @@ spec: - metadata - spec type: object - required: - - elements type: object matrix: properties: @@ -6128,6 +7592,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6137,10 +7604,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6149,10 +7622,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -6286,6 +7808,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6295,10 +7820,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6307,10 +7838,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -6603,6 +8183,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6612,10 +8195,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6624,10 +8213,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -6761,6 +8399,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6770,10 +8411,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6782,10 +8429,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7082,6 +8778,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7091,10 +8790,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7103,10 +8808,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7240,6 +8994,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7249,10 +9006,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7261,10 +9024,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7365,6 +9177,10 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object required: - repoURL - revision @@ -7375,6 +9191,8 @@ spec: items: x-kubernetes-preserve-unknown-fields: true type: array + elementsYaml: + type: string template: properties: metadata: @@ -7535,6 +9353,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7544,10 +9365,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7556,10 +9383,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7693,6 +9569,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7702,10 +9581,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7714,10 +9599,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7818,153 +9752,49 @@ spec: - metadata - spec type: object - required: - - elements type: object matrix: x-kubernetes-preserve-unknown-fields: true merge: x-kubernetes-preserve-unknown-fields: true - pullRequest: + plugin: properties: - bitbucketServer: + configMapRef: properties: - api: - type: string - basicAuth: - properties: - passwordRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - username: - type: string - required: - - passwordRef - - username - type: object - project: - type: string - repo: + name: type: string required: - - api - - project - - repo + - name type: object - filters: - items: - properties: - branchMatch: - type: string - type: object - type: array - gitea: + input: properties: - api: - type: string - insecure: - type: boolean - owner: - type: string - repo: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName + parameters: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true type: object - required: - - api - - owner - - repo type: object - github: + requeueAfterSeconds: + format: int64 + type: integer + template: properties: - api: - type: string - appSecretName: - type: string - labels: - items: - type: string - type: array - owner: - type: string - repo: - type: string - tokenRef: + metadata: properties: - key: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - owner - - repo - type: object - gitlab: - properties: - api: - type: string - labels: - items: - type: string - type: array - project: - type: string - pullRequestState: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - project - type: object - requeueAfterSeconds: - format: int64 - type: integer - template: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: + namespace: type: string type: object spec: @@ -8106,6 +9936,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8115,10 +9948,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8127,10 +9966,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -8264,6 +10152,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8273,10 +10164,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8285,10 +10182,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -8389,12 +10335,30 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object + required: + - configMapRef type: object - scmProvider: + pullRequest: properties: - azureDevOps: + azuredevops: properties: - accessTokenRef: + api: + type: string + labels: + items: + type: string + type: array + organization: + type: string + project: + type: string + repo: + type: string + tokenRef: properties: key: type: string @@ -8404,46 +10368,58 @@ spec: - key - secretName type: object - allBranches: - type: boolean - api: - type: string - organization: - type: string - teamProject: - type: string required: - - accessTokenRef - organization - - teamProject + - project + - repo type: object bitbucket: properties: - allBranches: - type: boolean - appPasswordRef: + api: + type: string + basicAuth: properties: - key: - type: string - secretName: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: type: string required: - - key - - secretName + - passwordRef + - username + type: object + bearerToken: + properties: + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - tokenRef type: object owner: type: string - user: + repo: type: string required: - - appPasswordRef - owner - - user + - repo type: object bitbucketServer: properties: - allBranches: - type: boolean api: type: string basicAuth: @@ -8466,41 +10442,32 @@ spec: type: object project: type: string + repo: + type: string required: - api - project + - repo type: object - cloneProtocol: - type: string filters: items: properties: branchMatch: type: string - labelMatch: - type: string - pathsDoNotExist: - items: - type: string - type: array - pathsExist: - items: - type: string - type: array - repositoryMatch: + targetBranchMatch: type: string type: object type: array gitea: properties: - allBranches: - type: boolean api: type: string insecure: type: boolean owner: type: string + repo: + type: string tokenRef: properties: key: @@ -8514,16 +10481,21 @@ spec: required: - api - owner + - repo type: object github: properties: - allBranches: - type: boolean api: type: string appSecretName: type: string - organization: + labels: + items: + type: string + type: array + owner: + type: string + repo: type: string tokenRef: properties: @@ -8536,18 +10508,23 @@ spec: - secretName type: object required: - - organization + - owner + - repo type: object gitlab: properties: - allBranches: - type: boolean api: type: string - group: - type: string - includeSubgroups: + insecure: type: boolean + labels: + items: + type: string + type: array + project: + type: string + pullRequestState: + type: string tokenRef: properties: key: @@ -8559,7 +10536,7 @@ spec: - secretName type: object required: - - group + - project type: object requeueAfterSeconds: format: int64 @@ -8724,6 +10701,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8733,10 +10713,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8745,23 +10731,72 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string - version: + namespace: type: string - type: object - path: - type: string - plugin: - properties: - env: + patches: items: properties: - name: - type: string - value: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: type: string required: - name @@ -8882,6 +10917,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8891,10 +10929,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8903,10 +10947,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -9008,511 +11101,203 @@ spec: - spec type: object type: object - selector: + scmProvider: properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - type: array - template: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - destination: - properties: - name: - type: string - namespace: - type: string - server: - type: string - type: object - ignoreDifferences: - items: + awsCodeCommit: properties: - group: + allBranches: + type: boolean + region: type: string - jqPathExpressions: - items: - type: string - type: array - jsonPointers: - items: - type: string - type: array - kind: + role: type: string - managedFieldsManagers: + tagFilters: items: - type: string + properties: + key: + type: string + value: + type: string + required: + - key + type: object type: array - name: + type: object + azureDevOps: + properties: + accessTokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + allBranches: + type: boolean + api: type: string - namespace: + organization: + type: string + teamProject: type: string required: - - kind + - accessTokenRef + - organization + - teamProject type: object - type: array - info: - items: + bitbucket: properties: - name: + allBranches: + type: boolean + appPasswordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + owner: type: string - value: + user: type: string required: - - name - - value + - appPasswordRef + - owner + - user type: object - type: array - project: - type: string - revisionHistoryLimit: - format: int64 - type: integer - source: - properties: - chart: - type: string - directory: + bitbucketServer: + properties: + allBranches: + type: boolean + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + project: + type: string + required: + - api + - project + type: object + cloneProtocol: + type: string + filters: + items: properties: - exclude: + branchMatch: type: string - include: + labelMatch: type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean - type: object - helm: - properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: - type: boolean - valueFiles: + pathsDoNotExist: items: type: string type: array - values: - type: string - version: - type: string - type: object - kustomize: - properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: - type: boolean - images: + pathsExist: items: type: string type: array - namePrefix: - type: string - nameSuffix: - type: string - version: - type: string - type: object - path: - type: string - plugin: - properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - name: + repositoryMatch: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array type: object - ref: - type: string - repoURL: - type: string - targetRevision: - type: string - required: - - repoURL - type: object - sources: - items: + type: array + gitea: properties: - chart: + allBranches: + type: boolean + api: type: string - directory: + insecure: + type: boolean + owner: + type: string + tokenRef: properties: - exclude: + key: type: string - include: + secretName: type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean + required: + - key + - secretName type: object - helm: + required: + - api + - owner + type: object + github: + properties: + allBranches: + type: boolean + api: + type: string + appSecretName: + type: string + organization: + type: string + tokenRef: properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: - type: boolean - valueFiles: - items: - type: string - type: array - values: + key: type: string - version: + secretName: type: string + required: + - key + - secretName type: object - kustomize: - properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: - type: boolean - images: - items: - type: string - type: array - namePrefix: - type: string - nameSuffix: - type: string - version: - type: string - type: object - path: + required: + - organization + type: object + gitlab: + properties: + allBranches: + type: boolean + api: type: string - plugin: + group: + type: string + includeSharedProjects: + type: boolean + includeSubgroups: + type: boolean + insecure: + type: boolean + tokenRef: properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - name: + key: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array + secretName: + type: string + required: + - key + - secretName type: object - ref: - type: string - repoURL: - type: string - targetRevision: + topic: type: string required: - - repoURL - type: object - type: array - syncPolicy: - properties: - automated: - properties: - allowEmpty: - type: boolean - prune: - type: boolean - selfHeal: - type: boolean - type: object - managedNamespaceMetadata: - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - retry: - properties: - backoff: - properties: - duration: - type: string - factor: - format: int64 - type: integer - maxDuration: - type: string - type: object - limit: - format: int64 - type: integer - type: object - syncOptions: - items: - type: string - type: array - type: object - required: - - destination - - project - type: object - required: - - metadata - - spec - type: object - required: - - generators - type: object - merge: - properties: - generators: - items: - properties: - clusterDecisionResource: - properties: - configMapRef: - type: string - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object + - group type: object - name: - type: string requeueAfterSeconds: format: int64 type: integer @@ -9676,6 +11461,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -9685,10 +11473,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -9697,10 +11491,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -9834,6 +11677,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -9843,10 +11689,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -9855,10 +11707,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -9963,34 +11864,2392 @@ spec: additionalProperties: type: string type: object - required: - - configMapRef type: object - clusters: + selector: properties: - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + type: object + type: array + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: items: type: string type: array - required: - - key - - operator + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array type: object - type: array - matchLabels: - additionalProperties: + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + required: + - generators + type: object + merge: + properties: + generators: + items: + properties: + clusterDecisionResource: + properties: + configMapRef: + type: string + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + name: + type: string + requeueAfterSeconds: + format: int64 + type: integer + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + required: + - configMapRef + type: object + clusters: + properties: + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + type: object + git: + properties: + directories: + items: + properties: + exclude: + type: boolean + path: + type: string + required: + - path + type: object + type: array + files: + items: + properties: + path: + type: string + required: + - path + type: object + type: array + pathParamPrefix: + type: string + repoURL: + type: string + requeueAfterSeconds: + format: int64 + type: integer + revision: + type: string + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string type: object + required: + - repoURL + - revision + type: object + list: + properties: + elements: + items: + x-kubernetes-preserve-unknown-fields: true + type: array + elementsYaml: + type: string template: properties: metadata: @@ -10151,6 +14410,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10160,10 +14422,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10172,10 +14440,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10309,6 +14626,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10318,10 +14638,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10330,10 +14656,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10434,42 +14809,30 @@ spec: - metadata - spec type: object - values: - additionalProperties: - type: string - type: object type: object - git: + matrix: + x-kubernetes-preserve-unknown-fields: true + merge: + x-kubernetes-preserve-unknown-fields: true + plugin: properties: - directories: - items: - properties: - exclude: - type: boolean - path: - type: string - required: - - path - type: object - type: array - files: - items: - properties: - path: - type: string - required: - - path - type: object - type: array - pathParamPrefix: - type: string - repoURL: - type: string + configMapRef: + properties: + name: + type: string + required: + - name + type: object + input: + properties: + parameters: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + type: object + type: object requeueAfterSeconds: format: int64 type: integer - revision: - type: string template: properties: metadata: @@ -10630,6 +14993,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10639,10 +15005,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10651,10 +15023,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10788,6 +15209,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10797,10 +15221,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10809,10 +15239,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10906,23 +15385,219 @@ spec: type: array type: object required: - - destination - - project + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + required: + - configMapRef + type: object + pullRequest: + properties: + azuredevops: + properties: + api: + type: string + labels: + items: + type: string + type: array + organization: + type: string + project: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - organization + - project + - repo + type: object + bitbucket: + properties: + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + bearerToken: + properties: + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - tokenRef + type: object + owner: + type: string + repo: + type: string + required: + - owner + - repo + type: object + bitbucketServer: + properties: + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + project: + type: string + repo: + type: string + required: + - api + - project + - repo + type: object + filters: + items: + properties: + branchMatch: + type: string + targetBranchMatch: + type: string + type: object + type: array + gitea: + properties: + api: + type: string + insecure: + type: boolean + owner: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName type: object required: - - metadata - - spec + - api + - owner + - repo type: object - required: - - repoURL - - revision - type: object - list: - properties: - elements: - items: - x-kubernetes-preserve-unknown-fields: true - type: array + github: + properties: + api: + type: string + appSecretName: + type: string + labels: + items: + type: string + type: array + owner: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - owner + - repo + type: object + gitlab: + properties: + api: + type: string + insecure: + type: boolean + labels: + items: + type: string + type: array + project: + type: string + pullRequestState: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - project + type: object + requeueAfterSeconds: + format: int64 + type: integer template: properties: metadata: @@ -11083,6 +15758,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -11092,10 +15770,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -11104,10 +15788,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -11241,6 +15974,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -11250,10 +15986,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -11262,10 +16004,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -11366,17 +16157,81 @@ spec: - metadata - spec type: object - required: - - elements type: object - matrix: - x-kubernetes-preserve-unknown-fields: true - merge: - x-kubernetes-preserve-unknown-fields: true - pullRequest: + scmProvider: properties: + awsCodeCommit: + properties: + allBranches: + type: boolean + region: + type: string + role: + type: string + tagFilters: + items: + properties: + key: + type: string + value: + type: string + required: + - key + type: object + type: array + type: object + azureDevOps: + properties: + accessTokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + allBranches: + type: boolean + api: + type: string + organization: + type: string + teamProject: + type: string + required: + - accessTokenRef + - organization + - teamProject + type: object + bitbucket: + properties: + allBranches: + type: boolean + appPasswordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + owner: + type: string + user: + type: string + required: + - appPasswordRef + - owner + - user + type: object bitbucketServer: properties: + allBranches: + type: boolean api: type: string basicAuth: @@ -11399,30 +16254,41 @@ spec: type: object project: type: string - repo: - type: string required: - api - project - - repo type: object + cloneProtocol: + type: string filters: items: properties: branchMatch: type: string + labelMatch: + type: string + pathsDoNotExist: + items: + type: string + type: array + pathsExist: + items: + type: string + type: array + repositoryMatch: + type: string type: object type: array gitea: properties: + allBranches: + type: boolean api: type: string insecure: type: boolean owner: type: string - repo: - type: string tokenRef: properties: key: @@ -11436,21 +16302,16 @@ spec: required: - api - owner - - repo type: object github: properties: + allBranches: + type: boolean api: type: string - appSecretName: - type: string - labels: - items: - type: string - type: array - owner: - type: string - repo: + appSecretName: + type: string + organization: type: string tokenRef: properties: @@ -11463,21 +16324,22 @@ spec: - secretName type: object required: - - owner - - repo + - organization type: object gitlab: properties: + allBranches: + type: boolean api: type: string - labels: - items: - type: string - type: array - project: - type: string - pullRequestState: + group: type: string + includeSharedProjects: + type: boolean + includeSubgroups: + type: boolean + insecure: + type: boolean tokenRef: properties: key: @@ -11488,8 +16350,10 @@ spec: - key - secretName type: object + topic: + type: string required: - - project + - group type: object requeueAfterSeconds: format: int64 @@ -11654,6 +16518,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -11663,10 +16530,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -11675,10 +16548,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -11812,6 +16734,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -11821,10 +16746,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -11833,10 +16764,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -11937,654 +16917,622 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object type: object - scmProvider: + selector: properties: - azureDevOps: - properties: - accessTokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - allBranches: - type: boolean - api: - type: string - organization: - type: string - teamProject: - type: string - required: - - accessTokenRef - - organization - - teamProject - type: object - bitbucket: - properties: - allBranches: - type: boolean - appPasswordRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - owner: - type: string - user: - type: string - required: - - appPasswordRef - - owner - - user - type: object - bitbucketServer: - properties: - allBranches: - type: boolean - api: - type: string - basicAuth: - properties: - passwordRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - username: - type: string - required: - - passwordRef - - username - type: object - project: - type: string - required: - - api - - project - type: object - cloneProtocol: - type: string - filters: + matchExpressions: items: properties: - branchMatch: + key: type: string - labelMatch: + operator: type: string - pathsDoNotExist: - items: - type: string - type: array - pathsExist: + values: items: type: string type: array - repositoryMatch: - type: string + required: + - key + - operator type: object type: array - gitea: - properties: - allBranches: - type: boolean - api: - type: string - insecure: - type: boolean - owner: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - api - - owner + matchLabels: + additionalProperties: + type: string type: object - github: + type: object + type: object + type: array + mergeKeys: + items: + type: string + type: array + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: properties: - allBranches: - type: boolean - api: + group: type: string - appSecretName: + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: type: string - organization: + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object required: - - organization + - kind type: object - gitlab: + type: array + info: + items: properties: - allBranches: - type: boolean - api: + name: type: string - group: + value: type: string - includeSubgroups: - type: boolean - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object required: - - group + - name + - value type: object - requeueAfterSeconds: - format: int64 - type: integer - template: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - destination: - properties: - name: - type: string - namespace: - type: string - server: - type: string - type: object - ignoreDifferences: - items: - properties: - group: - type: string - jqPathExpressions: - items: - type: string - type: array - jsonPointers: - items: - type: string - type: array - kind: - type: string - managedFieldsManagers: - items: - type: string - type: array - name: - type: string - namespace: - type: string - required: - - kind - type: object - type: array - info: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - project: - type: string - revisionHistoryLimit: - format: int64 - type: integer - source: - properties: - chart: - type: string - directory: - properties: - exclude: - type: string - include: - type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean - type: object - helm: + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: + code: type: boolean - valueFiles: - items: - type: string - type: array - values: + name: type: string - version: + value: type: string + required: + - name + - value type: object - kustomize: + type: array + libs: + items: + type: string + type: array + tlas: + items: properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: + code: type: boolean - images: - items: - type: string - type: array - namePrefix: - type: string - nameSuffix: + name: type: string - version: + value: type: string + required: + - name + - value type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string path: type: string - plugin: + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string name: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array + namespace: + type: string + version: + type: string type: object - ref: + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: type: string - repoURL: + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: type: string - targetRevision: + value: type: string required: - - repoURL + - name + - value type: object - sources: - items: - properties: - chart: + type: array + name: + type: string + parameters: + items: + properties: + array: + items: type: string - directory: - properties: - exclude: - type: string - include: - type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean - type: object - helm: + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: + code: type: boolean - valueFiles: - items: - type: string - type: array - values: + name: type: string - version: + value: type: string + required: + - name + - value type: object - kustomize: + type: array + libs: + items: + type: string + type: array + tlas: + items: properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: + code: type: boolean - images: - items: - type: string - type: array - namePrefix: - type: string - nameSuffix: + name: type: string - version: + value: type: string + required: + - name + - value type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string path: type: string - plugin: + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string name: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array + namespace: + type: string + version: + type: string type: object - ref: - type: string - repoURL: - type: string - targetRevision: + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: type: string required: - - repoURL + - count + - name type: object type: array - syncPolicy: - properties: - automated: - properties: - allowEmpty: - type: boolean - prune: - type: boolean - selfHeal: - type: boolean - type: object - managedNamespaceMetadata: - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - retry: - properties: - backoff: - properties: - duration: - type: string - factor: - format: int64 - type: integer - maxDuration: - type: string - type: object - limit: - format: int64 - type: integer - type: object - syncOptions: - items: + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: type: string - type: array - type: object - required: - - destination - - project + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string required: - - metadata - - spec + - repoURL type: object - type: object - selector: - properties: - matchExpressions: - items: + type: array + syncPolicy: + properties: + automated: properties: - key: - type: string - operator: - type: string - values: - items: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: type: string - type: array - required: - - key - - operator + type: object + labels: + additionalProperties: + type: string + type: object type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - type: array - mergeKeys: - items: - type: string - type: array + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + required: + - generators + - mergeKeys + type: object + plugin: + properties: + configMapRef: + properties: + name: + type: string + required: + - name + type: object + input: + properties: + parameters: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + requeueAfterSeconds: + format: int64 + type: integer template: properties: metadata: @@ -12745,6 +17693,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -12754,10 +17705,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -12766,10 +17723,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -12903,6 +17909,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -12912,10 +17921,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -12924,10 +17939,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -13021,19 +18085,96 @@ spec: type: array type: object required: - - destination - - project + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + required: + - configMapRef + type: object + pullRequest: + properties: + azuredevops: + properties: + api: + type: string + labels: + items: + type: string + type: array + organization: + type: string + project: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - organization + - project + - repo + type: object + bitbucket: + properties: + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + bearerToken: + properties: + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - tokenRef type: object + owner: + type: string + repo: + type: string required: - - metadata - - spec + - owner + - repo type: object - required: - - generators - - mergeKeys - type: object - pullRequest: - properties: bitbucketServer: properties: api: @@ -13070,6 +18211,8 @@ spec: properties: branchMatch: type: string + targetBranchMatch: + type: string type: object type: array gitea: @@ -13129,6 +18272,8 @@ spec: properties: api: type: string + insecure: + type: boolean labels: items: type: string @@ -13313,6 +18458,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -13322,10 +18470,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -13334,10 +18488,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -13471,6 +18674,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -13480,10 +18686,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -13492,10 +18704,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -13599,6 +18860,26 @@ spec: type: object scmProvider: properties: + awsCodeCommit: + properties: + allBranches: + type: boolean + region: + type: string + role: + type: string + tagFilters: + items: + properties: + key: + type: string + value: + type: string + required: + - key + type: object + type: array + type: object azureDevOps: properties: accessTokenRef: @@ -13753,8 +19034,12 @@ spec: type: string group: type: string + includeSharedProjects: + type: boolean includeSubgroups: type: boolean + insecure: + type: boolean tokenRef: properties: key: @@ -13765,6 +19050,8 @@ spec: - key - secretName type: object + topic: + type: string required: - group type: object @@ -13931,6 +19218,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -13940,10 +19230,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -13952,10 +19248,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -14089,6 +19434,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -14098,10 +19446,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -14110,10 +19464,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -14214,6 +19617,10 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object type: object selector: properties: @@ -14242,6 +19649,36 @@ spec: type: array goTemplate: type: boolean + goTemplateOptions: + items: + type: string + type: array + ignoreApplicationDifferences: + items: + properties: + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + name: + type: string + type: object + type: array + preservedFields: + properties: + annotations: + items: + type: string + type: array + labels: + items: + type: string + type: array + type: object strategy: properties: rollingSync: @@ -14275,6 +19712,13 @@ spec: type: object syncPolicy: properties: + applicationsSync: + enum: + - create-only + - create-update + - create-delete + - sync + type: string preserveResourcesOnDeletion: type: boolean type: object @@ -14438,6 +19882,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -14447,10 +19894,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -14459,10 +19912,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -14596,6 +20098,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -14605,10 +20110,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -14617,10 +20128,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -14721,6 +20281,8 @@ spec: - metadata - spec type: object + templatePatch: + type: string required: - generators - template @@ -14739,10 +20301,13 @@ spec: type: string status: type: string + step: + type: string required: - application - message - status + - step type: object type: array conditions: @@ -14866,7 +20431,8 @@ spec: properties: name: description: Name is an alternate way of specifying the target - cluster by its symbolic name + cluster by its symbolic name. This must be set if Server is + not set. type: string namespace: description: Namespace specifies the target namespace for the @@ -14874,8 +20440,9 @@ spec: namespace-scoped resources that have not set a value for .metadata.namespace type: string server: - description: Server specifies the URL of the target cluster - and must be set to the Kubernetes control plane API + description: Server specifies the URL of the target cluster's + Kubernetes control plane API. This must be set if Name is + not set. type: string type: object type: array @@ -15112,9 +20679,9 @@ apiVersion: v1 kind: ServiceAccount metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller --- apiVersion: v1 @@ -15173,14 +20740,22 @@ rules: verbs: - create - list +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller rules: - apiGroups: @@ -15280,9 +20855,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller roleRef: apiGroup: rbac.authorization.k8s.io @@ -15293,22 +20868,6 @@ subjects: name: argocd-applicationset-controller --- apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - app.kubernetes.io/component: redis - app.kubernetes.io/name: argocd-redis - app.kubernetes.io/part-of: argocd - name: argocd-redis -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: argocd-redis -subjects: -- kind: ServiceAccount - name: argocd-redis ---- -apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: @@ -15359,16 +20918,22 @@ metadata: --- apiVersion: v1 data: - ssh_known_hosts: |- - bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw== - github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + ssh_known_hosts: | + # This file was automatically generated by hack/update-ssh-known-hosts.sh. DO NOT EDIT + [ssh.github.com]:443 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + [ssh.github.com]:443 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + [ssh.github.com]:443 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + bitbucket.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIQmuzMBuKdWeF4+a2sjSSpBK0iqitSQ+5BM9KhpexuGt20JpTVM7u5BDZngncgrqDMbWdxMWWOGtZ9UgbqgZE= + bitbucket.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIazEu89wgQZ4bqs3d63QSMzYVa0MuJ2e2gKTKqu+UUO + bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQeJzhupRu0u0cdegZIa8e86EG2qOCsIsD1Xw0xSeiPDlCr7kq97NLmMbpKTX6Esc30NuoqEEHCuc7yWtwp8dI76EEEB1VqY9QJq6vk+aySyboD5QF61I/1WeTwu+deCbgKMGbUijeXhtfbxSxm6JwGrXrhBdofTsbKRUsrN1WoNgUa8uqN1Vx6WAJw1JHPhglEGGHea6QICwJOAr/6mrui/oB7pkaWKHj3z7d1IC4KWLtY47elvjbaTlkN04Kc/5LFEirorGYVbt15kAUlqGM65pk6ZBxtaO3+30LVlORZkxOh+LKL/BvbZ/iRNhItLqNyieoQj/uh/7Iv4uyH/cV/0b4WDSd3DptigWq84lJubb9t/DnZlrJazxyDCulTmKdOR7vs9gMTo+uoIrPSb8ScTtvw65+odKAlBj59dhnVp9zd7QUojOpXlL62Aw56U4oO+FALuevvMjiWeavKhJqlR7i5n9srYcrNV7ttmDw7kf/97P5zauIhxcjX+xHv4M= + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H - github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= - github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl kind: ConfigMap metadata: labels: @@ -15397,9 +20962,9 @@ apiVersion: v1 kind: Service metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller spec: ports: @@ -15472,9 +21037,9 @@ apiVersion: apps/v1 kind: Deployment metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller spec: selector: @@ -15486,10 +21051,21 @@ spec: app.kubernetes.io/name: argocd-applicationset-controller spec: containers: - - command: - - entrypoint.sh - - argocd-applicationset-controller + - args: + - /usr/local/bin/argocd-applicationset-controller env: + - name: ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_ANNOTATIONS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.global.preserved.annotations + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_LABELS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.global.preserved.labels + name: argocd-cmd-params-cm + optional: true - name: NAMESPACE valueFrom: fieldRef: @@ -15500,12 +21076,6 @@ spec: key: applicationsetcontroller.enable.leader.election name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACE - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.namespace - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER valueFrom: configMapKeyRef: @@ -15518,6 +21088,12 @@ spec: key: applicationsetcontroller.policy name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_POLICY_OVERRIDE + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.policy.override + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG valueFrom: configMapKeyRef: @@ -15548,10 +21124,64 @@ spec: key: applicationsetcontroller.enable.git.submodule name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_ROLLOUTS + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS valueFrom: configMapKeyRef: - key: applicationsetcontroller.enable.progressive.rollouts + key: applicationsetcontroller.enable.progressive.syncs + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.new.git.file.globbing + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_PLAINTEXT + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.plaintext + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_STRICT_TLS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.strict.tls + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.timeout.seconds + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_CONCURRENT_RECONCILIATIONS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.concurrent.reconciliations.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.namespaces + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.scm.root.ca.path + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.allowed.scm.providers + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.scm.providers name: argocd-cmd-params-cm optional: true image: quay.io/argoproj/argocd:latest @@ -15582,6 +21212,8 @@ spec: name: gpg-keyring - mountPath: /tmp name: tmp + - mountPath: /app/config/reposerver/tls + name: argocd-repo-server-tls serviceAccountName: argocd-applicationset-controller volumes: - configMap: @@ -15597,6 +21229,17 @@ spec: name: gpg-keyring - emptyDir: {} name: tmp + - name: argocd-repo-server-tls + secret: + items: + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key + - key: ca.crt + path: ca.crt + optional: true + secretName: argocd-repo-server-tls --- apiVersion: apps/v1 kind: Deployment @@ -15636,7 +21279,7 @@ spec: - "" - --appendonly - "no" - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: Always name: redis ports: @@ -15646,6 +21289,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true securityContext: runAsNonRoot: true runAsUser: 999 @@ -15687,10 +21331,8 @@ spec: weight: 5 automountServiceAccountToken: false containers: - - command: - - sh - - -c - - entrypoint.sh argocd-repo-server --redis argocd-redis:6379 + - args: + - /usr/local/bin/argocd-repo-server env: - name: ARGOCD_RECONCILIATION_TIMEOUT valueFrom: @@ -15716,6 +21358,18 @@ spec: key: reposerver.parallelism.limit name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: reposerver.listen.address + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_LISTEN_METRICS_ADDRESS + valueFrom: + configMapKeyRef: + key: reposerver.metrics.listen.address + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_DISABLE_TLS valueFrom: configMapKeyRef: @@ -15776,6 +21430,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE valueFrom: configMapKeyRef: @@ -15806,12 +21472,42 @@ spec: key: reposerver.streamed.manifest.max.extracted.size name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE + valueFrom: + configMapKeyRef: + key: reposerver.helm.manifest.max.extracted.size + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE + valueFrom: + configMapKeyRef: + key: reposerver.disable.helm.manifest.max.extracted.size + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REVISION_CACHE_LOCK_TIMEOUT + valueFrom: + configMapKeyRef: + key: reposerver.revision.cache.lock.timeout + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_GIT_MODULES_ENABLED valueFrom: configMapKeyRef: key: reposerver.enable.git.submodule name: argocd-cmd-params-cm optional: true + - name: ARGOCD_GIT_LS_REMOTE_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + key: reposerver.git.lsremote.parallelism.limit + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_GIT_REQUEST_TIMEOUT + valueFrom: + configMapKeyRef: + key: reposerver.git.request.timeout + name: argocd-cmd-params-cm + optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME @@ -15866,7 +21562,7 @@ spec: name: plugins initContainers: - command: - - cp + - /bin/cp - -n - /usr/local/bin/argocd - /var/run/argocd/argocd-cmp-server @@ -15952,8 +21648,8 @@ spec: topologyKey: kubernetes.io/hostname weight: 5 containers: - - command: - - argocd-application-controller + - args: + - /usr/local/bin/argocd-application-controller env: - name: ARGOCD_CONTROLLER_REPLICAS value: "1" @@ -15969,6 +21665,18 @@ spec: key: timeout.hard.reconciliation name: argocd-cm optional: true + - name: ARGOCD_RECONCILIATION_JITTER + valueFrom: + configMapKeyRef: + key: timeout.reconciliation.jitter + name: argocd-cm + optional: true + - name: ARGOCD_REPO_ERROR_GRACE_PERIOD_SECONDS + valueFrom: + configMapKeyRef: + key: controller.repo.error.grace.period.seconds + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER valueFrom: configMapKeyRef: @@ -16071,12 +21779,54 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: key: application.namespaces name: argocd-cmd-params-cm optional: true + - name: ARGOCD_CONTROLLER_SHARDING_ALGORITHM + valueFrom: + configMapKeyRef: + key: controller.sharding.algorithm + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + key: controller.kubectl.parallelism.limit + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_MAX + valueFrom: + configMapKeyRef: + key: controller.k8sclient.retry.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF + valueFrom: + configMapKeyRef: + key: controller.k8sclient.retry.base.backoff + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + key: controller.diff.server.side + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller @@ -16202,6 +21952,9 @@ spec: - podSelector: matchLabels: app.kubernetes.io/name: argocd-notifications-controller + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-applicationset-controller ports: - port: 8081 protocol: TCP diff --git a/manifests/crds/application-crd.yaml b/manifests/crds/application-crd.yaml index d19394e84f8fd..aaf1347f64dfb 100644 --- a/manifests/crds/application-crd.yaml +++ b/manifests/crds/application-crd.yaml @@ -286,8 +286,15 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be + passed to helm template, defined as a map. This takes + precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -302,12 +309,22 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources for @@ -325,6 +342,10 @@ spec: definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether to + apply common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -333,6 +354,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -548,8 +623,15 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be + passed to helm template, defined as a map. This takes + precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -564,12 +646,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -588,6 +681,10 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -596,6 +693,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -717,7 +868,8 @@ spec: properties: name: description: Name is an alternate way of specifying the target - cluster by its symbolic name + cluster by its symbolic name. This must be set if Server is + not set. type: string namespace: description: Namespace specifies the target namespace for the @@ -725,8 +877,9 @@ spec: namespace-scoped resources that have not set a value for .metadata.namespace type: string server: - description: Server specifies the URL of the target cluster and - must be set to the Kubernetes control plane API + description: Server specifies the URL of the target cluster's + Kubernetes control plane API. This must be set if Name is not + set. type: string type: object ignoreDifferences: @@ -926,8 +1079,15 @@ spec: type: array values: description: Values specifies Helm values to be passed to - helm template, typically defined as a block + helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be passed + to helm template, defined as a map. This takes precedence + over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -942,12 +1102,22 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether to + apply env variables substitution for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize components + to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize apps @@ -964,6 +1134,10 @@ spec: definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether to apply + common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -972,6 +1146,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize adds + to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas override + specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -1179,8 +1407,15 @@ spec: type: array values: description: Values specifies Helm values to be passed to - helm template, typically defined as a block + helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be passed + to helm template, defined as a map. This takes precedence + over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -1195,12 +1430,22 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize components + to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize @@ -1218,6 +1463,10 @@ spec: definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether to apply + common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -1226,6 +1475,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas override + specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -1318,7 +1621,7 @@ spec: as part of automated sync (default: false)' type: boolean selfHeal: - description: 'SelfHeal specifes whether to revert resources + description: 'SelfHeal specifies whether to revert resources back to their desired state upon modification in the cluster (default: false)' type: boolean @@ -1382,7 +1685,7 @@ spec: conditions items: description: ApplicationCondition contains details about an application - condition, which is usally an error or warning + condition, which is usually an error or warning properties: lastTransitionTime: description: LastTransitionTime is the time the condition was @@ -1401,6 +1704,10 @@ spec: - type type: object type: array + controllerNamespace: + description: ControllerNamespace indicates the namespace in which + the application controller is located + type: string health: description: Health contains information about the application's current health status @@ -1434,6 +1741,19 @@ spec: description: ID is an auto incrementing identifier of the RevisionHistory format: int64 type: integer + initiatedBy: + description: InitiatedBy contains information about who initiated + the operations + properties: + automated: + description: Automated is set to true if operation was initiated + automatically by the application controller. + type: boolean + username: + description: Username contains the name of a user who started + operation + type: string + type: object revision: description: Revision holds the revision the sync was performed against @@ -1580,8 +1900,15 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be + passed to helm template, defined as a map. This takes + precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -1596,12 +1923,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -1620,6 +1958,10 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -1628,6 +1970,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -1845,8 +2241,16 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. + ValuesObject takes precedence over Values, so use + one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to + be passed to helm template, defined as a map. This + takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -1861,12 +2265,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -1885,6 +2300,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -1893,6 +2313,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -2255,8 +2729,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined as - a block + a block. ValuesObject takes precedence over + Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as a + map. This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -2271,12 +2752,24 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution + for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before + building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations @@ -2295,6 +2788,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors + or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -2303,6 +2801,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that + Kustomize adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -2536,8 +3088,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined - as a block + as a block. ValuesObject takes precedence + over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as + a map. This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -2554,12 +3113,24 @@ spec: additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution + for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of + kustomize components to add to the kustomization + before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations @@ -2578,6 +3149,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies + whether to apply common labels to resource + selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -2586,6 +3162,61 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that + Kustomize adds to all resources + type: string + patches: + description: Patches is a list of Kustomize + patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize + Replicas override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -2719,6 +3350,19 @@ spec: syncResult: description: SyncResult is the result of a Sync operation properties: + managedNamespaceMetadata: + description: ManagedNamespaceMetadata contains the current + sync state of managed namespace metadata + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object resources: description: Resources contains a list of sync result items for each individual resource in a sync operation @@ -2921,8 +3565,16 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. + ValuesObject takes precedence over Values, so use + one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to + be passed to helm template, defined as a map. This + takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -2937,12 +3589,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -2961,6 +3624,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -2969,6 +3637,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -3197,8 +3919,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined as - a block + a block. ValuesObject takes precedence over Values, + so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as a map. + This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -3213,12 +3942,24 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution for + annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before + building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -3237,6 +3978,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -3245,6 +3991,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -3427,7 +4227,8 @@ spec: properties: name: description: Name is an alternate way of specifying the - target cluster by its symbolic name + target cluster by its symbolic name. This must be set + if Server is not set. type: string namespace: description: Namespace specifies the target namespace @@ -3436,10 +4237,47 @@ spec: not set a value for .metadata.namespace type: string server: - description: Server specifies the URL of the target cluster - and must be set to the Kubernetes control plane API + description: Server specifies the URL of the target cluster's + Kubernetes control plane API. This must be set if Name + is not set. type: string type: object + ignoreDifferences: + description: IgnoreDifferences is a reference to the application's + ignored differences used for comparison + items: + description: ResourceIgnoreDifferences contains resource + filter and list of json paths which should be ignored + during comparison with live state. + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + description: ManagedFieldsManagers is a list of trusted + managers. Fields mutated by those managers will take + precedence over the desired state defined in the SCM + and won't be displayed in diffs + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array source: description: Source is a reference to the application's source used for comparison @@ -3578,8 +4416,16 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. + ValuesObject takes precedence over Values, so use + one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to + be passed to helm template, defined as a map. This + takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -3594,12 +4440,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -3618,6 +4475,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -3626,6 +4488,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -3854,8 +4770,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined as - a block + a block. ValuesObject takes precedence over Values, + so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as a map. + This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -3870,12 +4793,24 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution for + annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before + building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -3894,6 +4829,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -3902,6 +4842,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests diff --git a/manifests/crds/applicationset-crd.yaml b/manifests/crds/applicationset-crd.yaml index 7f8c3e0251972..2668052f431a0 100644 --- a/manifests/crds/applicationset-crd.yaml +++ b/manifests/crds/applicationset-crd.yaml @@ -29,6 +29,8 @@ spec: type: object spec: properties: + applyNestedSelectors: + type: boolean generators: items: properties: @@ -224,6 +226,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -233,10 +238,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -245,10 +256,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -382,6 +442,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -391,10 +454,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -403,10 +472,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -699,6 +817,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -708,10 +829,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -720,10 +847,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -857,6 +1033,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -866,10 +1045,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -878,10 +1063,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -1178,6 +1412,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -1187,10 +1424,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -1199,10 +1442,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -1336,6 +1628,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -1345,10 +1640,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -1357,10 +1658,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -1461,6 +1811,10 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object required: - repoURL - revision @@ -1471,6 +1825,8 @@ spec: items: x-kubernetes-preserve-unknown-fields: true type: array + elementsYaml: + type: string template: properties: metadata: @@ -1631,6 +1987,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -1640,10 +1999,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -1652,36 +2017,85 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string - version: + namespace: type: string - type: object - path: - type: string - plugin: - properties: - env: + patches: items: properties: - name: + options: + additionalProperties: + type: boolean + type: object + patch: type: string - value: + path: type: string - required: - - name - - value - type: object - type: array - name: - type: string - parameters: - items: - properties: - array: - items: + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: type: string type: array map: @@ -1789,6 +2203,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -1798,10 +2215,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -1810,10 +2233,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -1914,8 +2386,6 @@ spec: - metadata - spec type: object - required: - - elements type: object matrix: properties: @@ -2114,6 +2584,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -2123,10 +2596,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -2135,10 +2614,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -2272,6 +2800,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -2281,10 +2812,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -2293,10 +2830,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -2589,6 +3175,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -2598,10 +3187,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -2610,10 +3205,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -2747,6 +3391,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -2756,10 +3403,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -2768,10 +3421,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -3068,6 +3770,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -3077,10 +3782,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -3089,36 +3800,85 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string - version: + namespace: type: string - type: object - path: - type: string - plugin: - properties: - env: + patches: items: properties: - name: + options: + additionalProperties: + type: boolean + type: object + patch: type: string - value: + path: type: string - required: - - name - - value - type: object - type: array - name: - type: string - parameters: - items: - properties: - array: - items: + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: type: string type: array map: @@ -3226,6 +3986,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -3235,10 +3998,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -3247,10 +4016,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -3351,6 +4169,10 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object required: - repoURL - revision @@ -3361,6 +4183,8 @@ spec: items: x-kubernetes-preserve-unknown-fields: true type: array + elementsYaml: + type: string template: properties: metadata: @@ -3521,6 +4345,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -3530,10 +4357,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -3542,10 +4375,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -3679,6 +4561,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -3688,10 +4573,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -3700,10 +4591,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -3804,153 +4744,49 @@ spec: - metadata - spec type: object - required: - - elements type: object matrix: x-kubernetes-preserve-unknown-fields: true merge: x-kubernetes-preserve-unknown-fields: true - pullRequest: + plugin: properties: - bitbucketServer: + configMapRef: properties: - api: - type: string - basicAuth: - properties: - passwordRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - username: - type: string - required: - - passwordRef - - username - type: object - project: - type: string - repo: + name: type: string required: - - api - - project - - repo + - name type: object - filters: - items: - properties: - branchMatch: - type: string - type: object - type: array - gitea: + input: properties: - api: - type: string - insecure: - type: boolean - owner: - type: string - repo: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName + parameters: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true type: object - required: - - api - - owner - - repo type: object - github: + requeueAfterSeconds: + format: int64 + type: integer + template: properties: - api: - type: string - appSecretName: - type: string - labels: - items: - type: string - type: array - owner: - type: string - repo: - type: string - tokenRef: + metadata: properties: - key: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - owner - - repo - type: object - gitlab: - properties: - api: - type: string - labels: - items: - type: string - type: array - project: - type: string - pullRequestState: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - project - type: object - requeueAfterSeconds: - format: int64 - type: integer - template: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: + namespace: type: string type: object spec: @@ -4092,6 +4928,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4101,10 +4940,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4113,10 +4958,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4250,6 +5144,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4259,10 +5156,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4271,10 +5174,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4375,12 +5327,30 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object + required: + - configMapRef type: object - scmProvider: + pullRequest: properties: - azureDevOps: + azuredevops: properties: - accessTokenRef: + api: + type: string + labels: + items: + type: string + type: array + organization: + type: string + project: + type: string + repo: + type: string + tokenRef: properties: key: type: string @@ -4390,46 +5360,58 @@ spec: - key - secretName type: object - allBranches: - type: boolean - api: - type: string - organization: - type: string - teamProject: - type: string required: - - accessTokenRef - organization - - teamProject + - project + - repo type: object bitbucket: properties: - allBranches: - type: boolean - appPasswordRef: + api: + type: string + basicAuth: properties: - key: - type: string - secretName: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: type: string required: - - key - - secretName + - passwordRef + - username + type: object + bearerToken: + properties: + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - tokenRef type: object owner: type: string - user: + repo: type: string required: - - appPasswordRef - owner - - user + - repo type: object bitbucketServer: properties: - allBranches: - type: boolean api: type: string basicAuth: @@ -4452,41 +5434,32 @@ spec: type: object project: type: string + repo: + type: string required: - api - project + - repo type: object - cloneProtocol: - type: string filters: items: properties: branchMatch: type: string - labelMatch: - type: string - pathsDoNotExist: - items: - type: string - type: array - pathsExist: - items: - type: string - type: array - repositoryMatch: + targetBranchMatch: type: string type: object type: array gitea: properties: - allBranches: - type: boolean api: type: string insecure: type: boolean owner: type: string + repo: + type: string tokenRef: properties: key: @@ -4500,16 +5473,21 @@ spec: required: - api - owner + - repo type: object github: properties: - allBranches: - type: boolean api: type: string appSecretName: type: string - organization: + labels: + items: + type: string + type: array + owner: + type: string + repo: type: string tokenRef: properties: @@ -4522,18 +5500,23 @@ spec: - secretName type: object required: - - organization + - owner + - repo type: object gitlab: properties: - allBranches: - type: boolean api: type: string - group: - type: string - includeSubgroups: + insecure: type: boolean + labels: + items: + type: string + type: array + project: + type: string + pullRequestState: + type: string tokenRef: properties: key: @@ -4545,7 +5528,7 @@ spec: - secretName type: object required: - - group + - project type: object requeueAfterSeconds: format: int64 @@ -4710,6 +5693,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4719,10 +5705,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4731,10 +5723,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4868,6 +5909,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4877,10 +5921,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4889,10 +5939,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4994,511 +6093,203 @@ spec: - spec type: object type: object - selector: + scmProvider: properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - type: array - template: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - destination: - properties: - name: - type: string - namespace: - type: string - server: - type: string - type: object - ignoreDifferences: - items: + awsCodeCommit: properties: - group: + allBranches: + type: boolean + region: type: string - jqPathExpressions: - items: - type: string - type: array - jsonPointers: - items: - type: string - type: array - kind: + role: type: string - managedFieldsManagers: + tagFilters: items: - type: string + properties: + key: + type: string + value: + type: string + required: + - key + type: object type: array - name: + type: object + azureDevOps: + properties: + accessTokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + allBranches: + type: boolean + api: type: string - namespace: + organization: + type: string + teamProject: type: string required: - - kind + - accessTokenRef + - organization + - teamProject type: object - type: array - info: - items: + bitbucket: properties: - name: + allBranches: + type: boolean + appPasswordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + owner: type: string - value: + user: type: string required: - - name - - value + - appPasswordRef + - owner + - user type: object - type: array - project: - type: string - revisionHistoryLimit: - format: int64 - type: integer - source: - properties: - chart: - type: string - directory: + bitbucketServer: + properties: + allBranches: + type: boolean + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + project: + type: string + required: + - api + - project + type: object + cloneProtocol: + type: string + filters: + items: properties: - exclude: + branchMatch: type: string - include: + labelMatch: type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean - type: object - helm: - properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: - type: boolean - valueFiles: + pathsDoNotExist: items: type: string type: array - values: - type: string - version: - type: string - type: object - kustomize: - properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: - type: boolean - images: + pathsExist: items: type: string type: array - namePrefix: - type: string - nameSuffix: - type: string - version: - type: string - type: object - path: - type: string - plugin: - properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - name: + repositoryMatch: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array type: object - ref: - type: string - repoURL: - type: string - targetRevision: - type: string - required: - - repoURL - type: object - sources: - items: + type: array + gitea: properties: - chart: + allBranches: + type: boolean + api: type: string - directory: + insecure: + type: boolean + owner: + type: string + tokenRef: properties: - exclude: + key: type: string - include: + secretName: type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean + required: + - key + - secretName type: object - helm: + required: + - api + - owner + type: object + github: + properties: + allBranches: + type: boolean + api: + type: string + appSecretName: + type: string + organization: + type: string + tokenRef: properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: - type: boolean - valueFiles: - items: - type: string - type: array - values: + key: type: string - version: + secretName: type: string + required: + - key + - secretName type: object - kustomize: - properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: - type: boolean - images: - items: - type: string - type: array - namePrefix: - type: string - nameSuffix: - type: string - version: - type: string - type: object - path: + required: + - organization + type: object + gitlab: + properties: + allBranches: + type: boolean + api: type: string - plugin: + group: + type: string + includeSharedProjects: + type: boolean + includeSubgroups: + type: boolean + insecure: + type: boolean + tokenRef: properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - name: + key: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array + secretName: + type: string + required: + - key + - secretName type: object - ref: - type: string - repoURL: - type: string - targetRevision: + topic: type: string required: - - repoURL - type: object - type: array - syncPolicy: - properties: - automated: - properties: - allowEmpty: - type: boolean - prune: - type: boolean - selfHeal: - type: boolean - type: object - managedNamespaceMetadata: - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - retry: - properties: - backoff: - properties: - duration: - type: string - factor: - format: int64 - type: integer - maxDuration: - type: string - type: object - limit: - format: int64 - type: integer - type: object - syncOptions: - items: - type: string - type: array - type: object - required: - - destination - - project - type: object - required: - - metadata - - spec - type: object - required: - - generators - type: object - merge: - properties: - generators: - items: - properties: - clusterDecisionResource: - properties: - configMapRef: - type: string - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object + - group type: object - name: - type: string requeueAfterSeconds: format: int64 type: integer @@ -5662,6 +6453,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5671,10 +6465,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5683,10 +6483,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5820,6 +6669,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5829,10 +6681,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5841,10 +6699,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5949,94 +6856,499 @@ spec: additionalProperties: type: string type: object - required: - - configMapRef type: object - clusters: + selector: properties: - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: + matchExpressions: + items: + properties: + key: type: string - type: object + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string type: object - template: + type: object + type: object + type: array + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: properties: - metadata: - properties: - annotations: - additionalProperties: - type: string + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string type: object - name: + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: type: string - namespace: + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: type: string - type: object - spec: - properties: - destination: + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: properties: - name: - type: string - namespace: + options: + additionalProperties: + type: boolean + type: object + patch: type: string - server: + path: type: string - type: object - ignoreDifferences: - items: - properties: - group: - type: string - jqPathExpressions: - items: + target: + properties: + annotationSelector: type: string - type: array - jsonPointers: - items: + group: type: string - type: array - kind: - type: string - managedFieldsManagers: - items: + kind: type: string - type: array - name: + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: type: string - namespace: + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: type: string required: - - kind + - count + - name type: object type: array - info: + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: items: properties: name: @@ -6048,38 +7360,230 @@ spec: - value type: object type: array - project: + name: type: string - revisionHistoryLimit: - format: int64 - type: integer - source: - properties: - chart: - type: string - directory: - properties: - exclude: + parameters: + items: + properties: + array: + items: type: string - include: + type: array + map: + additionalProperties: type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + required: + - generators + type: object + merge: + properties: + generators: + items: + properties: + clusterDecisionResource: + properties: + configMapRef: + type: string + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + name: + type: string + requeueAfterSeconds: + format: int64 + type: integer + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: items: type: string type: array @@ -6137,6 +7641,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6146,10 +7653,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6158,10 +7671,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -6295,6 +7857,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6304,10 +7869,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6316,14 +7887,63 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string - version: + namespace: type: string - type: object - path: + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: type: string plugin: properties: @@ -6424,38 +8044,34 @@ spec: additionalProperties: type: string type: object + required: + - configMapRef type: object - git: + clusters: properties: - directories: - items: - properties: - exclude: - type: boolean - path: - type: string - required: - - path - type: object - type: array - files: - items: - properties: - path: + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: type: string - required: - - path - type: object - type: array - pathParamPrefix: - type: string - repoURL: - type: string - requeueAfterSeconds: - format: int64 - type: integer - revision: - type: string + type: object + type: object template: properties: metadata: @@ -6616,6 +8232,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6625,10 +8244,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6637,10 +8262,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -6774,6 +8448,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6783,10 +8460,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6795,10 +8478,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -6899,16 +8631,42 @@ spec: - metadata - spec type: object - required: - - repoURL - - revision + values: + additionalProperties: + type: string + type: object type: object - list: + git: properties: - elements: + directories: items: - x-kubernetes-preserve-unknown-fields: true + properties: + exclude: + type: boolean + path: + type: string + required: + - path + type: object type: array + files: + items: + properties: + path: + type: string + required: + - path + type: object + type: array + pathParamPrefix: + type: string + repoURL: + type: string + requeueAfterSeconds: + format: int64 + type: integer + revision: + type: string template: properties: metadata: @@ -7069,6 +8827,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7078,10 +8839,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7090,10 +8857,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7227,6 +9043,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7236,10 +9055,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7248,32 +9073,81 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string - version: + namespace: type: string - type: object - path: - type: string - plugin: - properties: - env: + patches: items: properties: - name: + options: + additionalProperties: + type: boolean + type: object + patch: type: string - value: + path: type: string - required: - - name - - value - type: object - type: array - name: - type: string - parameters: + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: items: properties: array: @@ -7352,134 +9226,22 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object required: - - elements + - repoURL + - revision type: object - matrix: - x-kubernetes-preserve-unknown-fields: true - merge: - x-kubernetes-preserve-unknown-fields: true - pullRequest: + list: properties: - bitbucketServer: - properties: - api: - type: string - basicAuth: - properties: - passwordRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - username: - type: string - required: - - passwordRef - - username - type: object - project: - type: string - repo: - type: string - required: - - api - - project - - repo - type: object - filters: + elements: items: - properties: - branchMatch: - type: string - type: object + x-kubernetes-preserve-unknown-fields: true type: array - gitea: - properties: - api: - type: string - insecure: - type: boolean - owner: - type: string - repo: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - api - - owner - - repo - type: object - github: - properties: - api: - type: string - appSecretName: - type: string - labels: - items: - type: string - type: array - owner: - type: string - repo: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - owner - - repo - type: object - gitlab: - properties: - api: - type: string - labels: - items: - type: string - type: array - project: - type: string - pullRequestState: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - project - type: object - requeueAfterSeconds: - format: int64 - type: integer + elementsYaml: + type: string template: properties: metadata: @@ -7640,6 +9402,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7649,10 +9414,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7661,10 +9432,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7798,6 +9618,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7807,10 +9630,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7819,10 +9648,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7924,176 +9802,25 @@ spec: - spec type: object type: object - scmProvider: + matrix: + x-kubernetes-preserve-unknown-fields: true + merge: + x-kubernetes-preserve-unknown-fields: true + plugin: properties: - azureDevOps: + configMapRef: properties: - accessTokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - allBranches: - type: boolean - api: - type: string - organization: - type: string - teamProject: - type: string - required: - - accessTokenRef - - organization - - teamProject - type: object - bitbucket: - properties: - allBranches: - type: boolean - appPasswordRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - owner: - type: string - user: - type: string - required: - - appPasswordRef - - owner - - user - type: object - bitbucketServer: - properties: - allBranches: - type: boolean - api: - type: string - basicAuth: - properties: - passwordRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - username: - type: string - required: - - passwordRef - - username - type: object - project: - type: string - required: - - api - - project - type: object - cloneProtocol: - type: string - filters: - items: - properties: - branchMatch: - type: string - labelMatch: - type: string - pathsDoNotExist: - items: - type: string - type: array - pathsExist: - items: - type: string - type: array - repositoryMatch: - type: string - type: object - type: array - gitea: - properties: - allBranches: - type: boolean - api: - type: string - insecure: - type: boolean - owner: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - api - - owner - type: object - github: - properties: - allBranches: - type: boolean - api: - type: string - appSecretName: - type: string - organization: + name: type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object required: - - organization + - name type: object - gitlab: + input: properties: - allBranches: - type: boolean - api: - type: string - group: - type: string - includeSubgroups: - type: boolean - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName + parameters: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true type: object - required: - - group type: object requeueAfterSeconds: format: int64 @@ -8222,355 +9949,2582 @@ spec: recurse: type: boolean type: object - helm: + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + required: + - configMapRef + type: object + pullRequest: + properties: + azuredevops: + properties: + api: + type: string + labels: + items: + type: string + type: array + organization: + type: string + project: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - organization + - project + - repo + type: object + bitbucket: + properties: + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + bearerToken: + properties: + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - tokenRef + type: object + owner: + type: string + repo: + type: string + required: + - owner + - repo + type: object + bitbucketServer: + properties: + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + project: + type: string + repo: + type: string + required: + - api + - project + - repo + type: object + filters: + items: + properties: + branchMatch: + type: string + targetBranchMatch: + type: string + type: object + type: array + gitea: + properties: + api: + type: string + insecure: + type: boolean + owner: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - api + - owner + - repo + type: object + github: + properties: + api: + type: string + appSecretName: + type: string + labels: + items: + type: string + type: array + owner: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - owner + - repo + type: object + gitlab: + properties: + api: + type: string + insecure: + type: boolean + labels: + items: + type: string + type: array + project: + type: string + pullRequestState: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - project + type: object + requeueAfterSeconds: + format: int64 + type: integer + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + type: object + scmProvider: + properties: + awsCodeCommit: + properties: + allBranches: + type: boolean + region: + type: string + role: + type: string + tagFilters: + items: + properties: + key: + type: string + value: + type: string + required: + - key + type: object + type: array + type: object + azureDevOps: + properties: + accessTokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + allBranches: + type: boolean + api: + type: string + organization: + type: string + teamProject: + type: string + required: + - accessTokenRef + - organization + - teamProject + type: object + bitbucket: + properties: + allBranches: + type: boolean + appPasswordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + owner: + type: string + user: + type: string + required: + - appPasswordRef + - owner + - user + type: object + bitbucketServer: + properties: + allBranches: + type: boolean + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + project: + type: string + required: + - api + - project + type: object + cloneProtocol: + type: string + filters: + items: + properties: + branchMatch: + type: string + labelMatch: + type: string + pathsDoNotExist: + items: + type: string + type: array + pathsExist: + items: + type: string + type: array + repositoryMatch: + type: string + type: object + type: array + gitea: + properties: + allBranches: + type: boolean + api: + type: string + insecure: + type: boolean + owner: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - api + - owner + type: object + github: + properties: + allBranches: + type: boolean + api: + type: string + appSecretName: + type: string + organization: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - organization + type: object + gitlab: + properties: + allBranches: + type: boolean + api: + type: string + group: + type: string + includeSharedProjects: + type: boolean + includeSubgroups: + type: boolean + insecure: + type: boolean + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + topic: + type: string + required: + - group + type: object + requeueAfterSeconds: + format: int64 + type: integer + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + type: object + type: array + mergeKeys: + items: + type: string + type: array + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: + annotationSelector: type: string - skipCrds: - type: boolean - valueFiles: - items: - type: string - type: array - values: + group: type: string - version: + kind: type: string - type: object - kustomize: - properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: - type: boolean - images: - items: - type: string - type: array - namePrefix: + labelSelector: type: string - nameSuffix: + name: type: string - version: + namespace: type: string - type: object - path: - type: string - plugin: - properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - name: + version: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array type: object - ref: + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: type: string - repoURL: + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: type: string - targetRevision: + value: type: string required: - - repoURL + - name + - value type: object - sources: - items: - properties: - chart: + type: array + name: + type: string + parameters: + items: + properties: + array: + items: type: string - directory: - properties: - exclude: - type: string - include: - type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean - type: object - helm: + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: - type: boolean - valueFiles: - items: - type: string - type: array - values: + code: + type: boolean + name: type: string - version: + value: type: string + required: + - name + - value type: object - kustomize: + type: array + libs: + items: + type: string + type: array + tlas: + items: properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: + code: type: boolean - images: - items: - type: string - type: array - namePrefix: - type: string - nameSuffix: + name: type: string - version: + value: type: string + required: + - name + - value type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string path: type: string - plugin: + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string name: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array + namespace: + type: string + version: + type: string type: object - ref: + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: type: string - repoURL: + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: type: string - targetRevision: + value: type: string required: - - repoURL + - name + - value type: object type: array - syncPolicy: - properties: - automated: - properties: - allowEmpty: - type: boolean - prune: - type: boolean - selfHeal: - type: boolean - type: object - managedNamespaceMetadata: - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - retry: - properties: - backoff: - properties: - duration: - type: string - factor: - format: int64 - type: integer - maxDuration: - type: string - type: object - limit: - format: int64 - type: integer - type: object - syncOptions: - items: + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: type: string - type: array - type: object - required: - - destination - - project + string: + type: string + type: object + type: array type: object - required: - - metadata - - spec + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL type: object - type: object - selector: - properties: - matchExpressions: - items: + type: array + syncPolicy: + properties: + automated: properties: - key: - type: string - operator: - type: string - values: - items: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: type: string - type: array - required: - - key - - operator + type: object + labels: + additionalProperties: + type: string + type: object type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - type: array - mergeKeys: - items: - type: string - type: array + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + required: + - generators + - mergeKeys + type: object + plugin: + properties: + configMapRef: + properties: + name: + type: string + required: + - name + type: object + input: + properties: + parameters: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + requeueAfterSeconds: + format: int64 + type: integer template: properties: metadata: @@ -8731,6 +12685,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8740,10 +12697,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8752,10 +12715,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -8889,6 +12901,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8898,10 +12913,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8910,10 +12931,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -9014,12 +13084,89 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object required: - - generators - - mergeKeys + - configMapRef type: object pullRequest: properties: + azuredevops: + properties: + api: + type: string + labels: + items: + type: string + type: array + organization: + type: string + project: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - organization + - project + - repo + type: object + bitbucket: + properties: + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + bearerToken: + properties: + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - tokenRef + type: object + owner: + type: string + repo: + type: string + required: + - owner + - repo + type: object bitbucketServer: properties: api: @@ -9056,6 +13203,8 @@ spec: properties: branchMatch: type: string + targetBranchMatch: + type: string type: object type: array gitea: @@ -9115,6 +13264,8 @@ spec: properties: api: type: string + insecure: + type: boolean labels: items: type: string @@ -9299,6 +13450,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -9308,10 +13462,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string - type: object + type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -9320,10 +13480,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -9457,6 +13666,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -9466,10 +13678,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -9478,10 +13696,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -9585,6 +13852,26 @@ spec: type: object scmProvider: properties: + awsCodeCommit: + properties: + allBranches: + type: boolean + region: + type: string + role: + type: string + tagFilters: + items: + properties: + key: + type: string + value: + type: string + required: + - key + type: object + type: array + type: object azureDevOps: properties: accessTokenRef: @@ -9739,8 +14026,12 @@ spec: type: string group: type: string + includeSharedProjects: + type: boolean includeSubgroups: type: boolean + insecure: + type: boolean tokenRef: properties: key: @@ -9751,6 +14042,8 @@ spec: - key - secretName type: object + topic: + type: string required: - group type: object @@ -9917,6 +14210,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -9926,10 +14222,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -9938,10 +14240,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10075,6 +14426,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10084,10 +14438,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10096,10 +14456,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10200,6 +14609,10 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object type: object selector: properties: @@ -10228,6 +14641,36 @@ spec: type: array goTemplate: type: boolean + goTemplateOptions: + items: + type: string + type: array + ignoreApplicationDifferences: + items: + properties: + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + name: + type: string + type: object + type: array + preservedFields: + properties: + annotations: + items: + type: string + type: array + labels: + items: + type: string + type: array + type: object strategy: properties: rollingSync: @@ -10261,6 +14704,13 @@ spec: type: object syncPolicy: properties: + applicationsSync: + enum: + - create-only + - create-update + - create-delete + - sync + type: string preserveResourcesOnDeletion: type: boolean type: object @@ -10424,6 +14874,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10433,10 +14886,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10445,10 +14904,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10582,6 +15090,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10591,10 +15102,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10603,10 +15120,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10707,6 +15273,8 @@ spec: - metadata - spec type: object + templatePatch: + type: string required: - generators - template @@ -10725,10 +15293,13 @@ spec: type: string status: type: string + step: + type: string required: - application - message - status + - step type: object type: array conditions: diff --git a/manifests/crds/appproject-crd.yaml b/manifests/crds/appproject-crd.yaml index 335decfc564df..989b3004892f6 100644 --- a/manifests/crds/appproject-crd.yaml +++ b/manifests/crds/appproject-crd.yaml @@ -88,7 +88,8 @@ spec: properties: name: description: Name is an alternate way of specifying the target - cluster by its symbolic name + cluster by its symbolic name. This must be set if Server is + not set. type: string namespace: description: Namespace specifies the target namespace for the @@ -96,8 +97,9 @@ spec: namespace-scoped resources that have not set a value for .metadata.namespace type: string server: - description: Server specifies the URL of the target cluster - and must be set to the Kubernetes control plane API + description: Server specifies the URL of the target cluster's + Kubernetes control plane API. This must be set if Name is + not set. type: string type: object type: array diff --git a/manifests/ha/base/controller-deployment/argocd-application-controller-statefulset.yaml b/manifests/ha/base/controller-deployment/argocd-application-controller-statefulset.yaml new file mode 100644 index 0000000000000..10e4ea2ac7e3e --- /dev/null +++ b/manifests/ha/base/controller-deployment/argocd-application-controller-statefulset.yaml @@ -0,0 +1,15 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: argocd-application-controller +spec: + replicas: 0 + template: + spec: + containers: + - name: argocd-application-controller + args: + - /usr/local/bin/argocd-application-controller + env: + - name: ARGOCD_CONTROLLER_REPLICAS + value: "0" \ No newline at end of file diff --git a/manifests/ha/base/controller-deployment/argocd-cmd-params-cm.yaml b/manifests/ha/base/controller-deployment/argocd-cmd-params-cm.yaml new file mode 100644 index 0000000000000..5752543cc1af3 --- /dev/null +++ b/manifests/ha/base/controller-deployment/argocd-cmd-params-cm.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cmd-params-cm +data: + redis.server: argocd-redis-ha-haproxy:6379 diff --git a/manifests/ha/base/controller-deployment/argocd-repo-server-deployment.yaml b/manifests/ha/base/controller-deployment/argocd-repo-server-deployment.yaml new file mode 100644 index 0000000000000..b237cf6c13b24 --- /dev/null +++ b/manifests/ha/base/controller-deployment/argocd-repo-server-deployment.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: argocd-repo-server +spec: + replicas: 2 + template: + spec: + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: argocd-repo-server + topologyKey: kubernetes.io/hostname + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/name: argocd-repo-server + topologyKey: topology.kubernetes.io/zone + containers: + - name: argocd-repo-server + args: + - /usr/local/bin/argocd-repo-server diff --git a/manifests/ha/base/controller-deployment/argocd-server-deployment.yaml b/manifests/ha/base/controller-deployment/argocd-server-deployment.yaml new file mode 100644 index 0000000000000..49eb31b1b0f29 --- /dev/null +++ b/manifests/ha/base/controller-deployment/argocd-server-deployment.yaml @@ -0,0 +1,29 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: argocd-server +spec: + replicas: 2 + template: + spec: + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: argocd-server + topologyKey: kubernetes.io/hostname + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/name: argocd-server + topologyKey: topology.kubernetes.io/zone + containers: + - name: argocd-server + env: + - name: ARGOCD_API_SERVER_REPLICAS + value: '2' + args: + - /usr/local/bin/argocd-server diff --git a/manifests/ha/base/controller-deployment/kustomization.yaml b/manifests/ha/base/controller-deployment/kustomization.yaml new file mode 100644 index 0000000000000..e98bd250d699e --- /dev/null +++ b/manifests/ha/base/controller-deployment/kustomization.yaml @@ -0,0 +1,22 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +patches: +- path: argocd-application-controller-statefulset.yaml +- path: argocd-repo-server-deployment.yaml +- path: argocd-server-deployment.yaml +- path: argocd-cmd-params-cm.yaml + +images: +- name: quay.io/argoproj/argocd + newName: quay.io/argoproj/argocd + newTag: latest +resources: +- ../../../base/application-controller-deployment +- ../../../base/applicationset-controller +- ../../../base/dex +- ../../../base/repo-server +- ../../../base/server +- ../../../base/config +- ../../../base/notification +- ../redis-ha diff --git a/manifests/ha/base/kustomization.yaml b/manifests/ha/base/kustomization.yaml index c8f8e9e8f5b9b..ae40b96e8657e 100644 --- a/manifests/ha/base/kustomization.yaml +++ b/manifests/ha/base/kustomization.yaml @@ -2,10 +2,11 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -patchesStrategicMerge: -- overlays/argocd-repo-server-deployment.yaml -- overlays/argocd-server-deployment.yaml -- overlays/argocd-application-controller-statefulset.yaml +patches: +- path: overlays/argocd-repo-server-deployment.yaml +- path: overlays/argocd-server-deployment.yaml +- path: overlays/argocd-application-controller-statefulset.yaml +- path: overlays/argocd-cmd-params-cm.yaml images: diff --git a/manifests/ha/base/overlays/argocd-application-controller-statefulset.yaml b/manifests/ha/base/overlays/argocd-application-controller-statefulset.yaml index 07b5c252c29cd..c7e5e0b8e1131 100644 --- a/manifests/ha/base/overlays/argocd-application-controller-statefulset.yaml +++ b/manifests/ha/base/overlays/argocd-application-controller-statefulset.yaml @@ -7,7 +7,5 @@ spec: spec: containers: - name: argocd-application-controller - command: - - argocd-application-controller - - --redis - - "argocd-redis-ha-haproxy:6379" + args: + - /usr/local/bin/argocd-application-controller \ No newline at end of file diff --git a/manifests/ha/base/overlays/argocd-cmd-params-cm.yaml b/manifests/ha/base/overlays/argocd-cmd-params-cm.yaml new file mode 100644 index 0000000000000..5752543cc1af3 --- /dev/null +++ b/manifests/ha/base/overlays/argocd-cmd-params-cm.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cmd-params-cm +data: + redis.server: argocd-redis-ha-haproxy:6379 diff --git a/manifests/ha/base/overlays/argocd-repo-server-deployment.yaml b/manifests/ha/base/overlays/argocd-repo-server-deployment.yaml index 496bb136f602a..b237cf6c13b24 100644 --- a/manifests/ha/base/overlays/argocd-repo-server-deployment.yaml +++ b/manifests/ha/base/overlays/argocd-repo-server-deployment.yaml @@ -19,11 +19,8 @@ spec: labelSelector: matchLabels: app.kubernetes.io/name: argocd-repo-server - topologyKey: failure-domain.beta.kubernetes.io/zone + topologyKey: topology.kubernetes.io/zone containers: - name: argocd-repo-server - command: - - entrypoint.sh - - argocd-repo-server - - --redis - - "argocd-redis-ha-haproxy:6379" + args: + - /usr/local/bin/argocd-repo-server diff --git a/manifests/ha/base/overlays/argocd-server-deployment.yaml b/manifests/ha/base/overlays/argocd-server-deployment.yaml index 0b09752afa3a0..49eb31b1b0f29 100644 --- a/manifests/ha/base/overlays/argocd-server-deployment.yaml +++ b/manifests/ha/base/overlays/argocd-server-deployment.yaml @@ -19,13 +19,11 @@ spec: labelSelector: matchLabels: app.kubernetes.io/name: argocd-server - topologyKey: failure-domain.beta.kubernetes.io/zone + topologyKey: topology.kubernetes.io/zone containers: - name: argocd-server env: - name: ARGOCD_API_SERVER_REPLICAS value: '2' - command: - - argocd-server - - --redis - - "argocd-redis-ha-haproxy:6379" + args: + - /usr/local/bin/argocd-server diff --git a/manifests/ha/base/redis-ha/chart/upstream.yaml b/manifests/ha/base/redis-ha/chart/upstream.yaml index cd890ba33dc4c..1d0e4b3c247f8 100644 --- a/manifests/ha/base/redis-ha/chart/upstream.yaml +++ b/manifests/ha/base/redis-ha/chart/upstream.yaml @@ -686,6 +686,13 @@ data: server R1 argocd-redis-ha-announce-1:6379 check inter 3s fall 1 rise 1 use-server R2 if { srv_is_up(R2) } { nbsrv(check_if_redis_is_master_2) ge 2 } server R2 argocd-redis-ha-announce-2:6379 check inter 3s fall 1 rise 1 + frontend stats + mode http + bind :9101 + http-request use-service prometheus-exporter if { path /metrics } + stats enable + stats uri /stats + stats refresh 10s haproxy_init.sh: | HAPROXY_CONF=/data/haproxy.cfg cp /readonly/haproxy.cfg "$HAPROXY_CONF" @@ -1015,6 +1022,10 @@ spec: port: 6379 protocol: TCP targetPort: redis + - name: http-exporter-port + port: 9101 + protocol: TCP + targetPort: metrics-port selector: release: argocd app: redis-ha-haproxy @@ -1047,7 +1058,10 @@ spec: release: argocd revision: "1" annotations: - checksum/config: 718bbb277da8610063a7c0fd810984577c2e8ab215815a71211dfa6e20f67321 + prometheus.io/port: "9101" + prometheus.io/scrape: "true" + prometheus.io/path: "/metrics" + checksum/config: 492a6adabb741e0cee39be9aa5155c41a4456629f862d0006a2d892dbecfbcae spec: # Needed when using unmodified rbac-setup.yml @@ -1071,7 +1085,7 @@ spec: topologyKey: kubernetes.io/hostname initContainers: - name: config-init - image: haproxy:2.6.2-alpine + image: haproxy:2.6.14-alpine imagePullPolicy: IfNotPresent resources: {} @@ -1080,7 +1094,13 @@ spec: args: - /readonly/haproxy_init.sh securityContext: - null + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - name: config-volume mountPath: /readonly @@ -1089,10 +1109,16 @@ spec: mountPath: /data containers: - name: haproxy - image: haproxy:2.6.2-alpine + image: haproxy:2.6.14-alpine imagePullPolicy: IfNotPresent securityContext: - null + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault livenessProbe: httpGet: path: /healthz @@ -1108,6 +1134,8 @@ spec: ports: - name: redis containerPort: 6379 + - name: metrics-port + containerPort: 9101 resources: {} volumeMounts: @@ -1179,7 +1207,7 @@ spec: automountServiceAccountToken: false initContainers: - name: config-init - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: IfNotPresent resources: {} @@ -1188,7 +1216,14 @@ spec: args: - /readonly-config/init.sh securityContext: - null + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault env: - name: SENTINEL_ID_0 value: 3c0d9c0320bb34888c2df5757c718ce6ca992ce6 @@ -1206,14 +1241,21 @@ spec: containers: - name: redis - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: IfNotPresent command: - redis-server args: - /data/conf/redis.conf securityContext: - null + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault livenessProbe: initialDelaySeconds: 30 periodSeconds: 15 @@ -1256,14 +1298,21 @@ spec: - /bin/sh - /readonly-config/trigger-failover-if-master.sh - name: sentinel - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: IfNotPresent command: - redis-sentinel args: - /data/conf/sentinel.conf securityContext: - null + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault livenessProbe: initialDelaySeconds: 30 periodSeconds: 15 @@ -1300,14 +1349,21 @@ spec: {} - name: split-brain-fix - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: IfNotPresent command: - sh args: - /readonly-config/fix-split-brain.sh securityContext: - null + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault env: - name: SENTINEL_ID_0 value: 3c0d9c0320bb34888c2df5757c718ce6ca992ce6 diff --git a/manifests/ha/base/redis-ha/chart/values.yaml b/manifests/ha/base/redis-ha/chart/values.yaml index db8973b575627..5606daac34bb3 100644 --- a/manifests/ha/base/redis-ha/chart/values.yaml +++ b/manifests/ha/base/redis-ha/chart/values.yaml @@ -11,14 +11,16 @@ redis-ha: IPv6: enabled: false image: - tag: 2.6.2-alpine + tag: 2.6.14-alpine containerSecurityContext: null timeout: server: 6m client: 6m checkInterval: 3s + metrics: + enabled: true image: - tag: 7.0.7-alpine + tag: 7.0.14-alpine containerSecurityContext: null sentinel: bind: "0.0.0.0" diff --git a/manifests/ha/base/redis-ha/kustomization.yaml b/manifests/ha/base/redis-ha/kustomization.yaml index 373a9754527ac..bf0c6c3dff255 100644 --- a/manifests/ha/base/redis-ha/kustomization.yaml +++ b/manifests/ha/base/redis-ha/kustomization.yaml @@ -6,7 +6,7 @@ resources: - argocd-redis-ha-proxy-network-policy.yaml - argocd-redis-ha-server-network-policy.yaml -patchesJson6902: +patches: - target: version: v1 group: "" diff --git a/manifests/ha/base/redis-ha/overlays/deployment-containers-securityContext.yaml b/manifests/ha/base/redis-ha/overlays/deployment-containers-securityContext.yaml index 812e97d8049cf..8ce2b23f876a2 100644 --- a/manifests/ha/base/redis-ha/overlays/deployment-containers-securityContext.yaml +++ b/manifests/ha/base/redis-ha/overlays/deployment-containers-securityContext.yaml @@ -1,6 +1,7 @@ - op: add path: /spec/template/spec/initContainers/0/securityContext value: + readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: @@ -10,6 +11,7 @@ - op: add path: /spec/template/spec/containers/0/securityContext value: + readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: diff --git a/manifests/ha/base/redis-ha/overlays/statefulset-containers-securityContext.yaml b/manifests/ha/base/redis-ha/overlays/statefulset-containers-securityContext.yaml index 386b219575eb7..53b395e14da12 100644 --- a/manifests/ha/base/redis-ha/overlays/statefulset-containers-securityContext.yaml +++ b/manifests/ha/base/redis-ha/overlays/statefulset-containers-securityContext.yaml @@ -1,6 +1,7 @@ - op: add path: /spec/template/spec/initContainers/0/securityContext value: + readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: @@ -10,6 +11,7 @@ - op: add path: /spec/template/spec/containers/0/securityContext value: + readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: @@ -19,6 +21,7 @@ - op: add path: /spec/template/spec/containers/1/securityContext value: + readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: @@ -28,6 +31,7 @@ - op: add path: /spec/template/spec/containers/2/securityContext value: + readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml index 62461d59ffa66..9ce3b1cb4b824 100644 --- a/manifests/ha/install.yaml +++ b/manifests/ha/install.yaml @@ -287,8 +287,15 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be + passed to helm template, defined as a map. This takes + precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -303,12 +310,22 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources for @@ -326,6 +343,10 @@ spec: definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether to + apply common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -334,6 +355,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -549,8 +624,15 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be + passed to helm template, defined as a map. This takes + precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -565,12 +647,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -589,6 +682,10 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -597,6 +694,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -718,7 +869,8 @@ spec: properties: name: description: Name is an alternate way of specifying the target - cluster by its symbolic name + cluster by its symbolic name. This must be set if Server is + not set. type: string namespace: description: Namespace specifies the target namespace for the @@ -726,8 +878,9 @@ spec: namespace-scoped resources that have not set a value for .metadata.namespace type: string server: - description: Server specifies the URL of the target cluster and - must be set to the Kubernetes control plane API + description: Server specifies the URL of the target cluster's + Kubernetes control plane API. This must be set if Name is not + set. type: string type: object ignoreDifferences: @@ -927,8 +1080,15 @@ spec: type: array values: description: Values specifies Helm values to be passed to - helm template, typically defined as a block + helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be passed + to helm template, defined as a map. This takes precedence + over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -943,12 +1103,22 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether to + apply env variables substitution for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize components + to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize apps @@ -965,6 +1135,10 @@ spec: definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether to apply + common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -973,6 +1147,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize adds + to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas override + specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -1180,8 +1408,15 @@ spec: type: array values: description: Values specifies Helm values to be passed to - helm template, typically defined as a block + helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be passed + to helm template, defined as a map. This takes precedence + over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -1196,12 +1431,22 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize components + to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize @@ -1219,6 +1464,10 @@ spec: definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether to apply + common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -1227,6 +1476,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas override + specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -1319,7 +1622,7 @@ spec: as part of automated sync (default: false)' type: boolean selfHeal: - description: 'SelfHeal specifes whether to revert resources + description: 'SelfHeal specifies whether to revert resources back to their desired state upon modification in the cluster (default: false)' type: boolean @@ -1383,7 +1686,7 @@ spec: conditions items: description: ApplicationCondition contains details about an application - condition, which is usally an error or warning + condition, which is usually an error or warning properties: lastTransitionTime: description: LastTransitionTime is the time the condition was @@ -1402,6 +1705,10 @@ spec: - type type: object type: array + controllerNamespace: + description: ControllerNamespace indicates the namespace in which + the application controller is located + type: string health: description: Health contains information about the application's current health status @@ -1435,6 +1742,19 @@ spec: description: ID is an auto incrementing identifier of the RevisionHistory format: int64 type: integer + initiatedBy: + description: InitiatedBy contains information about who initiated + the operations + properties: + automated: + description: Automated is set to true if operation was initiated + automatically by the application controller. + type: boolean + username: + description: Username contains the name of a user who started + operation + type: string + type: object revision: description: Revision holds the revision the sync was performed against @@ -1581,8 +1901,15 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be + passed to helm template, defined as a map. This takes + precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -1597,12 +1924,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -1621,6 +1959,10 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -1629,6 +1971,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -1846,8 +2242,16 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. + ValuesObject takes precedence over Values, so use + one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to + be passed to helm template, defined as a map. This + takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -1862,12 +2266,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -1886,6 +2301,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -1894,6 +2314,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -2256,8 +2730,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined as - a block + a block. ValuesObject takes precedence over + Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as a + map. This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -2272,12 +2753,24 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution + for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before + building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations @@ -2296,6 +2789,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors + or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -2304,14 +2802,68 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string - version: - description: Version controls which version of - Kustomize to use for rendering manifests + namespace: + description: Namespace sets the namespace that + Kustomize adds to all resources type: string - type: object - path: - description: Path is a directory path within the Git - repository, and is only valid for applications sourced + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array + version: + description: Version controls which version of + Kustomize to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git + repository, and is only valid for applications sourced from Git. type: string plugin: @@ -2537,8 +3089,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined - as a block + as a block. ValuesObject takes precedence + over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as + a map. This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -2555,12 +3114,24 @@ spec: additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution + for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of + kustomize components to add to the kustomization + before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations @@ -2579,6 +3150,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies + whether to apply common labels to resource + selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -2587,6 +3163,61 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that + Kustomize adds to all resources + type: string + patches: + description: Patches is a list of Kustomize + patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize + Replicas override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -2720,6 +3351,19 @@ spec: syncResult: description: SyncResult is the result of a Sync operation properties: + managedNamespaceMetadata: + description: ManagedNamespaceMetadata contains the current + sync state of managed namespace metadata + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object resources: description: Resources contains a list of sync result items for each individual resource in a sync operation @@ -2922,8 +3566,16 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. + ValuesObject takes precedence over Values, so use + one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to + be passed to helm template, defined as a map. This + takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -2938,12 +3590,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -2962,6 +3625,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -2970,6 +3638,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -3198,8 +3920,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined as - a block + a block. ValuesObject takes precedence over Values, + so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as a map. + This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -3214,12 +3943,24 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution for + annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before + building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -3238,6 +3979,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -3246,6 +3992,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -3428,7 +4228,8 @@ spec: properties: name: description: Name is an alternate way of specifying the - target cluster by its symbolic name + target cluster by its symbolic name. This must be set + if Server is not set. type: string namespace: description: Namespace specifies the target namespace @@ -3437,10 +4238,47 @@ spec: not set a value for .metadata.namespace type: string server: - description: Server specifies the URL of the target cluster - and must be set to the Kubernetes control plane API + description: Server specifies the URL of the target cluster's + Kubernetes control plane API. This must be set if Name + is not set. type: string type: object + ignoreDifferences: + description: IgnoreDifferences is a reference to the application's + ignored differences used for comparison + items: + description: ResourceIgnoreDifferences contains resource + filter and list of json paths which should be ignored + during comparison with live state. + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + description: ManagedFieldsManagers is a list of trusted + managers. Fields mutated by those managers will take + precedence over the desired state defined in the SCM + and won't be displayed in diffs + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array source: description: Source is a reference to the application's source used for comparison @@ -3579,8 +4417,16 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. + ValuesObject takes precedence over Values, so use + one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to + be passed to helm template, defined as a map. This + takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -3595,12 +4441,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -3619,6 +4476,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -3627,6 +4489,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -3855,8 +4771,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined as - a block + a block. ValuesObject takes precedence over Values, + so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as a map. + This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -3871,12 +4794,24 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution for + annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before + building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -3895,6 +4830,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -3903,33 +4843,87 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string - version: - description: Version controls which version of Kustomize - to use for rendering manifests + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources type: string - type: object - path: - description: Path is a directory path within the Git - repository, and is only valid for applications sourced - from Git. - type: string - plugin: - description: Plugin holds config management plugin specific - options - properties: - env: - description: Env is a list of environment variable - entries + patches: + description: Patches is a list of Kustomize patches items: - description: EnvEntry represents an entry in the - application's environment properties: - name: - description: Name is the name of the variable, - usually expressed in uppercase + options: + additionalProperties: + type: boolean + type: object + patch: type: string - value: - description: Value is the value of the variable + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array + version: + description: Version controls which version of Kustomize + to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git + repository, and is only valid for applications sourced + from Git. + type: string + plugin: + description: Plugin holds config management plugin specific + options + properties: + env: + description: Env is a list of environment variable + entries + items: + description: EnvEntry represents an entry in the + application's environment + properties: + name: + description: Name is the name of the variable, + usually expressed in uppercase + type: string + value: + description: Value is the value of the variable type: string required: - name @@ -4043,6 +5037,8 @@ spec: type: object spec: properties: + applyNestedSelectors: + type: boolean generators: items: properties: @@ -4238,6 +5234,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4247,10 +5246,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4259,10 +5264,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4396,6 +5450,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4405,10 +5462,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4417,10 +5480,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4713,6 +5825,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4722,10 +5837,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4734,10 +5855,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4871,6 +6041,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4880,10 +6053,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4892,10 +6071,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5192,6 +6420,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5201,10 +6432,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5213,10 +6450,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5350,6 +6636,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5359,10 +6648,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5371,25 +6666,74 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string - version: + namespace: type: string - type: object - path: - type: string - plugin: - properties: - env: + patches: items: properties: - name: + options: + additionalProperties: + type: boolean + type: object + patch: type: string - value: + path: type: string - required: + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: - name - value type: object @@ -5475,6 +6819,10 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object required: - repoURL - revision @@ -5485,6 +6833,8 @@ spec: items: x-kubernetes-preserve-unknown-fields: true type: array + elementsYaml: + type: string template: properties: metadata: @@ -5645,6 +6995,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5654,10 +7007,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5666,10 +7025,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5803,6 +7211,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5812,10 +7223,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5824,10 +7241,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5928,8 +7394,6 @@ spec: - metadata - spec type: object - required: - - elements type: object matrix: properties: @@ -6128,6 +7592,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6137,10 +7604,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6149,10 +7622,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -6286,6 +7808,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6295,10 +7820,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6307,10 +7838,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -6603,6 +8183,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6612,10 +8195,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6624,10 +8213,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -6761,6 +8399,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6770,10 +8411,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6782,10 +8429,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7082,6 +8778,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7091,10 +8790,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7103,10 +8808,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7240,6 +8994,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7249,10 +9006,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7261,10 +9024,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7365,6 +9177,10 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object required: - repoURL - revision @@ -7375,6 +9191,8 @@ spec: items: x-kubernetes-preserve-unknown-fields: true type: array + elementsYaml: + type: string template: properties: metadata: @@ -7535,6 +9353,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7544,10 +9365,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7556,10 +9383,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7693,6 +9569,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7702,10 +9581,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7714,10 +9599,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7818,153 +9752,49 @@ spec: - metadata - spec type: object - required: - - elements type: object matrix: x-kubernetes-preserve-unknown-fields: true merge: x-kubernetes-preserve-unknown-fields: true - pullRequest: + plugin: properties: - bitbucketServer: + configMapRef: properties: - api: - type: string - basicAuth: - properties: - passwordRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - username: - type: string - required: - - passwordRef - - username - type: object - project: - type: string - repo: + name: type: string required: - - api - - project - - repo + - name type: object - filters: - items: - properties: - branchMatch: - type: string - type: object - type: array - gitea: + input: properties: - api: - type: string - insecure: - type: boolean - owner: - type: string - repo: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName + parameters: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true type: object - required: - - api - - owner - - repo type: object - github: + requeueAfterSeconds: + format: int64 + type: integer + template: properties: - api: - type: string - appSecretName: - type: string - labels: - items: - type: string - type: array - owner: - type: string - repo: - type: string - tokenRef: + metadata: properties: - key: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - owner - - repo - type: object - gitlab: - properties: - api: - type: string - labels: - items: - type: string - type: array - project: - type: string - pullRequestState: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - project - type: object - requeueAfterSeconds: - format: int64 - type: integer - template: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: + namespace: type: string type: object spec: @@ -8106,6 +9936,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8115,10 +9948,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8127,10 +9966,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -8264,6 +10152,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8273,10 +10164,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8285,10 +10182,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -8389,12 +10335,30 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object + required: + - configMapRef type: object - scmProvider: + pullRequest: properties: - azureDevOps: + azuredevops: properties: - accessTokenRef: + api: + type: string + labels: + items: + type: string + type: array + organization: + type: string + project: + type: string + repo: + type: string + tokenRef: properties: key: type: string @@ -8404,46 +10368,58 @@ spec: - key - secretName type: object - allBranches: - type: boolean - api: - type: string - organization: - type: string - teamProject: - type: string required: - - accessTokenRef - organization - - teamProject + - project + - repo type: object bitbucket: properties: - allBranches: - type: boolean - appPasswordRef: + api: + type: string + basicAuth: properties: - key: - type: string - secretName: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: type: string required: - - key - - secretName + - passwordRef + - username + type: object + bearerToken: + properties: + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - tokenRef type: object owner: type: string - user: + repo: type: string required: - - appPasswordRef - owner - - user + - repo type: object bitbucketServer: properties: - allBranches: - type: boolean api: type: string basicAuth: @@ -8466,41 +10442,32 @@ spec: type: object project: type: string + repo: + type: string required: - api - project + - repo type: object - cloneProtocol: - type: string filters: items: properties: branchMatch: type: string - labelMatch: - type: string - pathsDoNotExist: - items: - type: string - type: array - pathsExist: - items: - type: string - type: array - repositoryMatch: + targetBranchMatch: type: string type: object type: array gitea: properties: - allBranches: - type: boolean api: type: string insecure: type: boolean owner: type: string + repo: + type: string tokenRef: properties: key: @@ -8514,16 +10481,21 @@ spec: required: - api - owner + - repo type: object github: properties: - allBranches: - type: boolean api: type: string appSecretName: type: string - organization: + labels: + items: + type: string + type: array + owner: + type: string + repo: type: string tokenRef: properties: @@ -8536,18 +10508,23 @@ spec: - secretName type: object required: - - organization + - owner + - repo type: object gitlab: properties: - allBranches: - type: boolean api: type: string - group: - type: string - includeSubgroups: + insecure: type: boolean + labels: + items: + type: string + type: array + project: + type: string + pullRequestState: + type: string tokenRef: properties: key: @@ -8559,7 +10536,7 @@ spec: - secretName type: object required: - - group + - project type: object requeueAfterSeconds: format: int64 @@ -8724,6 +10701,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8733,10 +10713,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8745,23 +10731,72 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string - version: + namespace: type: string - type: object - path: - type: string - plugin: - properties: - env: + patches: items: properties: - name: - type: string - value: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: type: string required: - name @@ -8882,6 +10917,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8891,10 +10929,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8903,10 +10947,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -9008,511 +11101,203 @@ spec: - spec type: object type: object - selector: + scmProvider: properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - type: array - template: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - destination: - properties: - name: - type: string - namespace: - type: string - server: - type: string - type: object - ignoreDifferences: - items: + awsCodeCommit: properties: - group: + allBranches: + type: boolean + region: type: string - jqPathExpressions: - items: - type: string - type: array - jsonPointers: - items: - type: string - type: array - kind: + role: type: string - managedFieldsManagers: + tagFilters: items: - type: string + properties: + key: + type: string + value: + type: string + required: + - key + type: object type: array - name: + type: object + azureDevOps: + properties: + accessTokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + allBranches: + type: boolean + api: type: string - namespace: + organization: + type: string + teamProject: type: string required: - - kind + - accessTokenRef + - organization + - teamProject type: object - type: array - info: - items: + bitbucket: properties: - name: + allBranches: + type: boolean + appPasswordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + owner: type: string - value: + user: type: string required: - - name - - value + - appPasswordRef + - owner + - user type: object - type: array - project: - type: string - revisionHistoryLimit: - format: int64 - type: integer - source: - properties: - chart: - type: string - directory: + bitbucketServer: + properties: + allBranches: + type: boolean + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + project: + type: string + required: + - api + - project + type: object + cloneProtocol: + type: string + filters: + items: properties: - exclude: + branchMatch: type: string - include: + labelMatch: type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean - type: object - helm: - properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: - type: boolean - valueFiles: + pathsDoNotExist: items: type: string type: array - values: - type: string - version: - type: string - type: object - kustomize: - properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: - type: boolean - images: + pathsExist: items: type: string type: array - namePrefix: - type: string - nameSuffix: - type: string - version: - type: string - type: object - path: - type: string - plugin: - properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - name: + repositoryMatch: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array type: object - ref: - type: string - repoURL: - type: string - targetRevision: - type: string - required: - - repoURL - type: object - sources: - items: + type: array + gitea: properties: - chart: + allBranches: + type: boolean + api: type: string - directory: + insecure: + type: boolean + owner: + type: string + tokenRef: properties: - exclude: + key: type: string - include: + secretName: type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean + required: + - key + - secretName type: object - helm: + required: + - api + - owner + type: object + github: + properties: + allBranches: + type: boolean + api: + type: string + appSecretName: + type: string + organization: + type: string + tokenRef: properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: - type: boolean - valueFiles: - items: - type: string - type: array - values: + key: type: string - version: + secretName: type: string + required: + - key + - secretName type: object - kustomize: - properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: - type: boolean - images: - items: - type: string - type: array - namePrefix: - type: string - nameSuffix: - type: string - version: - type: string - type: object - path: + required: + - organization + type: object + gitlab: + properties: + allBranches: + type: boolean + api: type: string - plugin: + group: + type: string + includeSharedProjects: + type: boolean + includeSubgroups: + type: boolean + insecure: + type: boolean + tokenRef: properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - name: + key: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array + secretName: + type: string + required: + - key + - secretName type: object - ref: - type: string - repoURL: - type: string - targetRevision: + topic: type: string required: - - repoURL - type: object - type: array - syncPolicy: - properties: - automated: - properties: - allowEmpty: - type: boolean - prune: - type: boolean - selfHeal: - type: boolean - type: object - managedNamespaceMetadata: - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - retry: - properties: - backoff: - properties: - duration: - type: string - factor: - format: int64 - type: integer - maxDuration: - type: string - type: object - limit: - format: int64 - type: integer - type: object - syncOptions: - items: - type: string - type: array - type: object - required: - - destination - - project - type: object - required: - - metadata - - spec - type: object - required: - - generators - type: object - merge: - properties: - generators: - items: - properties: - clusterDecisionResource: - properties: - configMapRef: - type: string - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object + - group type: object - name: - type: string requeueAfterSeconds: format: int64 type: integer @@ -9676,6 +11461,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -9685,10 +11473,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -9697,10 +11491,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -9834,6 +11677,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -9843,10 +11689,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -9855,10 +11707,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -9963,34 +11864,2392 @@ spec: additionalProperties: type: string type: object - required: - - configMapRef type: object - clusters: + selector: properties: - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + type: object + type: array + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: items: type: string type: array - required: - - key - - operator + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array type: object - type: array - matchLabels: - additionalProperties: + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: type: string - type: object - type: object + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + required: + - generators + type: object + merge: + properties: + generators: + items: + properties: + clusterDecisionResource: + properties: + configMapRef: + type: string + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + name: + type: string + requeueAfterSeconds: + format: int64 + type: integer + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + required: + - configMapRef + type: object + clusters: + properties: + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + type: object + git: + properties: + directories: + items: + properties: + exclude: + type: boolean + path: + type: string + required: + - path + type: object + type: array + files: + items: + properties: + path: + type: string + required: + - path + type: object + type: array + pathParamPrefix: + type: string + repoURL: + type: string + requeueAfterSeconds: + format: int64 + type: integer + revision: + type: string + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + required: + - repoURL + - revision + type: object + list: + properties: + elements: + items: + x-kubernetes-preserve-unknown-fields: true + type: array + elementsYaml: + type: string template: properties: metadata: @@ -10151,6 +14410,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10160,10 +14422,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10172,10 +14440,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10309,6 +14626,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10318,10 +14638,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10330,10 +14656,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10434,42 +14809,30 @@ spec: - metadata - spec type: object - values: - additionalProperties: - type: string - type: object type: object - git: + matrix: + x-kubernetes-preserve-unknown-fields: true + merge: + x-kubernetes-preserve-unknown-fields: true + plugin: properties: - directories: - items: - properties: - exclude: - type: boolean - path: - type: string - required: - - path - type: object - type: array - files: - items: - properties: - path: - type: string - required: - - path - type: object - type: array - pathParamPrefix: - type: string - repoURL: - type: string + configMapRef: + properties: + name: + type: string + required: + - name + type: object + input: + properties: + parameters: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + type: object + type: object requeueAfterSeconds: format: int64 type: integer - revision: - type: string template: properties: metadata: @@ -10630,6 +14993,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10639,10 +15005,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10651,10 +15023,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10788,6 +15209,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10797,10 +15221,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10809,10 +15239,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10906,23 +15385,219 @@ spec: type: array type: object required: - - destination - - project + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + required: + - configMapRef + type: object + pullRequest: + properties: + azuredevops: + properties: + api: + type: string + labels: + items: + type: string + type: array + organization: + type: string + project: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - organization + - project + - repo + type: object + bitbucket: + properties: + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + bearerToken: + properties: + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - tokenRef type: object + owner: + type: string + repo: + type: string required: - - metadata - - spec + - owner + - repo type: object - required: - - repoURL - - revision - type: object - list: - properties: - elements: + bitbucketServer: + properties: + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + project: + type: string + repo: + type: string + required: + - api + - project + - repo + type: object + filters: items: - x-kubernetes-preserve-unknown-fields: true + properties: + branchMatch: + type: string + targetBranchMatch: + type: string + type: object type: array + gitea: + properties: + api: + type: string + insecure: + type: boolean + owner: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - api + - owner + - repo + type: object + github: + properties: + api: + type: string + appSecretName: + type: string + labels: + items: + type: string + type: array + owner: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - owner + - repo + type: object + gitlab: + properties: + api: + type: string + insecure: + type: boolean + labels: + items: + type: string + type: array + project: + type: string + pullRequestState: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - project + type: object + requeueAfterSeconds: + format: int64 + type: integer template: properties: metadata: @@ -11083,6 +15758,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -11092,10 +15770,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -11104,10 +15788,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -11241,6 +15974,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -11250,10 +15986,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -11262,10 +16004,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -11359,24 +16150,88 @@ spec: type: array type: object required: - - destination - - project + - destination + - project + type: object + required: + - metadata + - spec + type: object + type: object + scmProvider: + properties: + awsCodeCommit: + properties: + allBranches: + type: boolean + region: + type: string + role: + type: string + tagFilters: + items: + properties: + key: + type: string + value: + type: string + required: + - key + type: object + type: array + type: object + azureDevOps: + properties: + accessTokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + allBranches: + type: boolean + api: + type: string + organization: + type: string + teamProject: + type: string + required: + - accessTokenRef + - organization + - teamProject + type: object + bitbucket: + properties: + allBranches: + type: boolean + appPasswordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName type: object + owner: + type: string + user: + type: string required: - - metadata - - spec + - appPasswordRef + - owner + - user type: object - required: - - elements - type: object - matrix: - x-kubernetes-preserve-unknown-fields: true - merge: - x-kubernetes-preserve-unknown-fields: true - pullRequest: - properties: bitbucketServer: properties: + allBranches: + type: boolean api: type: string basicAuth: @@ -11399,30 +16254,41 @@ spec: type: object project: type: string - repo: - type: string required: - api - project - - repo type: object + cloneProtocol: + type: string filters: items: properties: branchMatch: type: string + labelMatch: + type: string + pathsDoNotExist: + items: + type: string + type: array + pathsExist: + items: + type: string + type: array + repositoryMatch: + type: string type: object type: array gitea: properties: + allBranches: + type: boolean api: type: string insecure: type: boolean owner: type: string - repo: - type: string tokenRef: properties: key: @@ -11436,21 +16302,16 @@ spec: required: - api - owner - - repo type: object github: properties: + allBranches: + type: boolean api: type: string appSecretName: type: string - labels: - items: - type: string - type: array - owner: - type: string - repo: + organization: type: string tokenRef: properties: @@ -11463,21 +16324,22 @@ spec: - secretName type: object required: - - owner - - repo + - organization type: object gitlab: properties: + allBranches: + type: boolean api: type: string - labels: - items: - type: string - type: array - project: - type: string - pullRequestState: + group: type: string + includeSharedProjects: + type: boolean + includeSubgroups: + type: boolean + insecure: + type: boolean tokenRef: properties: key: @@ -11488,8 +16350,10 @@ spec: - key - secretName type: object + topic: + type: string required: - - project + - group type: object requeueAfterSeconds: format: int64 @@ -11654,6 +16518,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -11663,10 +16530,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -11675,10 +16548,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -11812,6 +16734,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -11821,10 +16746,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -11833,10 +16764,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -11937,654 +16917,622 @@ spec: - metadata - spec type: object - type: object - scmProvider: - properties: - azureDevOps: - properties: - accessTokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - allBranches: - type: boolean - api: - type: string - organization: - type: string - teamProject: - type: string - required: - - accessTokenRef - - organization - - teamProject - type: object - bitbucket: - properties: - allBranches: - type: boolean - appPasswordRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - owner: - type: string - user: - type: string - required: - - appPasswordRef - - owner - - user - type: object - bitbucketServer: - properties: - allBranches: - type: boolean - api: - type: string - basicAuth: - properties: - passwordRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - username: - type: string - required: - - passwordRef - - username - type: object - project: - type: string - required: - - api - - project + values: + additionalProperties: + type: string type: object - cloneProtocol: - type: string - filters: + type: object + selector: + properties: + matchExpressions: items: properties: - branchMatch: + key: type: string - labelMatch: + operator: type: string - pathsDoNotExist: - items: - type: string - type: array - pathsExist: + values: items: type: string type: array - repositoryMatch: - type: string + required: + - key + - operator type: object type: array - gitea: - properties: - allBranches: - type: boolean - api: - type: string - insecure: - type: boolean - owner: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - api - - owner + matchLabels: + additionalProperties: + type: string type: object - github: + type: object + type: object + type: array + mergeKeys: + items: + type: string + type: array + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: properties: - allBranches: - type: boolean - api: + group: type: string - appSecretName: + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: type: string - organization: + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object required: - - organization + - kind type: object - gitlab: + type: array + info: + items: properties: - allBranches: - type: boolean - api: + name: type: string - group: + value: type: string - includeSubgroups: - type: boolean - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object required: - - group + - name + - value type: object - requeueAfterSeconds: - format: int64 - type: integer - template: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string type: object - name: + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: type: string - namespace: + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: type: string - type: object - spec: - properties: - destination: - properties: - name: - type: string - namespace: - type: string - server: - type: string - type: object - ignoreDifferences: - items: - properties: - group: - type: string - jqPathExpressions: - items: - type: string - type: array - jsonPointers: - items: - type: string - type: array - kind: - type: string - managedFieldsManagers: - items: - type: string - type: array - name: - type: string - namespace: - type: string - required: - - kind - type: object - type: array - info: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - project: + type: object + components: + items: type: string - revisionHistoryLimit: - format: int64 - type: integer - source: + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: properties: - chart: + options: + additionalProperties: + type: boolean + type: object + patch: type: string - directory: + path: + type: string + target: properties: - exclude: - type: string - include: + annotationSelector: type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean - type: object - helm: - properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: + group: type: string - skipCrds: - type: boolean - valueFiles: - items: - type: string - type: array - values: + kind: type: string - version: + labelSelector: type: string - type: object - kustomize: - properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: - type: boolean - images: - items: - type: string - type: array - namePrefix: + name: type: string - nameSuffix: + namespace: type: string version: type: string type: object - path: + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: type: string - plugin: - properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - name: - type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string type: object - ref: - type: string - repoURL: + name: type: string - targetRevision: + string: type: string - required: - - repoURL type: object - sources: - items: - properties: - chart: - type: string - directory: - properties: - exclude: - type: string - include: - type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean - type: object - helm: + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: + code: type: boolean - valueFiles: - items: - type: string - type: array - values: + name: type: string - version: + value: type: string + required: + - name + - value type: object - kustomize: + type: array + libs: + items: + type: string + type: array + tlas: + items: properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: + code: type: boolean - images: - items: - type: string - type: array - namePrefix: - type: string - nameSuffix: + name: type: string - version: + value: type: string + required: + - name + - value type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string path: type: string - plugin: - properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string name: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array + namespace: + type: string + version: + type: string type: object - ref: + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: type: string - repoURL: + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: type: string - targetRevision: + value: type: string required: - - repoURL + - name + - value type: object type: array - syncPolicy: - properties: - automated: - properties: - allowEmpty: - type: boolean - prune: - type: boolean - selfHeal: - type: boolean - type: object - managedNamespaceMetadata: - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - retry: - properties: - backoff: - properties: - duration: - type: string - factor: - format: int64 - type: integer - maxDuration: - type: string - type: object - limit: - format: int64 - type: integer - type: object - syncOptions: - items: + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: type: string - type: array - type: object - required: - - destination - - project + string: + type: string + type: object + type: array type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string required: - - metadata - - spec + - repoURL type: object - type: object - selector: - properties: - matchExpressions: - items: + type: array + syncPolicy: + properties: + automated: properties: - key: - type: string - operator: - type: string - values: - items: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: type: string - type: array - required: - - key - - operator + type: object + labels: + additionalProperties: + type: string + type: object type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - type: array - mergeKeys: - items: - type: string - type: array + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + required: + - generators + - mergeKeys + type: object + plugin: + properties: + configMapRef: + properties: + name: + type: string + required: + - name + type: object + input: + properties: + parameters: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + requeueAfterSeconds: + format: int64 + type: integer template: properties: metadata: @@ -12745,6 +17693,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -12754,10 +17705,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -12766,10 +17723,59 @@ spec: items: type: string type: array - namePrefix: - type: string - nameSuffix: - type: string + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -12903,6 +17909,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -12912,10 +17921,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -12924,10 +17939,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -13028,12 +18092,89 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object required: - - generators - - mergeKeys + - configMapRef type: object pullRequest: properties: + azuredevops: + properties: + api: + type: string + labels: + items: + type: string + type: array + organization: + type: string + project: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - organization + - project + - repo + type: object + bitbucket: + properties: + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + bearerToken: + properties: + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - tokenRef + type: object + owner: + type: string + repo: + type: string + required: + - owner + - repo + type: object bitbucketServer: properties: api: @@ -13070,6 +18211,8 @@ spec: properties: branchMatch: type: string + targetBranchMatch: + type: string type: object type: array gitea: @@ -13129,6 +18272,8 @@ spec: properties: api: type: string + insecure: + type: boolean labels: items: type: string @@ -13313,6 +18458,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -13322,10 +18470,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -13334,10 +18488,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -13471,6 +18674,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -13480,10 +18686,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -13492,10 +18704,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -13599,6 +18860,26 @@ spec: type: object scmProvider: properties: + awsCodeCommit: + properties: + allBranches: + type: boolean + region: + type: string + role: + type: string + tagFilters: + items: + properties: + key: + type: string + value: + type: string + required: + - key + type: object + type: array + type: object azureDevOps: properties: accessTokenRef: @@ -13753,8 +19034,12 @@ spec: type: string group: type: string + includeSharedProjects: + type: boolean includeSubgroups: type: boolean + insecure: + type: boolean tokenRef: properties: key: @@ -13765,6 +19050,8 @@ spec: - key - secretName type: object + topic: + type: string required: - group type: object @@ -13931,6 +19218,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -13940,10 +19230,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -13952,10 +19248,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -14089,6 +19434,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -14098,10 +19446,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -14110,10 +19464,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -14214,6 +19617,10 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object type: object selector: properties: @@ -14242,6 +19649,36 @@ spec: type: array goTemplate: type: boolean + goTemplateOptions: + items: + type: string + type: array + ignoreApplicationDifferences: + items: + properties: + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + name: + type: string + type: object + type: array + preservedFields: + properties: + annotations: + items: + type: string + type: array + labels: + items: + type: string + type: array + type: object strategy: properties: rollingSync: @@ -14275,6 +19712,13 @@ spec: type: object syncPolicy: properties: + applicationsSync: + enum: + - create-only + - create-update + - create-delete + - sync + type: string preserveResourcesOnDeletion: type: boolean type: object @@ -14438,6 +19882,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -14447,10 +19894,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -14459,10 +19912,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -14596,6 +20098,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -14605,10 +20110,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -14617,10 +20128,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -14721,6 +20281,8 @@ spec: - metadata - spec type: object + templatePatch: + type: string required: - generators - template @@ -14739,10 +20301,13 @@ spec: type: string status: type: string + step: + type: string required: - application - message - status + - step type: object type: array conditions: @@ -14866,7 +20431,8 @@ spec: properties: name: description: Name is an alternate way of specifying the target - cluster by its symbolic name + cluster by its symbolic name. This must be set if Server is + not set. type: string namespace: description: Namespace specifies the target namespace for the @@ -14874,8 +20440,9 @@ spec: namespace-scoped resources that have not set a value for .metadata.namespace type: string server: - description: Server specifies the URL of the target cluster - and must be set to the Kubernetes control plane API + description: Server specifies the URL of the target cluster's + Kubernetes control plane API. This must be set if Name is + not set. type: string type: object type: array @@ -15112,9 +20679,9 @@ apiVersion: v1 kind: ServiceAccount metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller --- apiVersion: v1 @@ -15209,14 +20776,22 @@ rules: verbs: - create - list +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller rules: - apiGroups: @@ -15298,6 +20873,10 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller rules: - apiGroups: @@ -15434,6 +21013,95 @@ rules: --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: applicationset-controller + app.kubernetes.io/name: argocd-applicationset-controller + app.kubernetes.io/part-of: argocd + name: argocd-applicationset-controller +rules: +- apiGroups: + - argoproj.io + resources: + - applications + - applicationsets + - applicationsets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - argoproj.io + resources: + - applicationsets/status + verbs: + - get + - patch + - update +- apiGroups: + - argoproj.io + resources: + - appprojects + verbs: + - get +- apiGroups: + - "" + resources: + - events + verbs: + - create + - get + - list + - patch + - watch +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - update + - delete + - get + - list + - patch + - watch +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch +- apiGroups: + - apps + - extensions + resources: + - deployments + verbs: + - get + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole metadata: labels: app.kubernetes.io/component: server @@ -15465,11 +21133,24 @@ rules: - apiGroups: - argoproj.io resources: - - applications + - applications + - applicationsets + verbs: + - get + - list + - watch +- apiGroups: + - batch + resources: + - jobs + verbs: + - create +- apiGroups: + - argoproj.io + resources: + - workflows verbs: - - get - - list - - watch + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -15491,9 +21172,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller roleRef: apiGroup: rbac.authorization.k8s.io @@ -15522,6 +21203,10 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller roleRef: apiGroup: rbac.authorization.k8s.io @@ -15598,6 +21283,23 @@ subjects: --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: applicationset-controller + app.kubernetes.io/name: argocd-applicationset-controller + app.kubernetes.io/part-of: argocd + name: argocd-applicationset-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: argocd-applicationset-controller +subjects: +- kind: ServiceAccount + name: argocd-applicationset-controller + namespace: argocd +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/component: server @@ -15622,6 +21324,8 @@ metadata: name: argocd-cm --- apiVersion: v1 +data: + redis.server: argocd-redis-ha-haproxy:6379 kind: ConfigMap metadata: labels: @@ -15640,6 +21344,10 @@ metadata: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-cm --- apiVersion: v1 @@ -15971,7 +21679,9 @@ data: check inter 3s fall 1 rise 1\n use-server R1 if { srv_is_up(R1) } { nbsrv(check_if_redis_is_master_1) ge 2 }\n server R1 argocd-redis-ha-announce-1:6379 check inter 3s fall 1 rise 1\n use-server R2 if { srv_is_up(R2) } { nbsrv(check_if_redis_is_master_2) ge - 2 }\n server R2 argocd-redis-ha-announce-2:6379 check inter 3s fall 1 rise 1\n" + 2 }\n server R2 argocd-redis-ha-announce-2:6379 check inter 3s fall 1 rise 1\nfrontend + stats\n mode http\n bind :9101 \n http-request use-service prometheus-exporter + if { path /metrics }\n stats enable\n stats uri /stats\n stats refresh 10s\n" haproxy_init.sh: | HAPROXY_CONF=/data/haproxy.cfg cp /readonly/haproxy.cfg "$HAPROXY_CONF" @@ -16373,16 +22083,22 @@ metadata: --- apiVersion: v1 data: - ssh_known_hosts: |- - bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw== - github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + ssh_known_hosts: | + # This file was automatically generated by hack/update-ssh-known-hosts.sh. DO NOT EDIT + [ssh.github.com]:443 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + [ssh.github.com]:443 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + [ssh.github.com]:443 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + bitbucket.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIQmuzMBuKdWeF4+a2sjSSpBK0iqitSQ+5BM9KhpexuGt20JpTVM7u5BDZngncgrqDMbWdxMWWOGtZ9UgbqgZE= + bitbucket.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIazEu89wgQZ4bqs3d63QSMzYVa0MuJ2e2gKTKqu+UUO + bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQeJzhupRu0u0cdegZIa8e86EG2qOCsIsD1Xw0xSeiPDlCr7kq97NLmMbpKTX6Esc30NuoqEEHCuc7yWtwp8dI76EEEB1VqY9QJq6vk+aySyboD5QF61I/1WeTwu+deCbgKMGbUijeXhtfbxSxm6JwGrXrhBdofTsbKRUsrN1WoNgUa8uqN1Vx6WAJw1JHPhglEGGHea6QICwJOAr/6mrui/oB7pkaWKHj3z7d1IC4KWLtY47elvjbaTlkN04Kc/5LFEirorGYVbt15kAUlqGM65pk6ZBxtaO3+30LVlORZkxOh+LKL/BvbZ/iRNhItLqNyieoQj/uh/7Iv4uyH/cV/0b4WDSd3DptigWq84lJubb9t/DnZlrJazxyDCulTmKdOR7vs9gMTo+uoIrPSb8ScTtvw65+odKAlBj59dhnVp9zd7QUojOpXlL62Aw56U4oO+FALuevvMjiWeavKhJqlR7i5n9srYcrNV7ttmDw7kf/97P5zauIhxcjX+xHv4M= + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H - github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= - github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl kind: ConfigMap metadata: labels: @@ -16401,6 +22117,10 @@ metadata: apiVersion: v1 kind: Secret metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-secret type: Opaque --- @@ -16417,9 +22137,9 @@ apiVersion: v1 kind: Service metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller spec: ports: @@ -16444,7 +22164,8 @@ metadata: name: argocd-dex-server spec: ports: - - name: http + - appProtocol: TCP + name: http port: 5556 protocol: TCP targetPort: 5556 @@ -16480,7 +22201,9 @@ apiVersion: v1 kind: Service metadata: labels: + app.kubernetes.io/component: notifications-controller app.kubernetes.io/name: argocd-notifications-controller-metrics + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller-metrics spec: ports: @@ -16606,6 +22329,10 @@ spec: port: 6379 protocol: TCP targetPort: redis + - name: http-exporter-port + port: 9101 + protocol: TCP + targetPort: metrics-port selector: app.kubernetes.io/name: argocd-redis-ha-haproxy type: ClusterIP @@ -16673,9 +22400,9 @@ apiVersion: apps/v1 kind: Deployment metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller spec: selector: @@ -16687,10 +22414,21 @@ spec: app.kubernetes.io/name: argocd-applicationset-controller spec: containers: - - command: - - entrypoint.sh - - argocd-applicationset-controller + - args: + - /usr/local/bin/argocd-applicationset-controller env: + - name: ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_ANNOTATIONS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.global.preserved.annotations + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_LABELS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.global.preserved.labels + name: argocd-cmd-params-cm + optional: true - name: NAMESPACE valueFrom: fieldRef: @@ -16701,12 +22439,6 @@ spec: key: applicationsetcontroller.enable.leader.election name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACE - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.namespace - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER valueFrom: configMapKeyRef: @@ -16719,6 +22451,12 @@ spec: key: applicationsetcontroller.policy name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_POLICY_OVERRIDE + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.policy.override + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG valueFrom: configMapKeyRef: @@ -16749,10 +22487,64 @@ spec: key: applicationsetcontroller.enable.git.submodule name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_ROLLOUTS + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS valueFrom: configMapKeyRef: - key: applicationsetcontroller.enable.progressive.rollouts + key: applicationsetcontroller.enable.progressive.syncs + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.new.git.file.globbing + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_PLAINTEXT + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.plaintext + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_STRICT_TLS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.strict.tls + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.timeout.seconds + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_CONCURRENT_RECONCILIATIONS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.concurrent.reconciliations.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.namespaces + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.scm.root.ca.path + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.allowed.scm.providers + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.scm.providers name: argocd-cmd-params-cm optional: true image: quay.io/argoproj/argocd:latest @@ -16783,6 +22575,8 @@ spec: name: gpg-keyring - mountPath: /tmp name: tmp + - mountPath: /app/config/reposerver/tls + name: argocd-repo-server-tls serviceAccountName: argocd-applicationset-controller volumes: - configMap: @@ -16798,6 +22592,17 @@ spec: name: gpg-keyring - emptyDir: {} name: tmp + - name: argocd-repo-server-tls + secret: + items: + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key + - key: ca.crt + path: ca.crt + optional: true + secretName: argocd-repo-server-tls --- apiVersion: apps/v1 kind: Deployment @@ -16836,7 +22641,7 @@ spec: key: dexserver.disable.tls name: argocd-cmd-params-cm optional: true - image: ghcr.io/dexidp/dex:v2.35.3 + image: ghcr.io/dexidp/dex:v2.38.0 imagePullPolicy: Always name: dex ports: @@ -16861,7 +22666,7 @@ spec: name: argocd-dex-server-tls initContainers: - command: - - cp + - /bin/cp - -n - /usr/local/bin/argocd - /shared/argocd-dex @@ -16903,6 +22708,10 @@ spec: apiVersion: apps/v1 kind: Deployment metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller spec: selector: @@ -16916,8 +22725,33 @@ spec: app.kubernetes.io/name: argocd-notifications-controller spec: containers: - - command: - - argocd-notifications + - args: + - /usr/local/bin/argocd-notifications + env: + - name: ARGOCD_NOTIFICATIONS_CONTROLLER_LOGFORMAT + valueFrom: + configMapKeyRef: + key: notificationscontroller.log.format + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_NOTIFICATIONS_CONTROLLER_LOGLEVEL + valueFrom: + configMapKeyRef: + key: notificationscontroller.log.level + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_NAMESPACES + valueFrom: + configMapKeyRef: + key: application.namespaces + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED + valueFrom: + configMapKeyRef: + key: notificationscontroller.selfservice.enabled + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always livenessProbe: @@ -16976,7 +22810,10 @@ spec: template: metadata: annotations: - checksum/config: 718bbb277da8610063a7c0fd810984577c2e8ab215815a71211dfa6e20f67321 + checksum/config: 492a6adabb741e0cee39be9aa5155c41a4456629f862d0006a2d892dbecfbcae + prometheus.io/path: /metrics + prometheus.io/port: "9101" + prometheus.io/scrape: "true" labels: app.kubernetes.io/name: argocd-redis-ha-haproxy name: argocd-redis-ha-haproxy @@ -16989,7 +22826,7 @@ spec: app.kubernetes.io/name: argocd-redis-ha-haproxy topologyKey: kubernetes.io/hostname containers: - - image: haproxy:2.6.2-alpine + - image: haproxy:2.6.14-alpine imagePullPolicy: IfNotPresent lifecycle: {} livenessProbe: @@ -17002,6 +22839,8 @@ spec: ports: - containerPort: 6379 name: redis + - containerPort: 9101 + name: metrics-port readinessProbe: httpGet: path: /healthz @@ -17013,6 +22852,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true seccompProfile: type: RuntimeDefault volumeMounts: @@ -17025,7 +22865,7 @@ spec: - /readonly/haproxy_init.sh command: - sh - image: haproxy:2.6.2-alpine + image: haproxy:2.6.14-alpine imagePullPolicy: IfNotPresent name: config-init securityContext: @@ -17033,6 +22873,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true seccompProfile: type: RuntimeDefault volumeMounts: @@ -17080,7 +22921,7 @@ spec: labelSelector: matchLabels: app.kubernetes.io/name: argocd-repo-server - topologyKey: failure-domain.beta.kubernetes.io/zone + topologyKey: topology.kubernetes.io/zone weight: 100 requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: @@ -17089,11 +22930,8 @@ spec: topologyKey: kubernetes.io/hostname automountServiceAccountToken: false containers: - - command: - - entrypoint.sh - - argocd-repo-server - - --redis - - argocd-redis-ha-haproxy:6379 + - args: + - /usr/local/bin/argocd-repo-server env: - name: ARGOCD_RECONCILIATION_TIMEOUT valueFrom: @@ -17119,6 +22957,18 @@ spec: key: reposerver.parallelism.limit name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: reposerver.listen.address + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_LISTEN_METRICS_ADDRESS + valueFrom: + configMapKeyRef: + key: reposerver.metrics.listen.address + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_DISABLE_TLS valueFrom: configMapKeyRef: @@ -17179,6 +23029,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE valueFrom: configMapKeyRef: @@ -17209,12 +23071,42 @@ spec: key: reposerver.streamed.manifest.max.extracted.size name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE + valueFrom: + configMapKeyRef: + key: reposerver.helm.manifest.max.extracted.size + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE + valueFrom: + configMapKeyRef: + key: reposerver.disable.helm.manifest.max.extracted.size + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REVISION_CACHE_LOCK_TIMEOUT + valueFrom: + configMapKeyRef: + key: reposerver.revision.cache.lock.timeout + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_GIT_MODULES_ENABLED valueFrom: configMapKeyRef: key: reposerver.enable.git.submodule name: argocd-cmd-params-cm optional: true + - name: ARGOCD_GIT_LS_REMOTE_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + key: reposerver.git.lsremote.parallelism.limit + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_GIT_REQUEST_TIMEOUT + valueFrom: + configMapKeyRef: + key: reposerver.git.request.timeout + name: argocd-cmd-params-cm + optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME @@ -17269,7 +23161,7 @@ spec: name: plugins initContainers: - command: - - cp + - /bin/cp - -n - /usr/local/bin/argocd - /var/run/argocd/argocd-cmp-server @@ -17345,7 +23237,7 @@ spec: labelSelector: matchLabels: app.kubernetes.io/name: argocd-server - topologyKey: failure-domain.beta.kubernetes.io/zone + topologyKey: topology.kubernetes.io/zone weight: 100 requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: @@ -17353,10 +23245,8 @@ spec: app.kubernetes.io/name: argocd-server topologyKey: kubernetes.io/hostname containers: - - command: - - argocd-server - - --redis - - argocd-redis-ha-haproxy:6379 + - args: + - /usr/local/bin/argocd-server env: - name: ARGOCD_API_SERVER_REPLICAS value: "2" @@ -17534,12 +23424,36 @@ spec: key: server.http.cookie.maxnumber name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: server.listen.address + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_SERVER_METRICS_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: server.metrics.listen.address + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_SERVER_OTLP_ADDRESS valueFrom: configMapKeyRef: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: @@ -17552,6 +23466,24 @@ spec: key: server.enable.proxy.extension name: argocd-cmd-params-cm optional: true + - name: ARGOCD_K8SCLIENT_RETRY_MAX + valueFrom: + configMapKeyRef: + key: server.k8sclient.retry.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF + valueFrom: + configMapKeyRef: + key: server.k8sclient.retry.base.backoff + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_API_CONTENT_TYPES + valueFrom: + configMapKeyRef: + key: server.api.content.types + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always livenessProbe: @@ -17661,10 +23593,8 @@ spec: topologyKey: kubernetes.io/hostname weight: 5 containers: - - command: - - argocd-application-controller - - --redis - - argocd-redis-ha-haproxy:6379 + - args: + - /usr/local/bin/argocd-application-controller env: - name: ARGOCD_CONTROLLER_REPLICAS value: "1" @@ -17680,6 +23610,18 @@ spec: key: timeout.hard.reconciliation name: argocd-cm optional: true + - name: ARGOCD_RECONCILIATION_JITTER + valueFrom: + configMapKeyRef: + key: timeout.reconciliation.jitter + name: argocd-cm + optional: true + - name: ARGOCD_REPO_ERROR_GRACE_PERIOD_SECONDS + valueFrom: + configMapKeyRef: + key: controller.repo.error.grace.period.seconds + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER valueFrom: configMapKeyRef: @@ -17782,12 +23724,54 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: key: application.namespaces name: argocd-cmd-params-cm optional: true + - name: ARGOCD_CONTROLLER_SHARDING_ALGORITHM + valueFrom: + configMapKeyRef: + key: controller.sharding.algorithm + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + key: controller.kubectl.parallelism.limit + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_MAX + valueFrom: + configMapKeyRef: + key: controller.k8sclient.retry.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF + valueFrom: + configMapKeyRef: + key: controller.k8sclient.retry.base.backoff + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + key: controller.diff.server.side + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller @@ -17865,7 +23849,7 @@ spec: - /data/conf/redis.conf command: - redis-server - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -17904,6 +23888,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true seccompProfile: type: RuntimeDefault volumeMounts: @@ -17918,7 +23903,7 @@ spec: - /data/conf/sentinel.conf command: - redis-sentinel - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: IfNotPresent lifecycle: {} livenessProbe: @@ -17952,6 +23937,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true seccompProfile: type: RuntimeDefault volumeMounts: @@ -17970,7 +23956,7 @@ spec: value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4 - name: SENTINEL_ID_2 value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: IfNotPresent name: split-brain-fix resources: {} @@ -17979,6 +23965,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true seccompProfile: type: RuntimeDefault volumeMounts: @@ -17999,7 +23986,7 @@ spec: value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4 - name: SENTINEL_ID_2 value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: IfNotPresent name: config-init securityContext: @@ -18007,6 +23994,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true seccompProfile: type: RuntimeDefault volumeMounts: @@ -18098,6 +24086,10 @@ spec: apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller-network-policy spec: ingress: @@ -18211,6 +24203,9 @@ spec: - podSelector: matchLabels: app.kubernetes.io/name: argocd-notifications-controller + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-applicationset-controller ports: - port: 8081 protocol: TCP diff --git a/manifests/ha/namespace-install.yaml b/manifests/ha/namespace-install.yaml index 0f56433f0b492..73473875be715 100644 --- a/manifests/ha/namespace-install.yaml +++ b/manifests/ha/namespace-install.yaml @@ -12,9 +12,9 @@ apiVersion: v1 kind: ServiceAccount metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller --- apiVersion: v1 @@ -109,14 +109,22 @@ rules: verbs: - create - list +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller rules: - apiGroups: @@ -198,6 +206,10 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller rules: - apiGroups: @@ -332,9 +344,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller roleRef: apiGroup: rbac.authorization.k8s.io @@ -363,6 +375,10 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller roleRef: apiGroup: rbac.authorization.k8s.io @@ -429,6 +445,8 @@ metadata: name: argocd-cm --- apiVersion: v1 +data: + redis.server: argocd-redis-ha-haproxy:6379 kind: ConfigMap metadata: labels: @@ -447,6 +465,10 @@ metadata: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-cm --- apiVersion: v1 @@ -778,7 +800,9 @@ data: check inter 3s fall 1 rise 1\n use-server R1 if { srv_is_up(R1) } { nbsrv(check_if_redis_is_master_1) ge 2 }\n server R1 argocd-redis-ha-announce-1:6379 check inter 3s fall 1 rise 1\n use-server R2 if { srv_is_up(R2) } { nbsrv(check_if_redis_is_master_2) ge - 2 }\n server R2 argocd-redis-ha-announce-2:6379 check inter 3s fall 1 rise 1\n" + 2 }\n server R2 argocd-redis-ha-announce-2:6379 check inter 3s fall 1 rise 1\nfrontend + stats\n mode http\n bind :9101 \n http-request use-service prometheus-exporter + if { path /metrics }\n stats enable\n stats uri /stats\n stats refresh 10s\n" haproxy_init.sh: | HAPROXY_CONF=/data/haproxy.cfg cp /readonly/haproxy.cfg "$HAPROXY_CONF" @@ -1180,16 +1204,22 @@ metadata: --- apiVersion: v1 data: - ssh_known_hosts: |- - bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw== - github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + ssh_known_hosts: | + # This file was automatically generated by hack/update-ssh-known-hosts.sh. DO NOT EDIT + [ssh.github.com]:443 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + [ssh.github.com]:443 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + [ssh.github.com]:443 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + bitbucket.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIQmuzMBuKdWeF4+a2sjSSpBK0iqitSQ+5BM9KhpexuGt20JpTVM7u5BDZngncgrqDMbWdxMWWOGtZ9UgbqgZE= + bitbucket.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIazEu89wgQZ4bqs3d63QSMzYVa0MuJ2e2gKTKqu+UUO + bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQeJzhupRu0u0cdegZIa8e86EG2qOCsIsD1Xw0xSeiPDlCr7kq97NLmMbpKTX6Esc30NuoqEEHCuc7yWtwp8dI76EEEB1VqY9QJq6vk+aySyboD5QF61I/1WeTwu+deCbgKMGbUijeXhtfbxSxm6JwGrXrhBdofTsbKRUsrN1WoNgUa8uqN1Vx6WAJw1JHPhglEGGHea6QICwJOAr/6mrui/oB7pkaWKHj3z7d1IC4KWLtY47elvjbaTlkN04Kc/5LFEirorGYVbt15kAUlqGM65pk6ZBxtaO3+30LVlORZkxOh+LKL/BvbZ/iRNhItLqNyieoQj/uh/7Iv4uyH/cV/0b4WDSd3DptigWq84lJubb9t/DnZlrJazxyDCulTmKdOR7vs9gMTo+uoIrPSb8ScTtvw65+odKAlBj59dhnVp9zd7QUojOpXlL62Aw56U4oO+FALuevvMjiWeavKhJqlR7i5n9srYcrNV7ttmDw7kf/97P5zauIhxcjX+xHv4M= + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H - github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= - github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl kind: ConfigMap metadata: labels: @@ -1208,6 +1238,10 @@ metadata: apiVersion: v1 kind: Secret metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-secret type: Opaque --- @@ -1224,9 +1258,9 @@ apiVersion: v1 kind: Service metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller spec: ports: @@ -1251,7 +1285,8 @@ metadata: name: argocd-dex-server spec: ports: - - name: http + - appProtocol: TCP + name: http port: 5556 protocol: TCP targetPort: 5556 @@ -1287,7 +1322,9 @@ apiVersion: v1 kind: Service metadata: labels: + app.kubernetes.io/component: notifications-controller app.kubernetes.io/name: argocd-notifications-controller-metrics + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller-metrics spec: ports: @@ -1413,6 +1450,10 @@ spec: port: 6379 protocol: TCP targetPort: redis + - name: http-exporter-port + port: 9101 + protocol: TCP + targetPort: metrics-port selector: app.kubernetes.io/name: argocd-redis-ha-haproxy type: ClusterIP @@ -1480,9 +1521,9 @@ apiVersion: apps/v1 kind: Deployment metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller spec: selector: @@ -1494,10 +1535,21 @@ spec: app.kubernetes.io/name: argocd-applicationset-controller spec: containers: - - command: - - entrypoint.sh - - argocd-applicationset-controller + - args: + - /usr/local/bin/argocd-applicationset-controller env: + - name: ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_ANNOTATIONS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.global.preserved.annotations + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_LABELS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.global.preserved.labels + name: argocd-cmd-params-cm + optional: true - name: NAMESPACE valueFrom: fieldRef: @@ -1508,12 +1560,6 @@ spec: key: applicationsetcontroller.enable.leader.election name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACE - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.namespace - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER valueFrom: configMapKeyRef: @@ -1526,6 +1572,12 @@ spec: key: applicationsetcontroller.policy name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_POLICY_OVERRIDE + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.policy.override + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG valueFrom: configMapKeyRef: @@ -1556,10 +1608,64 @@ spec: key: applicationsetcontroller.enable.git.submodule name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_ROLLOUTS + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.progressive.syncs + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.new.git.file.globbing + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_PLAINTEXT + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.plaintext + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_STRICT_TLS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.strict.tls + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.timeout.seconds + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_CONCURRENT_RECONCILIATIONS valueFrom: configMapKeyRef: - key: applicationsetcontroller.enable.progressive.rollouts + key: applicationsetcontroller.concurrent.reconciliations.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.namespaces + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.scm.root.ca.path + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.allowed.scm.providers + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.scm.providers name: argocd-cmd-params-cm optional: true image: quay.io/argoproj/argocd:latest @@ -1590,6 +1696,8 @@ spec: name: gpg-keyring - mountPath: /tmp name: tmp + - mountPath: /app/config/reposerver/tls + name: argocd-repo-server-tls serviceAccountName: argocd-applicationset-controller volumes: - configMap: @@ -1605,6 +1713,17 @@ spec: name: gpg-keyring - emptyDir: {} name: tmp + - name: argocd-repo-server-tls + secret: + items: + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key + - key: ca.crt + path: ca.crt + optional: true + secretName: argocd-repo-server-tls --- apiVersion: apps/v1 kind: Deployment @@ -1643,7 +1762,7 @@ spec: key: dexserver.disable.tls name: argocd-cmd-params-cm optional: true - image: ghcr.io/dexidp/dex:v2.35.3 + image: ghcr.io/dexidp/dex:v2.38.0 imagePullPolicy: Always name: dex ports: @@ -1668,7 +1787,7 @@ spec: name: argocd-dex-server-tls initContainers: - command: - - cp + - /bin/cp - -n - /usr/local/bin/argocd - /shared/argocd-dex @@ -1710,6 +1829,10 @@ spec: apiVersion: apps/v1 kind: Deployment metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller spec: selector: @@ -1723,8 +1846,33 @@ spec: app.kubernetes.io/name: argocd-notifications-controller spec: containers: - - command: - - argocd-notifications + - args: + - /usr/local/bin/argocd-notifications + env: + - name: ARGOCD_NOTIFICATIONS_CONTROLLER_LOGFORMAT + valueFrom: + configMapKeyRef: + key: notificationscontroller.log.format + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_NOTIFICATIONS_CONTROLLER_LOGLEVEL + valueFrom: + configMapKeyRef: + key: notificationscontroller.log.level + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_NAMESPACES + valueFrom: + configMapKeyRef: + key: application.namespaces + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED + valueFrom: + configMapKeyRef: + key: notificationscontroller.selfservice.enabled + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always livenessProbe: @@ -1783,7 +1931,10 @@ spec: template: metadata: annotations: - checksum/config: 718bbb277da8610063a7c0fd810984577c2e8ab215815a71211dfa6e20f67321 + checksum/config: 492a6adabb741e0cee39be9aa5155c41a4456629f862d0006a2d892dbecfbcae + prometheus.io/path: /metrics + prometheus.io/port: "9101" + prometheus.io/scrape: "true" labels: app.kubernetes.io/name: argocd-redis-ha-haproxy name: argocd-redis-ha-haproxy @@ -1796,7 +1947,7 @@ spec: app.kubernetes.io/name: argocd-redis-ha-haproxy topologyKey: kubernetes.io/hostname containers: - - image: haproxy:2.6.2-alpine + - image: haproxy:2.6.14-alpine imagePullPolicy: IfNotPresent lifecycle: {} livenessProbe: @@ -1809,6 +1960,8 @@ spec: ports: - containerPort: 6379 name: redis + - containerPort: 9101 + name: metrics-port readinessProbe: httpGet: path: /healthz @@ -1820,6 +1973,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true seccompProfile: type: RuntimeDefault volumeMounts: @@ -1832,7 +1986,7 @@ spec: - /readonly/haproxy_init.sh command: - sh - image: haproxy:2.6.2-alpine + image: haproxy:2.6.14-alpine imagePullPolicy: IfNotPresent name: config-init securityContext: @@ -1840,6 +1994,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true seccompProfile: type: RuntimeDefault volumeMounts: @@ -1887,7 +2042,7 @@ spec: labelSelector: matchLabels: app.kubernetes.io/name: argocd-repo-server - topologyKey: failure-domain.beta.kubernetes.io/zone + topologyKey: topology.kubernetes.io/zone weight: 100 requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: @@ -1896,11 +2051,8 @@ spec: topologyKey: kubernetes.io/hostname automountServiceAccountToken: false containers: - - command: - - entrypoint.sh - - argocd-repo-server - - --redis - - argocd-redis-ha-haproxy:6379 + - args: + - /usr/local/bin/argocd-repo-server env: - name: ARGOCD_RECONCILIATION_TIMEOUT valueFrom: @@ -1926,6 +2078,18 @@ spec: key: reposerver.parallelism.limit name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: reposerver.listen.address + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_LISTEN_METRICS_ADDRESS + valueFrom: + configMapKeyRef: + key: reposerver.metrics.listen.address + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_DISABLE_TLS valueFrom: configMapKeyRef: @@ -1986,6 +2150,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE valueFrom: configMapKeyRef: @@ -2016,12 +2192,42 @@ spec: key: reposerver.streamed.manifest.max.extracted.size name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE + valueFrom: + configMapKeyRef: + key: reposerver.helm.manifest.max.extracted.size + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE + valueFrom: + configMapKeyRef: + key: reposerver.disable.helm.manifest.max.extracted.size + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REVISION_CACHE_LOCK_TIMEOUT + valueFrom: + configMapKeyRef: + key: reposerver.revision.cache.lock.timeout + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_GIT_MODULES_ENABLED valueFrom: configMapKeyRef: key: reposerver.enable.git.submodule name: argocd-cmd-params-cm optional: true + - name: ARGOCD_GIT_LS_REMOTE_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + key: reposerver.git.lsremote.parallelism.limit + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_GIT_REQUEST_TIMEOUT + valueFrom: + configMapKeyRef: + key: reposerver.git.request.timeout + name: argocd-cmd-params-cm + optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME @@ -2076,7 +2282,7 @@ spec: name: plugins initContainers: - command: - - cp + - /bin/cp - -n - /usr/local/bin/argocd - /var/run/argocd/argocd-cmp-server @@ -2152,7 +2358,7 @@ spec: labelSelector: matchLabels: app.kubernetes.io/name: argocd-server - topologyKey: failure-domain.beta.kubernetes.io/zone + topologyKey: topology.kubernetes.io/zone weight: 100 requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: @@ -2160,10 +2366,8 @@ spec: app.kubernetes.io/name: argocd-server topologyKey: kubernetes.io/hostname containers: - - command: - - argocd-server - - --redis - - argocd-redis-ha-haproxy:6379 + - args: + - /usr/local/bin/argocd-server env: - name: ARGOCD_API_SERVER_REPLICAS value: "2" @@ -2341,12 +2545,36 @@ spec: key: server.http.cookie.maxnumber name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: server.listen.address + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_SERVER_METRICS_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: server.metrics.listen.address + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_SERVER_OTLP_ADDRESS valueFrom: configMapKeyRef: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: @@ -2359,6 +2587,24 @@ spec: key: server.enable.proxy.extension name: argocd-cmd-params-cm optional: true + - name: ARGOCD_K8SCLIENT_RETRY_MAX + valueFrom: + configMapKeyRef: + key: server.k8sclient.retry.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF + valueFrom: + configMapKeyRef: + key: server.k8sclient.retry.base.backoff + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_API_CONTENT_TYPES + valueFrom: + configMapKeyRef: + key: server.api.content.types + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always livenessProbe: @@ -2468,10 +2714,8 @@ spec: topologyKey: kubernetes.io/hostname weight: 5 containers: - - command: - - argocd-application-controller - - --redis - - argocd-redis-ha-haproxy:6379 + - args: + - /usr/local/bin/argocd-application-controller env: - name: ARGOCD_CONTROLLER_REPLICAS value: "1" @@ -2487,6 +2731,18 @@ spec: key: timeout.hard.reconciliation name: argocd-cm optional: true + - name: ARGOCD_RECONCILIATION_JITTER + valueFrom: + configMapKeyRef: + key: timeout.reconciliation.jitter + name: argocd-cm + optional: true + - name: ARGOCD_REPO_ERROR_GRACE_PERIOD_SECONDS + valueFrom: + configMapKeyRef: + key: controller.repo.error.grace.period.seconds + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER valueFrom: configMapKeyRef: @@ -2589,12 +2845,54 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: key: application.namespaces name: argocd-cmd-params-cm optional: true + - name: ARGOCD_CONTROLLER_SHARDING_ALGORITHM + valueFrom: + configMapKeyRef: + key: controller.sharding.algorithm + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + key: controller.kubectl.parallelism.limit + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_MAX + valueFrom: + configMapKeyRef: + key: controller.k8sclient.retry.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF + valueFrom: + configMapKeyRef: + key: controller.k8sclient.retry.base.backoff + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + key: controller.diff.server.side + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller @@ -2672,7 +2970,7 @@ spec: - /data/conf/redis.conf command: - redis-server - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -2711,6 +3009,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true seccompProfile: type: RuntimeDefault volumeMounts: @@ -2725,7 +3024,7 @@ spec: - /data/conf/sentinel.conf command: - redis-sentinel - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: IfNotPresent lifecycle: {} livenessProbe: @@ -2759,6 +3058,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true seccompProfile: type: RuntimeDefault volumeMounts: @@ -2777,7 +3077,7 @@ spec: value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4 - name: SENTINEL_ID_2 value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: IfNotPresent name: split-brain-fix resources: {} @@ -2786,6 +3086,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true seccompProfile: type: RuntimeDefault volumeMounts: @@ -2806,7 +3107,7 @@ spec: value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4 - name: SENTINEL_ID_2 value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: IfNotPresent name: config-init securityContext: @@ -2814,6 +3115,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true seccompProfile: type: RuntimeDefault volumeMounts: @@ -2905,6 +3207,10 @@ spec: apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller-network-policy spec: ingress: @@ -3018,6 +3324,9 @@ spec: - podSelector: matchLabels: app.kubernetes.io/name: argocd-notifications-controller + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-applicationset-controller ports: - port: 8081 protocol: TCP diff --git a/manifests/install.yaml b/manifests/install.yaml index ce867269fafaa..282e6c9f66e7d 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -287,8 +287,15 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be + passed to helm template, defined as a map. This takes + precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -303,12 +310,22 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources for @@ -326,6 +343,10 @@ spec: definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether to + apply common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -334,6 +355,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -549,8 +624,15 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be + passed to helm template, defined as a map. This takes + precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -565,12 +647,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -589,6 +682,10 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -597,6 +694,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -718,7 +869,8 @@ spec: properties: name: description: Name is an alternate way of specifying the target - cluster by its symbolic name + cluster by its symbolic name. This must be set if Server is + not set. type: string namespace: description: Namespace specifies the target namespace for the @@ -726,8 +878,9 @@ spec: namespace-scoped resources that have not set a value for .metadata.namespace type: string server: - description: Server specifies the URL of the target cluster and - must be set to the Kubernetes control plane API + description: Server specifies the URL of the target cluster's + Kubernetes control plane API. This must be set if Name is not + set. type: string type: object ignoreDifferences: @@ -927,8 +1080,15 @@ spec: type: array values: description: Values specifies Helm values to be passed to - helm template, typically defined as a block + helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be passed + to helm template, defined as a map. This takes precedence + over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -943,12 +1103,22 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether to + apply env variables substitution for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize components + to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize apps @@ -965,6 +1135,10 @@ spec: definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether to apply + common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -973,6 +1147,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize adds + to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas override + specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -1180,8 +1408,15 @@ spec: type: array values: description: Values specifies Helm values to be passed to - helm template, typically defined as a block + helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be passed + to helm template, defined as a map. This takes precedence + over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -1196,12 +1431,22 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize components + to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize @@ -1219,6 +1464,10 @@ spec: definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether to apply + common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -1227,6 +1476,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas override + specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -1319,7 +1622,7 @@ spec: as part of automated sync (default: false)' type: boolean selfHeal: - description: 'SelfHeal specifes whether to revert resources + description: 'SelfHeal specifies whether to revert resources back to their desired state upon modification in the cluster (default: false)' type: boolean @@ -1383,7 +1686,7 @@ spec: conditions items: description: ApplicationCondition contains details about an application - condition, which is usally an error or warning + condition, which is usually an error or warning properties: lastTransitionTime: description: LastTransitionTime is the time the condition was @@ -1402,6 +1705,10 @@ spec: - type type: object type: array + controllerNamespace: + description: ControllerNamespace indicates the namespace in which + the application controller is located + type: string health: description: Health contains information about the application's current health status @@ -1435,6 +1742,19 @@ spec: description: ID is an auto incrementing identifier of the RevisionHistory format: int64 type: integer + initiatedBy: + description: InitiatedBy contains information about who initiated + the operations + properties: + automated: + description: Automated is set to true if operation was initiated + automatically by the application controller. + type: boolean + username: + description: Username contains the name of a user who started + operation + type: string + type: object revision: description: Revision holds the revision the sync was performed against @@ -1581,8 +1901,15 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. ValuesObject + takes precedence over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to be + passed to helm template, defined as a map. This takes + precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -1597,12 +1924,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -1621,6 +1959,10 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -1629,6 +1971,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -1846,8 +2242,16 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. + ValuesObject takes precedence over Values, so use + one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to + be passed to helm template, defined as a map. This + takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -1862,12 +2266,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -1886,6 +2301,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -1894,6 +2314,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -2256,8 +2730,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined as - a block + a block. ValuesObject takes precedence over + Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as a + map. This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -2272,12 +2753,24 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution + for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before + building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations @@ -2296,6 +2789,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors + or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -2304,14 +2802,68 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string - version: - description: Version controls which version of - Kustomize to use for rendering manifests + namespace: + description: Namespace sets the namespace that + Kustomize adds to all resources type: string - type: object - path: - description: Path is a directory path within the Git - repository, and is only valid for applications sourced + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array + version: + description: Version controls which version of + Kustomize to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git + repository, and is only valid for applications sourced from Git. type: string plugin: @@ -2537,8 +3089,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined - as a block + as a block. ValuesObject takes precedence + over Values, so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as + a map. This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -2555,12 +3114,24 @@ spec: additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution + for annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of + kustomize components to add to the kustomization + before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations @@ -2579,6 +3150,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies + whether to apply common labels to resource + selectors or not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -2587,6 +3163,61 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that + Kustomize adds to all resources + type: string + patches: + description: Patches is a list of Kustomize + patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize + Replicas override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -2720,6 +3351,19 @@ spec: syncResult: description: SyncResult is the result of a Sync operation properties: + managedNamespaceMetadata: + description: ManagedNamespaceMetadata contains the current + sync state of managed namespace metadata + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object resources: description: Resources contains a list of sync result items for each individual resource in a sync operation @@ -2922,8 +3566,16 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. + ValuesObject takes precedence over Values, so use + one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to + be passed to helm template, defined as a map. This + takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -2938,12 +3590,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -2962,6 +3625,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -2970,6 +3638,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -3198,8 +3920,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined as - a block + a block. ValuesObject takes precedence over Values, + so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as a map. + This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -3214,12 +3943,24 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution for + annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before + building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -3238,6 +3979,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -3246,6 +3992,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -3428,7 +4228,8 @@ spec: properties: name: description: Name is an alternate way of specifying the - target cluster by its symbolic name + target cluster by its symbolic name. This must be set + if Server is not set. type: string namespace: description: Namespace specifies the target namespace @@ -3437,10 +4238,47 @@ spec: not set a value for .metadata.namespace type: string server: - description: Server specifies the URL of the target cluster - and must be set to the Kubernetes control plane API + description: Server specifies the URL of the target cluster's + Kubernetes control plane API. This must be set if Name + is not set. type: string type: object + ignoreDifferences: + description: IgnoreDifferences is a reference to the application's + ignored differences used for comparison + items: + description: ResourceIgnoreDifferences contains resource + filter and list of json paths which should be ignored + during comparison with live state. + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + description: ManagedFieldsManagers is a list of trusted + managers. Fields mutated by those managers will take + precedence over the desired state defined in the SCM + and won't be displayed in diffs + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array source: description: Source is a reference to the application's source used for comparison @@ -3579,8 +4417,16 @@ spec: type: array values: description: Values specifies Helm values to be passed - to helm template, typically defined as a block + to helm template, typically defined as a block. + ValuesObject takes precedence over Values, so use + one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values to + be passed to helm template, defined as a map. This + takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -3595,12 +4441,23 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies whether + to apply env variables substitution for annotation + values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -3619,6 +4476,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -3627,6 +4489,60 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources + type: string + patches: + description: Patches is a list of Kustomize patches + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array version: description: Version controls which version of Kustomize to use for rendering manifests @@ -3855,8 +4771,15 @@ spec: values: description: Values specifies Helm values to be passed to helm template, typically defined as - a block + a block. ValuesObject takes precedence over Values, + so use one or the other. type: string + valuesObject: + description: ValuesObject specifies Helm values + to be passed to helm template, defined as a map. + This takes precedence over Values. + type: object + x-kubernetes-preserve-unknown-fields: true version: description: Version is the Helm version to use for templating ("3") @@ -3871,12 +4794,24 @@ spec: description: CommonAnnotations is a list of additional annotations to add to rendered manifests type: object + commonAnnotationsEnvsubst: + description: CommonAnnotationsEnvsubst specifies + whether to apply env variables substitution for + annotation values + type: boolean commonLabels: additionalProperties: type: string description: CommonLabels is a list of additional labels to add to rendered manifests type: object + components: + description: Components specifies a list of kustomize + components to add to the kustomization before + building + items: + type: string + type: array forceCommonAnnotations: description: ForceCommonAnnotations specifies whether to force applying common annotations to resources @@ -3895,6 +4830,11 @@ spec: image definition in the format [old_image_name=]: type: string type: array + labelWithoutSelector: + description: LabelWithoutSelector specifies whether + to apply common labels to resource selectors or + not + type: boolean namePrefix: description: NamePrefix is a prefix appended to resources for Kustomize apps @@ -3903,33 +4843,87 @@ spec: description: NameSuffix is a suffix appended to resources for Kustomize apps type: string - version: - description: Version controls which version of Kustomize - to use for rendering manifests + namespace: + description: Namespace sets the namespace that Kustomize + adds to all resources type: string - type: object - path: - description: Path is a directory path within the Git - repository, and is only valid for applications sourced - from Git. - type: string - plugin: - description: Plugin holds config management plugin specific - options - properties: - env: - description: Env is a list of environment variable - entries + patches: + description: Patches is a list of Kustomize patches items: - description: EnvEntry represents an entry in the - application's environment properties: - name: - description: Name is the name of the variable, - usually expressed in uppercase + options: + additionalProperties: + type: boolean + type: object + patch: type: string - value: - description: Value is the value of the variable + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + description: Replicas is a list of Kustomize Replicas + override specifications + items: + properties: + count: + anyOf: + - type: integer + - type: string + description: Number of replicas + x-kubernetes-int-or-string: true + name: + description: Name of Deployment or StatefulSet + type: string + required: + - count + - name + type: object + type: array + version: + description: Version controls which version of Kustomize + to use for rendering manifests + type: string + type: object + path: + description: Path is a directory path within the Git + repository, and is only valid for applications sourced + from Git. + type: string + plugin: + description: Plugin holds config management plugin specific + options + properties: + env: + description: Env is a list of environment variable + entries + items: + description: EnvEntry represents an entry in the + application's environment + properties: + name: + description: Name is the name of the variable, + usually expressed in uppercase + type: string + value: + description: Value is the value of the variable type: string required: - name @@ -4043,6 +5037,8 @@ spec: type: object spec: properties: + applyNestedSelectors: + type: boolean generators: items: properties: @@ -4238,6 +5234,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4247,10 +5246,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4259,10 +5264,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4396,6 +5450,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4405,10 +5462,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4417,10 +5480,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4713,6 +5825,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4722,10 +5837,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4734,10 +5855,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -4871,6 +6041,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -4880,10 +6053,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -4892,10 +6071,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5192,6 +6420,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5201,10 +6432,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5213,10 +6450,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5350,6 +6636,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5359,10 +6648,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5371,25 +6666,74 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string - version: + namespace: type: string - type: object - path: - type: string - plugin: - properties: - env: + patches: items: properties: - name: + options: + additionalProperties: + type: boolean + type: object + patch: type: string - value: + path: type: string - required: + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: - name - value type: object @@ -5475,6 +6819,10 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object required: - repoURL - revision @@ -5485,6 +6833,8 @@ spec: items: x-kubernetes-preserve-unknown-fields: true type: array + elementsYaml: + type: string template: properties: metadata: @@ -5645,6 +6995,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5654,10 +7007,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5666,10 +7025,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5803,6 +7211,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -5812,10 +7223,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -5824,10 +7241,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -5928,8 +7394,6 @@ spec: - metadata - spec type: object - required: - - elements type: object matrix: properties: @@ -6128,6 +7592,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6137,10 +7604,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6149,10 +7622,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -6286,6 +7808,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6295,10 +7820,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6307,10 +7838,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -6603,6 +8183,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6612,10 +8195,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6624,10 +8213,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -6761,6 +8399,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -6770,10 +8411,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -6782,10 +8429,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7082,6 +8778,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7091,10 +8790,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7103,10 +8808,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7240,6 +8994,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7249,10 +9006,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7261,10 +9024,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7365,6 +9177,10 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object required: - repoURL - revision @@ -7375,6 +9191,8 @@ spec: items: x-kubernetes-preserve-unknown-fields: true type: array + elementsYaml: + type: string template: properties: metadata: @@ -7535,6 +9353,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7544,10 +9365,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7556,10 +9383,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7693,6 +9569,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -7702,10 +9581,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -7714,10 +9599,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -7818,153 +9752,49 @@ spec: - metadata - spec type: object - required: - - elements type: object matrix: x-kubernetes-preserve-unknown-fields: true merge: x-kubernetes-preserve-unknown-fields: true - pullRequest: + plugin: properties: - bitbucketServer: + configMapRef: properties: - api: - type: string - basicAuth: - properties: - passwordRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - username: - type: string - required: - - passwordRef - - username - type: object - project: - type: string - repo: + name: type: string required: - - api - - project - - repo + - name type: object - filters: - items: - properties: - branchMatch: - type: string - type: object - type: array - gitea: + input: properties: - api: - type: string - insecure: - type: boolean - owner: - type: string - repo: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName + parameters: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true type: object - required: - - api - - owner - - repo type: object - github: + requeueAfterSeconds: + format: int64 + type: integer + template: properties: - api: - type: string - appSecretName: - type: string - labels: - items: - type: string - type: array - owner: - type: string - repo: - type: string - tokenRef: + metadata: properties: - key: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - owner - - repo - type: object - gitlab: - properties: - api: - type: string - labels: - items: - type: string - type: array - project: - type: string - pullRequestState: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - project - type: object - requeueAfterSeconds: - format: int64 - type: integer - template: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: + namespace: type: string type: object spec: @@ -8106,6 +9936,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8115,10 +9948,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8127,10 +9966,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -8264,6 +10152,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8273,10 +10164,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8285,10 +10182,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -8389,12 +10335,30 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object + required: + - configMapRef type: object - scmProvider: + pullRequest: properties: - azureDevOps: + azuredevops: properties: - accessTokenRef: + api: + type: string + labels: + items: + type: string + type: array + organization: + type: string + project: + type: string + repo: + type: string + tokenRef: properties: key: type: string @@ -8404,46 +10368,58 @@ spec: - key - secretName type: object - allBranches: - type: boolean - api: - type: string - organization: - type: string - teamProject: - type: string required: - - accessTokenRef - organization - - teamProject + - project + - repo type: object bitbucket: properties: - allBranches: - type: boolean - appPasswordRef: + api: + type: string + basicAuth: properties: - key: - type: string - secretName: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: type: string required: - - key - - secretName + - passwordRef + - username + type: object + bearerToken: + properties: + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - tokenRef type: object owner: type: string - user: + repo: type: string required: - - appPasswordRef - owner - - user + - repo type: object bitbucketServer: properties: - allBranches: - type: boolean api: type: string basicAuth: @@ -8466,41 +10442,32 @@ spec: type: object project: type: string + repo: + type: string required: - api - project + - repo type: object - cloneProtocol: - type: string filters: items: properties: branchMatch: type: string - labelMatch: - type: string - pathsDoNotExist: - items: - type: string - type: array - pathsExist: - items: - type: string - type: array - repositoryMatch: + targetBranchMatch: type: string type: object type: array gitea: properties: - allBranches: - type: boolean api: type: string insecure: type: boolean owner: type: string + repo: + type: string tokenRef: properties: key: @@ -8514,16 +10481,21 @@ spec: required: - api - owner + - repo type: object github: properties: - allBranches: - type: boolean api: type: string appSecretName: type: string - organization: + labels: + items: + type: string + type: array + owner: + type: string + repo: type: string tokenRef: properties: @@ -8536,18 +10508,23 @@ spec: - secretName type: object required: - - organization + - owner + - repo type: object gitlab: properties: - allBranches: - type: boolean api: type: string - group: - type: string - includeSubgroups: + insecure: type: boolean + labels: + items: + type: string + type: array + project: + type: string + pullRequestState: + type: string tokenRef: properties: key: @@ -8559,7 +10536,7 @@ spec: - secretName type: object required: - - group + - project type: object requeueAfterSeconds: format: int64 @@ -8724,6 +10701,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8733,10 +10713,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8745,23 +10731,72 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string - version: + namespace: type: string - type: object - path: - type: string - plugin: - properties: - env: + patches: items: properties: - name: - type: string - value: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: type: string required: - name @@ -8882,6 +10917,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -8891,10 +10929,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -8903,10 +10947,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -9008,511 +11101,203 @@ spec: - spec type: object type: object - selector: + scmProvider: properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - type: array - template: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - destination: - properties: - name: - type: string - namespace: - type: string - server: - type: string - type: object - ignoreDifferences: - items: + awsCodeCommit: properties: - group: + allBranches: + type: boolean + region: type: string - jqPathExpressions: - items: - type: string - type: array - jsonPointers: - items: - type: string - type: array - kind: + role: type: string - managedFieldsManagers: + tagFilters: items: - type: string + properties: + key: + type: string + value: + type: string + required: + - key + type: object type: array - name: + type: object + azureDevOps: + properties: + accessTokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + allBranches: + type: boolean + api: type: string - namespace: + organization: + type: string + teamProject: type: string required: - - kind + - accessTokenRef + - organization + - teamProject type: object - type: array - info: - items: + bitbucket: properties: - name: + allBranches: + type: boolean + appPasswordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + owner: type: string - value: + user: type: string required: - - name - - value + - appPasswordRef + - owner + - user type: object - type: array - project: - type: string - revisionHistoryLimit: - format: int64 - type: integer - source: - properties: - chart: - type: string - directory: + bitbucketServer: + properties: + allBranches: + type: boolean + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + project: + type: string + required: + - api + - project + type: object + cloneProtocol: + type: string + filters: + items: properties: - exclude: + branchMatch: type: string - include: + labelMatch: type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean - type: object - helm: - properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: - type: boolean - valueFiles: + pathsDoNotExist: items: type: string type: array - values: - type: string - version: - type: string - type: object - kustomize: - properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: - type: boolean - images: + pathsExist: items: type: string type: array - namePrefix: - type: string - nameSuffix: - type: string - version: - type: string - type: object - path: - type: string - plugin: - properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - name: + repositoryMatch: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array type: object - ref: - type: string - repoURL: - type: string - targetRevision: - type: string - required: - - repoURL - type: object - sources: - items: + type: array + gitea: properties: - chart: + allBranches: + type: boolean + api: type: string - directory: + insecure: + type: boolean + owner: + type: string + tokenRef: properties: - exclude: + key: type: string - include: + secretName: type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean + required: + - key + - secretName type: object - helm: + required: + - api + - owner + type: object + github: + properties: + allBranches: + type: boolean + api: + type: string + appSecretName: + type: string + organization: + type: string + tokenRef: properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: - type: boolean - valueFiles: - items: - type: string - type: array - values: + key: type: string - version: + secretName: type: string + required: + - key + - secretName type: object - kustomize: - properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: - type: boolean - images: - items: - type: string - type: array - namePrefix: - type: string - nameSuffix: - type: string - version: - type: string - type: object - path: + required: + - organization + type: object + gitlab: + properties: + allBranches: + type: boolean + api: type: string - plugin: + group: + type: string + includeSharedProjects: + type: boolean + includeSubgroups: + type: boolean + insecure: + type: boolean + tokenRef: properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - name: + key: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array + secretName: + type: string + required: + - key + - secretName type: object - ref: - type: string - repoURL: - type: string - targetRevision: + topic: type: string required: - - repoURL - type: object - type: array - syncPolicy: - properties: - automated: - properties: - allowEmpty: - type: boolean - prune: - type: boolean - selfHeal: - type: boolean - type: object - managedNamespaceMetadata: - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - retry: - properties: - backoff: - properties: - duration: - type: string - factor: - format: int64 - type: integer - maxDuration: - type: string - type: object - limit: - format: int64 - type: integer - type: object - syncOptions: - items: - type: string - type: array - type: object - required: - - destination - - project - type: object - required: - - metadata - - spec - type: object - required: - - generators - type: object - merge: - properties: - generators: - items: - properties: - clusterDecisionResource: - properties: - configMapRef: - type: string - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object + - group type: object - name: - type: string requeueAfterSeconds: format: int64 type: integer @@ -9676,6 +11461,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -9685,10 +11473,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -9697,10 +11491,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -9834,6 +11677,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -9843,10 +11689,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -9855,10 +11707,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -9963,34 +11864,2392 @@ spec: additionalProperties: type: string type: object - required: - - configMapRef type: object - clusters: + selector: properties: - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + type: object + type: array + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: items: type: string type: array - required: - - key - - operator + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array type: object - type: array - matchLabels: - additionalProperties: + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + required: + - generators + type: object + merge: + properties: + generators: + items: + properties: + clusterDecisionResource: + properties: + configMapRef: + type: string + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + name: + type: string + requeueAfterSeconds: + format: int64 + type: integer + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + required: + - configMapRef + type: object + clusters: + properties: + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + type: object + git: + properties: + directories: + items: + properties: + exclude: + type: boolean + path: + type: string + required: + - path + type: object + type: array + files: + items: + properties: + path: + type: string + required: + - path + type: object + type: array + pathParamPrefix: + type: string + repoURL: + type: string + requeueAfterSeconds: + format: int64 + type: integer + revision: + type: string + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: + type: string + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + kind: + type: string + managedFieldsManagers: + items: + type: string + type: array + name: + type: string + namespace: + type: string + required: + - kind + type: object + type: array + info: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + libs: + items: + type: string + type: array + tlas: + items: + properties: + code: + type: boolean + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string + path: + type: string + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + type: array + syncPolicy: + properties: + automated: + properties: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string type: object + required: + - repoURL + - revision + type: object + list: + properties: + elements: + items: + x-kubernetes-preserve-unknown-fields: true + type: array + elementsYaml: + type: string template: properties: metadata: @@ -10151,6 +14410,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10160,10 +14422,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10172,10 +14440,59 @@ spec: items: type: string type: array - namePrefix: - type: string - nameSuffix: - type: string + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10309,6 +14626,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10318,10 +14638,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10330,10 +14656,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10434,42 +14809,30 @@ spec: - metadata - spec type: object - values: - additionalProperties: - type: string - type: object type: object - git: + matrix: + x-kubernetes-preserve-unknown-fields: true + merge: + x-kubernetes-preserve-unknown-fields: true + plugin: properties: - directories: - items: - properties: - exclude: - type: boolean - path: - type: string - required: - - path - type: object - type: array - files: - items: - properties: - path: - type: string - required: - - path - type: object - type: array - pathParamPrefix: - type: string - repoURL: - type: string + configMapRef: + properties: + name: + type: string + required: + - name + type: object + input: + properties: + parameters: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + type: object + type: object requeueAfterSeconds: format: int64 type: integer - revision: - type: string template: properties: metadata: @@ -10630,6 +14993,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10639,10 +15005,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10651,10 +15023,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10788,6 +15209,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -10797,10 +15221,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -10809,10 +15239,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -10906,23 +15385,219 @@ spec: type: array type: object required: - - destination - - project + - destination + - project + type: object + required: + - metadata + - spec + type: object + values: + additionalProperties: + type: string + type: object + required: + - configMapRef + type: object + pullRequest: + properties: + azuredevops: + properties: + api: + type: string + labels: + items: + type: string + type: array + organization: + type: string + project: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - organization + - project + - repo + type: object + bitbucket: + properties: + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + bearerToken: + properties: + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - tokenRef + type: object + owner: + type: string + repo: + type: string + required: + - owner + - repo + type: object + bitbucketServer: + properties: + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username type: object + project: + type: string + repo: + type: string required: - - metadata - - spec + - api + - project + - repo type: object - required: - - repoURL - - revision - type: object - list: - properties: - elements: + filters: items: - x-kubernetes-preserve-unknown-fields: true + properties: + branchMatch: + type: string + targetBranchMatch: + type: string + type: object type: array + gitea: + properties: + api: + type: string + insecure: + type: boolean + owner: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - api + - owner + - repo + type: object + github: + properties: + api: + type: string + appSecretName: + type: string + labels: + items: + type: string + type: array + owner: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - owner + - repo + type: object + gitlab: + properties: + api: + type: string + insecure: + type: boolean + labels: + items: + type: string + type: array + project: + type: string + pullRequestState: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - project + type: object + requeueAfterSeconds: + format: int64 + type: integer template: properties: metadata: @@ -11083,6 +15758,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -11092,10 +15770,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -11104,10 +15788,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -11241,6 +15974,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -11250,10 +15986,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -11262,10 +16004,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -11366,17 +16157,81 @@ spec: - metadata - spec type: object - required: - - elements type: object - matrix: - x-kubernetes-preserve-unknown-fields: true - merge: - x-kubernetes-preserve-unknown-fields: true - pullRequest: + scmProvider: properties: + awsCodeCommit: + properties: + allBranches: + type: boolean + region: + type: string + role: + type: string + tagFilters: + items: + properties: + key: + type: string + value: + type: string + required: + - key + type: object + type: array + type: object + azureDevOps: + properties: + accessTokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + allBranches: + type: boolean + api: + type: string + organization: + type: string + teamProject: + type: string + required: + - accessTokenRef + - organization + - teamProject + type: object + bitbucket: + properties: + allBranches: + type: boolean + appPasswordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + owner: + type: string + user: + type: string + required: + - appPasswordRef + - owner + - user + type: object bitbucketServer: properties: + allBranches: + type: boolean api: type: string basicAuth: @@ -11399,30 +16254,41 @@ spec: type: object project: type: string - repo: - type: string required: - api - project - - repo type: object + cloneProtocol: + type: string filters: items: properties: branchMatch: type: string + labelMatch: + type: string + pathsDoNotExist: + items: + type: string + type: array + pathsExist: + items: + type: string + type: array + repositoryMatch: + type: string type: object type: array gitea: properties: + allBranches: + type: boolean api: type: string insecure: type: boolean owner: type: string - repo: - type: string tokenRef: properties: key: @@ -11436,21 +16302,16 @@ spec: required: - api - owner - - repo type: object github: properties: + allBranches: + type: boolean api: type: string appSecretName: type: string - labels: - items: - type: string - type: array - owner: - type: string - repo: + organization: type: string tokenRef: properties: @@ -11463,21 +16324,22 @@ spec: - secretName type: object required: - - owner - - repo + - organization type: object gitlab: properties: + allBranches: + type: boolean api: type: string - labels: - items: - type: string - type: array - project: - type: string - pullRequestState: + group: type: string + includeSharedProjects: + type: boolean + includeSubgroups: + type: boolean + insecure: + type: boolean tokenRef: properties: key: @@ -11488,8 +16350,10 @@ spec: - key - secretName type: object + topic: + type: string required: - - project + - group type: object requeueAfterSeconds: format: int64 @@ -11654,6 +16518,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -11663,10 +16530,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -11675,10 +16548,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -11812,6 +16734,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -11821,10 +16746,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -11833,10 +16764,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -11937,654 +16917,622 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object type: object - scmProvider: + selector: properties: - azureDevOps: - properties: - accessTokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - allBranches: - type: boolean - api: - type: string - organization: - type: string - teamProject: - type: string - required: - - accessTokenRef - - organization - - teamProject - type: object - bitbucket: - properties: - allBranches: - type: boolean - appPasswordRef: - properties: - key: - type: string - secretName: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: type: string - required: - - key - - secretName - type: object - owner: - type: string - user: - type: string - required: - - appPasswordRef - - owner - - user + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string type: object - bitbucketServer: - properties: - allBranches: - type: boolean - api: - type: string - basicAuth: - properties: - passwordRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - username: - type: string - required: - - passwordRef - - username - type: object - project: + type: object + type: object + type: array + mergeKeys: + items: + type: string + type: array + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + destination: + properties: + name: + type: string + namespace: + type: string + server: + type: string + type: object + ignoreDifferences: + items: + properties: + group: type: string - required: - - api - - project - type: object - cloneProtocol: - type: string - filters: - items: - properties: - branchMatch: - type: string - labelMatch: + jqPathExpressions: + items: type: string - pathsDoNotExist: - items: - type: string - type: array - pathsExist: - items: - type: string - type: array - repositoryMatch: + type: array + jsonPointers: + items: type: string - type: object - type: array - gitea: - properties: - allBranches: - type: boolean - api: - type: string - insecure: - type: boolean - owner: - type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object - required: - - api - - owner - type: object - github: - properties: - allBranches: - type: boolean - api: + type: array + kind: type: string - appSecretName: + managedFieldsManagers: + items: + type: string + type: array + name: type: string - organization: + namespace: type: string - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object required: - - organization + - kind type: object - gitlab: + type: array + info: + items: properties: - allBranches: - type: boolean - api: + name: type: string - group: + value: type: string - includeSubgroups: - type: boolean - tokenRef: - properties: - key: - type: string - secretName: - type: string - required: - - key - - secretName - type: object required: - - group + - name + - value type: object - requeueAfterSeconds: - format: int64 - type: integer - template: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - destination: - properties: - name: - type: string - namespace: - type: string - server: - type: string - type: object - ignoreDifferences: - items: - properties: - group: - type: string - jqPathExpressions: - items: - type: string - type: array - jsonPointers: - items: - type: string - type: array - kind: - type: string - managedFieldsManagers: - items: - type: string - type: array - name: - type: string - namespace: - type: string - required: - - kind - type: object - type: array - info: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - project: - type: string - revisionHistoryLimit: - format: int64 - type: integer - source: - properties: - chart: - type: string - directory: - properties: - exclude: - type: string - include: - type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean - type: object - helm: + type: array + project: + type: string + revisionHistoryLimit: + format: int64 + type: integer + source: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: + code: type: boolean - valueFiles: - items: - type: string - type: array - values: + name: type: string - version: + value: type: string + required: + - name + - value type: object - kustomize: + type: array + libs: + items: + type: string + type: array + tlas: + items: properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: + code: type: boolean - images: - items: - type: string - type: array - namePrefix: - type: string - nameSuffix: + name: type: string - version: + value: type: string + required: + - name + - value type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string path: type: string - plugin: + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string name: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array + namespace: + type: string + version: + type: string type: object - ref: - type: string - repoURL: - type: string - targetRevision: + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: type: string required: - - repoURL - type: object - sources: - items: - properties: - chart: - type: string - directory: - properties: - exclude: - type: string - include: - type: string - jsonnet: - properties: - extVars: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - libs: - items: - type: string - type: array - tlas: - items: - properties: - code: - type: boolean - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - type: object - recurse: - type: boolean - type: object - helm: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: + type: string + string: + type: string + type: object + type: array + type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string + required: + - repoURL + type: object + sources: + items: + properties: + chart: + type: string + directory: + properties: + exclude: + type: string + include: + type: string + jsonnet: + properties: + extVars: + items: properties: - fileParameters: - items: - properties: - name: - type: string - path: - type: string - type: object - type: array - ignoreMissingValueFiles: - type: boolean - parameters: - items: - properties: - forceString: - type: boolean - name: - type: string - value: - type: string - type: object - type: array - passCredentials: - type: boolean - releaseName: - type: string - skipCrds: + code: type: boolean - valueFiles: - items: - type: string - type: array - values: + name: type: string - version: + value: type: string + required: + - name + - value type: object - kustomize: + type: array + libs: + items: + type: string + type: array + tlas: + items: properties: - commonAnnotations: - additionalProperties: - type: string - type: object - commonLabels: - additionalProperties: - type: string - type: object - forceCommonAnnotations: - type: boolean - forceCommonLabels: + code: type: boolean - images: - items: - type: string - type: array - namePrefix: - type: string - nameSuffix: + name: type: string - version: + value: type: string + required: + - name + - value type: object + type: array + type: object + recurse: + type: boolean + type: object + helm: + properties: + fileParameters: + items: + properties: + name: + type: string path: type: string - plugin: + type: object + type: array + ignoreMissingValueFiles: + type: boolean + parameters: + items: + properties: + forceString: + type: boolean + name: + type: string + value: + type: string + type: object + type: array + passCredentials: + type: boolean + releaseName: + type: string + skipCrds: + type: boolean + valueFiles: + items: + type: string + type: array + values: + type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + kustomize: + properties: + commonAnnotations: + additionalProperties: + type: string + type: object + commonAnnotationsEnvsubst: + type: boolean + commonLabels: + additionalProperties: + type: string + type: object + components: + items: + type: string + type: array + forceCommonAnnotations: + type: boolean + forceCommonLabels: + type: boolean + images: + items: + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: properties: - env: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string name: type: string - parameters: - items: - properties: - array: - items: - type: string - type: array - map: - additionalProperties: - type: string - type: object - name: - type: string - string: - type: string - type: object - type: array + namespace: + type: string + version: + type: string type: object - ref: + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: type: string - repoURL: + required: + - count + - name + type: object + type: array + version: + type: string + type: object + path: + type: string + plugin: + properties: + env: + items: + properties: + name: type: string - targetRevision: + value: type: string required: - - repoURL + - name + - value type: object type: array - syncPolicy: - properties: - automated: - properties: - allowEmpty: - type: boolean - prune: - type: boolean - selfHeal: - type: boolean - type: object - managedNamespaceMetadata: - properties: - annotations: - additionalProperties: - type: string - type: object - labels: - additionalProperties: - type: string - type: object - type: object - retry: - properties: - backoff: - properties: - duration: - type: string - factor: - format: int64 - type: integer - maxDuration: - type: string - type: object - limit: - format: int64 - type: integer - type: object - syncOptions: - items: + name: + type: string + parameters: + items: + properties: + array: + items: + type: string + type: array + map: + additionalProperties: + type: string + type: object + name: type: string - type: array - type: object - required: - - destination - - project + string: + type: string + type: object + type: array type: object + ref: + type: string + repoURL: + type: string + targetRevision: + type: string required: - - metadata - - spec + - repoURL type: object - type: object - selector: - properties: - matchExpressions: - items: + type: array + syncPolicy: + properties: + automated: properties: - key: - type: string - operator: - type: string - values: - items: + allowEmpty: + type: boolean + prune: + type: boolean + selfHeal: + type: boolean + type: object + managedNamespaceMetadata: + properties: + annotations: + additionalProperties: type: string - type: array - required: - - key - - operator + type: object + labels: + additionalProperties: + type: string + type: object type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - type: array - mergeKeys: - items: - type: string - type: array + retry: + properties: + backoff: + properties: + duration: + type: string + factor: + format: int64 + type: integer + maxDuration: + type: string + type: object + limit: + format: int64 + type: integer + type: object + syncOptions: + items: + type: string + type: array + type: object + required: + - destination + - project + type: object + required: + - metadata + - spec + type: object + required: + - generators + - mergeKeys + type: object + plugin: + properties: + configMapRef: + properties: + name: + type: string + required: + - name + type: object + input: + properties: + parameters: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + requeueAfterSeconds: + format: int64 + type: integer template: properties: metadata: @@ -12745,6 +17693,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -12754,10 +17705,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -12766,10 +17723,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -12903,6 +17909,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -12912,22 +17921,77 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: type: boolean images: items: - type: string + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object type: array - namePrefix: - type: string - nameSuffix: - type: string version: type: string type: object @@ -13028,12 +18092,89 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object required: - - generators - - mergeKeys + - configMapRef type: object pullRequest: properties: + azuredevops: + properties: + api: + type: string + labels: + items: + type: string + type: array + organization: + type: string + project: + type: string + repo: + type: string + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - organization + - project + - repo + type: object + bitbucket: + properties: + api: + type: string + basicAuth: + properties: + passwordRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + username: + type: string + required: + - passwordRef + - username + type: object + bearerToken: + properties: + tokenRef: + properties: + key: + type: string + secretName: + type: string + required: + - key + - secretName + type: object + required: + - tokenRef + type: object + owner: + type: string + repo: + type: string + required: + - owner + - repo + type: object bitbucketServer: properties: api: @@ -13070,6 +18211,8 @@ spec: properties: branchMatch: type: string + targetBranchMatch: + type: string type: object type: array gitea: @@ -13129,6 +18272,8 @@ spec: properties: api: type: string + insecure: + type: boolean labels: items: type: string @@ -13313,6 +18458,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -13322,10 +18470,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -13334,10 +18488,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -13471,6 +18674,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -13480,10 +18686,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -13492,10 +18704,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -13599,6 +18860,26 @@ spec: type: object scmProvider: properties: + awsCodeCommit: + properties: + allBranches: + type: boolean + region: + type: string + role: + type: string + tagFilters: + items: + properties: + key: + type: string + value: + type: string + required: + - key + type: object + type: array + type: object azureDevOps: properties: accessTokenRef: @@ -13753,8 +19034,12 @@ spec: type: string group: type: string + includeSharedProjects: + type: boolean includeSubgroups: type: boolean + insecure: + type: boolean tokenRef: properties: key: @@ -13765,6 +19050,8 @@ spec: - key - secretName type: object + topic: + type: string required: - group type: object @@ -13931,6 +19218,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -13940,10 +19230,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -13952,10 +19248,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -14089,6 +19434,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -14098,22 +19446,77 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: type: boolean images: items: - type: string + type: string + type: array + labelWithoutSelector: + type: boolean + namePrefix: + type: string + nameSuffix: + type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object type: array - namePrefix: - type: string - nameSuffix: - type: string version: type: string type: object @@ -14214,6 +19617,10 @@ spec: - metadata - spec type: object + values: + additionalProperties: + type: string + type: object type: object selector: properties: @@ -14242,6 +19649,36 @@ spec: type: array goTemplate: type: boolean + goTemplateOptions: + items: + type: string + type: array + ignoreApplicationDifferences: + items: + properties: + jqPathExpressions: + items: + type: string + type: array + jsonPointers: + items: + type: string + type: array + name: + type: string + type: object + type: array + preservedFields: + properties: + annotations: + items: + type: string + type: array + labels: + items: + type: string + type: array + type: object strategy: properties: rollingSync: @@ -14275,6 +19712,13 @@ spec: type: object syncPolicy: properties: + applicationsSync: + enum: + - create-only + - create-update + - create-delete + - sync + type: string preserveResourcesOnDeletion: type: boolean type: object @@ -14438,6 +19882,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -14447,10 +19894,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -14459,10 +19912,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -14596,6 +20098,9 @@ spec: type: array values: type: string + valuesObject: + type: object + x-kubernetes-preserve-unknown-fields: true version: type: string type: object @@ -14605,10 +20110,16 @@ spec: additionalProperties: type: string type: object + commonAnnotationsEnvsubst: + type: boolean commonLabels: additionalProperties: type: string type: object + components: + items: + type: string + type: array forceCommonAnnotations: type: boolean forceCommonLabels: @@ -14617,10 +20128,59 @@ spec: items: type: string type: array + labelWithoutSelector: + type: boolean namePrefix: type: string nameSuffix: type: string + namespace: + type: string + patches: + items: + properties: + options: + additionalProperties: + type: boolean + type: object + patch: + type: string + path: + type: string + target: + properties: + annotationSelector: + type: string + group: + type: string + kind: + type: string + labelSelector: + type: string + name: + type: string + namespace: + type: string + version: + type: string + type: object + type: object + type: array + replicas: + items: + properties: + count: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + name: + type: string + required: + - count + - name + type: object + type: array version: type: string type: object @@ -14721,6 +20281,8 @@ spec: - metadata - spec type: object + templatePatch: + type: string required: - generators - template @@ -14739,10 +20301,13 @@ spec: type: string status: type: string + step: + type: string required: - application - message - status + - step type: object type: array conditions: @@ -14866,7 +20431,8 @@ spec: properties: name: description: Name is an alternate way of specifying the target - cluster by its symbolic name + cluster by its symbolic name. This must be set if Server is + not set. type: string namespace: description: Namespace specifies the target namespace for the @@ -14874,8 +20440,9 @@ spec: namespace-scoped resources that have not set a value for .metadata.namespace type: string server: - description: Server specifies the URL of the target cluster - and must be set to the Kubernetes control plane API + description: Server specifies the URL of the target cluster's + Kubernetes control plane API. This must be set if Name is + not set. type: string type: object type: array @@ -15112,9 +20679,9 @@ apiVersion: v1 kind: ServiceAccount metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller --- apiVersion: v1 @@ -15200,14 +20767,22 @@ rules: verbs: - create - list +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller rules: - apiGroups: @@ -15289,6 +20864,10 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller rules: - apiGroups: @@ -15393,6 +20972,95 @@ rules: --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: applicationset-controller + app.kubernetes.io/name: argocd-applicationset-controller + app.kubernetes.io/part-of: argocd + name: argocd-applicationset-controller +rules: +- apiGroups: + - argoproj.io + resources: + - applications + - applicationsets + - applicationsets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - argoproj.io + resources: + - applicationsets/status + verbs: + - get + - patch + - update +- apiGroups: + - argoproj.io + resources: + - appprojects + verbs: + - get +- apiGroups: + - "" + resources: + - events + verbs: + - create + - get + - list + - patch + - watch +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - update + - delete + - get + - list + - patch + - watch +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch +- apiGroups: + - apps + - extensions + resources: + - deployments + verbs: + - get + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole metadata: labels: app.kubernetes.io/component: server @@ -15425,10 +21093,23 @@ rules: - argoproj.io resources: - applications + - applicationsets verbs: - get - list - watch +- apiGroups: + - batch + resources: + - jobs + verbs: + - create +- apiGroups: + - argoproj.io + resources: + - workflows + verbs: + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -15450,9 +21131,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller roleRef: apiGroup: rbac.authorization.k8s.io @@ -15466,45 +21147,33 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: - app.kubernetes.io/component: dex-server - app.kubernetes.io/name: argocd-dex-server - app.kubernetes.io/part-of: argocd - name: argocd-dex-server -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: argocd-dex-server -subjects: -- kind: ServiceAccount - name: argocd-dex-server ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: argocd-notifications-controller + app.kubernetes.io/component: dex-server + app.kubernetes.io/name: argocd-dex-server + app.kubernetes.io/part-of: argocd + name: argocd-dex-server roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: argocd-notifications-controller + name: argocd-dex-server subjects: - kind: ServiceAccount - name: argocd-notifications-controller + name: argocd-dex-server --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: - app.kubernetes.io/component: redis - app.kubernetes.io/name: argocd-redis + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller app.kubernetes.io/part-of: argocd - name: argocd-redis + name: argocd-notifications-controller roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: argocd-redis + name: argocd-notifications-controller subjects: - kind: ServiceAccount - name: argocd-redis + name: argocd-notifications-controller --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -15541,6 +21210,23 @@ subjects: --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: applicationset-controller + app.kubernetes.io/name: argocd-applicationset-controller + app.kubernetes.io/part-of: argocd + name: argocd-applicationset-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: argocd-applicationset-controller +subjects: +- kind: ServiceAccount + name: argocd-applicationset-controller + namespace: argocd +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/component: server @@ -15583,6 +21269,10 @@ metadata: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-cm --- apiVersion: v1 @@ -15595,16 +21285,22 @@ metadata: --- apiVersion: v1 data: - ssh_known_hosts: |- - bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw== - github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + ssh_known_hosts: | + # This file was automatically generated by hack/update-ssh-known-hosts.sh. DO NOT EDIT + [ssh.github.com]:443 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + [ssh.github.com]:443 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + [ssh.github.com]:443 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + bitbucket.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIQmuzMBuKdWeF4+a2sjSSpBK0iqitSQ+5BM9KhpexuGt20JpTVM7u5BDZngncgrqDMbWdxMWWOGtZ9UgbqgZE= + bitbucket.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIazEu89wgQZ4bqs3d63QSMzYVa0MuJ2e2gKTKqu+UUO + bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQeJzhupRu0u0cdegZIa8e86EG2qOCsIsD1Xw0xSeiPDlCr7kq97NLmMbpKTX6Esc30NuoqEEHCuc7yWtwp8dI76EEEB1VqY9QJq6vk+aySyboD5QF61I/1WeTwu+deCbgKMGbUijeXhtfbxSxm6JwGrXrhBdofTsbKRUsrN1WoNgUa8uqN1Vx6WAJw1JHPhglEGGHea6QICwJOAr/6mrui/oB7pkaWKHj3z7d1IC4KWLtY47elvjbaTlkN04Kc/5LFEirorGYVbt15kAUlqGM65pk6ZBxtaO3+30LVlORZkxOh+LKL/BvbZ/iRNhItLqNyieoQj/uh/7Iv4uyH/cV/0b4WDSd3DptigWq84lJubb9t/DnZlrJazxyDCulTmKdOR7vs9gMTo+uoIrPSb8ScTtvw65+odKAlBj59dhnVp9zd7QUojOpXlL62Aw56U4oO+FALuevvMjiWeavKhJqlR7i5n9srYcrNV7ttmDw7kf/97P5zauIhxcjX+xHv4M= + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H - github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= - github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl kind: ConfigMap metadata: labels: @@ -15623,6 +21319,10 @@ metadata: apiVersion: v1 kind: Secret metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-secret type: Opaque --- @@ -15639,9 +21339,9 @@ apiVersion: v1 kind: Service metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller spec: ports: @@ -15666,7 +21366,8 @@ metadata: name: argocd-dex-server spec: ports: - - name: http + - appProtocol: TCP + name: http port: 5556 protocol: TCP targetPort: 5556 @@ -15702,7 +21403,9 @@ apiVersion: v1 kind: Service metadata: labels: + app.kubernetes.io/component: notifications-controller app.kubernetes.io/name: argocd-notifications-controller-metrics + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller-metrics spec: ports: @@ -15792,9 +21495,9 @@ apiVersion: apps/v1 kind: Deployment metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller spec: selector: @@ -15806,10 +21509,21 @@ spec: app.kubernetes.io/name: argocd-applicationset-controller spec: containers: - - command: - - entrypoint.sh - - argocd-applicationset-controller + - args: + - /usr/local/bin/argocd-applicationset-controller env: + - name: ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_ANNOTATIONS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.global.preserved.annotations + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_LABELS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.global.preserved.labels + name: argocd-cmd-params-cm + optional: true - name: NAMESPACE valueFrom: fieldRef: @@ -15820,12 +21534,6 @@ spec: key: applicationsetcontroller.enable.leader.election name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACE - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.namespace - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER valueFrom: configMapKeyRef: @@ -15838,6 +21546,12 @@ spec: key: applicationsetcontroller.policy name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_POLICY_OVERRIDE + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.policy.override + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG valueFrom: configMapKeyRef: @@ -15868,10 +21582,64 @@ spec: key: applicationsetcontroller.enable.git.submodule name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_ROLLOUTS + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.progressive.syncs + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.new.git.file.globbing + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_PLAINTEXT + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.plaintext + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_STRICT_TLS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.strict.tls + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.timeout.seconds + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_CONCURRENT_RECONCILIATIONS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.concurrent.reconciliations.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.namespaces + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.scm.root.ca.path + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.allowed.scm.providers + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS valueFrom: configMapKeyRef: - key: applicationsetcontroller.enable.progressive.rollouts + key: applicationsetcontroller.enable.scm.providers name: argocd-cmd-params-cm optional: true image: quay.io/argoproj/argocd:latest @@ -15902,6 +21670,8 @@ spec: name: gpg-keyring - mountPath: /tmp name: tmp + - mountPath: /app/config/reposerver/tls + name: argocd-repo-server-tls serviceAccountName: argocd-applicationset-controller volumes: - configMap: @@ -15917,6 +21687,17 @@ spec: name: gpg-keyring - emptyDir: {} name: tmp + - name: argocd-repo-server-tls + secret: + items: + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key + - key: ca.crt + path: ca.crt + optional: true + secretName: argocd-repo-server-tls --- apiVersion: apps/v1 kind: Deployment @@ -15955,7 +21736,7 @@ spec: key: dexserver.disable.tls name: argocd-cmd-params-cm optional: true - image: ghcr.io/dexidp/dex:v2.35.3 + image: ghcr.io/dexidp/dex:v2.38.0 imagePullPolicy: Always name: dex ports: @@ -15980,7 +21761,7 @@ spec: name: argocd-dex-server-tls initContainers: - command: - - cp + - /bin/cp - -n - /usr/local/bin/argocd - /shared/argocd-dex @@ -16022,6 +21803,10 @@ spec: apiVersion: apps/v1 kind: Deployment metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller spec: selector: @@ -16035,8 +21820,33 @@ spec: app.kubernetes.io/name: argocd-notifications-controller spec: containers: - - command: - - argocd-notifications + - args: + - /usr/local/bin/argocd-notifications + env: + - name: ARGOCD_NOTIFICATIONS_CONTROLLER_LOGFORMAT + valueFrom: + configMapKeyRef: + key: notificationscontroller.log.format + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_NOTIFICATIONS_CONTROLLER_LOGLEVEL + valueFrom: + configMapKeyRef: + key: notificationscontroller.log.level + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_NAMESPACES + valueFrom: + configMapKeyRef: + key: application.namespaces + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED + valueFrom: + configMapKeyRef: + key: notificationscontroller.selfservice.enabled + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always livenessProbe: @@ -16114,7 +21924,7 @@ spec: - "" - --appendonly - "no" - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: Always name: redis ports: @@ -16124,6 +21934,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true securityContext: runAsNonRoot: true runAsUser: 999 @@ -16165,10 +21976,8 @@ spec: weight: 5 automountServiceAccountToken: false containers: - - command: - - sh - - -c - - entrypoint.sh argocd-repo-server --redis argocd-redis:6379 + - args: + - /usr/local/bin/argocd-repo-server env: - name: ARGOCD_RECONCILIATION_TIMEOUT valueFrom: @@ -16194,6 +22003,18 @@ spec: key: reposerver.parallelism.limit name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: reposerver.listen.address + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_LISTEN_METRICS_ADDRESS + valueFrom: + configMapKeyRef: + key: reposerver.metrics.listen.address + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_DISABLE_TLS valueFrom: configMapKeyRef: @@ -16254,6 +22075,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE valueFrom: configMapKeyRef: @@ -16284,12 +22117,42 @@ spec: key: reposerver.streamed.manifest.max.extracted.size name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE + valueFrom: + configMapKeyRef: + key: reposerver.helm.manifest.max.extracted.size + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE + valueFrom: + configMapKeyRef: + key: reposerver.disable.helm.manifest.max.extracted.size + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REVISION_CACHE_LOCK_TIMEOUT + valueFrom: + configMapKeyRef: + key: reposerver.revision.cache.lock.timeout + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_GIT_MODULES_ENABLED valueFrom: configMapKeyRef: key: reposerver.enable.git.submodule name: argocd-cmd-params-cm optional: true + - name: ARGOCD_GIT_LS_REMOTE_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + key: reposerver.git.lsremote.parallelism.limit + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_GIT_REQUEST_TIMEOUT + valueFrom: + configMapKeyRef: + key: reposerver.git.request.timeout + name: argocd-cmd-params-cm + optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME @@ -16344,7 +22207,7 @@ spec: name: plugins initContainers: - command: - - cp + - /bin/cp - -n - /usr/local/bin/argocd - /var/run/argocd/argocd-cmp-server @@ -16428,8 +22291,8 @@ spec: topologyKey: kubernetes.io/hostname weight: 5 containers: - - command: - - argocd-server + - args: + - /usr/local/bin/argocd-server env: - name: ARGOCD_SERVER_INSECURE valueFrom: @@ -16605,12 +22468,36 @@ spec: key: server.http.cookie.maxnumber name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: server.listen.address + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_SERVER_METRICS_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: server.metrics.listen.address + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_SERVER_OTLP_ADDRESS valueFrom: configMapKeyRef: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: @@ -16623,6 +22510,24 @@ spec: key: server.enable.proxy.extension name: argocd-cmd-params-cm optional: true + - name: ARGOCD_K8SCLIENT_RETRY_MAX + valueFrom: + configMapKeyRef: + key: server.k8sclient.retry.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF + valueFrom: + configMapKeyRef: + key: server.k8sclient.retry.base.backoff + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_API_CONTENT_TYPES + valueFrom: + configMapKeyRef: + key: server.api.content.types + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always livenessProbe: @@ -16732,8 +22637,8 @@ spec: topologyKey: kubernetes.io/hostname weight: 5 containers: - - command: - - argocd-application-controller + - args: + - /usr/local/bin/argocd-application-controller env: - name: ARGOCD_CONTROLLER_REPLICAS value: "1" @@ -16749,6 +22654,18 @@ spec: key: timeout.hard.reconciliation name: argocd-cm optional: true + - name: ARGOCD_RECONCILIATION_JITTER + valueFrom: + configMapKeyRef: + key: timeout.reconciliation.jitter + name: argocd-cm + optional: true + - name: ARGOCD_REPO_ERROR_GRACE_PERIOD_SECONDS + valueFrom: + configMapKeyRef: + key: controller.repo.error.grace.period.seconds + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER valueFrom: configMapKeyRef: @@ -16851,12 +22768,54 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: key: application.namespaces name: argocd-cmd-params-cm optional: true + - name: ARGOCD_CONTROLLER_SHARDING_ALGORITHM + valueFrom: + configMapKeyRef: + key: controller.sharding.algorithm + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + key: controller.kubectl.parallelism.limit + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_MAX + valueFrom: + configMapKeyRef: + key: controller.k8sclient.retry.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF + valueFrom: + configMapKeyRef: + key: controller.k8sclient.retry.base.backoff + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + key: controller.diff.server.side + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller @@ -16963,6 +22922,10 @@ spec: apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller-network-policy spec: ingress: @@ -17025,6 +22988,9 @@ spec: - podSelector: matchLabels: app.kubernetes.io/name: argocd-notifications-controller + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-applicationset-controller ports: - port: 8081 protocol: TCP diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 606045c259df2..91826ef8d5620 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -12,9 +12,9 @@ apiVersion: v1 kind: ServiceAccount metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller --- apiVersion: v1 @@ -100,14 +100,22 @@ rules: verbs: - create - list +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller rules: - apiGroups: @@ -189,6 +197,10 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller rules: - apiGroups: @@ -291,9 +303,9 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller roleRef: apiGroup: rbac.authorization.k8s.io @@ -322,6 +334,10 @@ subjects: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller roleRef: apiGroup: rbac.authorization.k8s.io @@ -333,22 +349,6 @@ subjects: --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding -metadata: - labels: - app.kubernetes.io/component: redis - app.kubernetes.io/name: argocd-redis - app.kubernetes.io/part-of: argocd - name: argocd-redis -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: argocd-redis -subjects: -- kind: ServiceAccount - name: argocd-redis ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding metadata: labels: app.kubernetes.io/component: server @@ -390,6 +390,10 @@ metadata: apiVersion: v1 kind: ConfigMap metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-cm --- apiVersion: v1 @@ -402,16 +406,22 @@ metadata: --- apiVersion: v1 data: - ssh_known_hosts: |- - bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw== - github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + ssh_known_hosts: | + # This file was automatically generated by hack/update-ssh-known-hosts.sh. DO NOT EDIT + [ssh.github.com]:443 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + [ssh.github.com]:443 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + [ssh.github.com]:443 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + bitbucket.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIQmuzMBuKdWeF4+a2sjSSpBK0iqitSQ+5BM9KhpexuGt20JpTVM7u5BDZngncgrqDMbWdxMWWOGtZ9UgbqgZE= + bitbucket.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIazEu89wgQZ4bqs3d63QSMzYVa0MuJ2e2gKTKqu+UUO + bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQeJzhupRu0u0cdegZIa8e86EG2qOCsIsD1Xw0xSeiPDlCr7kq97NLmMbpKTX6Esc30NuoqEEHCuc7yWtwp8dI76EEEB1VqY9QJq6vk+aySyboD5QF61I/1WeTwu+deCbgKMGbUijeXhtfbxSxm6JwGrXrhBdofTsbKRUsrN1WoNgUa8uqN1Vx6WAJw1JHPhglEGGHea6QICwJOAr/6mrui/oB7pkaWKHj3z7d1IC4KWLtY47elvjbaTlkN04Kc/5LFEirorGYVbt15kAUlqGM65pk6ZBxtaO3+30LVlORZkxOh+LKL/BvbZ/iRNhItLqNyieoQj/uh/7Iv4uyH/cV/0b4WDSd3DptigWq84lJubb9t/DnZlrJazxyDCulTmKdOR7vs9gMTo+uoIrPSb8ScTtvw65+odKAlBj59dhnVp9zd7QUojOpXlL62Aw56U4oO+FALuevvMjiWeavKhJqlR7i5n9srYcrNV7ttmDw7kf/97P5zauIhxcjX+xHv4M= + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H - github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= - github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl kind: ConfigMap metadata: labels: @@ -430,6 +440,10 @@ metadata: apiVersion: v1 kind: Secret metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-secret type: Opaque --- @@ -446,9 +460,9 @@ apiVersion: v1 kind: Service metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller spec: ports: @@ -473,7 +487,8 @@ metadata: name: argocd-dex-server spec: ports: - - name: http + - appProtocol: TCP + name: http port: 5556 protocol: TCP targetPort: 5556 @@ -509,7 +524,9 @@ apiVersion: v1 kind: Service metadata: labels: + app.kubernetes.io/component: notifications-controller app.kubernetes.io/name: argocd-notifications-controller-metrics + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller-metrics spec: ports: @@ -599,9 +616,9 @@ apiVersion: apps/v1 kind: Deployment metadata: labels: - app.kubernetes.io/component: controller + app.kubernetes.io/component: applicationset-controller app.kubernetes.io/name: argocd-applicationset-controller - app.kubernetes.io/part-of: argocd-applicationset + app.kubernetes.io/part-of: argocd name: argocd-applicationset-controller spec: selector: @@ -613,10 +630,21 @@ spec: app.kubernetes.io/name: argocd-applicationset-controller spec: containers: - - command: - - entrypoint.sh - - argocd-applicationset-controller + - args: + - /usr/local/bin/argocd-applicationset-controller env: + - name: ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_ANNOTATIONS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.global.preserved.annotations + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_LABELS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.global.preserved.labels + name: argocd-cmd-params-cm + optional: true - name: NAMESPACE valueFrom: fieldRef: @@ -627,12 +655,6 @@ spec: key: applicationsetcontroller.enable.leader.election name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACE - valueFrom: - configMapKeyRef: - key: applicationsetcontroller.namespace - name: argocd-cmd-params-cm - optional: true - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER valueFrom: configMapKeyRef: @@ -645,6 +667,12 @@ spec: key: applicationsetcontroller.policy name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_POLICY_OVERRIDE + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.policy.override + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG valueFrom: configMapKeyRef: @@ -675,10 +703,64 @@ spec: key: applicationsetcontroller.enable.git.submodule name: argocd-cmd-params-cm optional: true - - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_ROLLOUTS + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.progressive.syncs + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.new.git.file.globbing + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_PLAINTEXT + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.plaintext + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_STRICT_TLS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.strict.tls + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.repo.server.timeout.seconds + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_CONCURRENT_RECONCILIATIONS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.concurrent.reconciliations.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.namespaces + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.scm.root.ca.path + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.allowed.scm.providers + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS valueFrom: configMapKeyRef: - key: applicationsetcontroller.enable.progressive.rollouts + key: applicationsetcontroller.enable.scm.providers name: argocd-cmd-params-cm optional: true image: quay.io/argoproj/argocd:latest @@ -709,6 +791,8 @@ spec: name: gpg-keyring - mountPath: /tmp name: tmp + - mountPath: /app/config/reposerver/tls + name: argocd-repo-server-tls serviceAccountName: argocd-applicationset-controller volumes: - configMap: @@ -724,6 +808,17 @@ spec: name: gpg-keyring - emptyDir: {} name: tmp + - name: argocd-repo-server-tls + secret: + items: + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key + - key: ca.crt + path: ca.crt + optional: true + secretName: argocd-repo-server-tls --- apiVersion: apps/v1 kind: Deployment @@ -762,7 +857,7 @@ spec: key: dexserver.disable.tls name: argocd-cmd-params-cm optional: true - image: ghcr.io/dexidp/dex:v2.35.3 + image: ghcr.io/dexidp/dex:v2.38.0 imagePullPolicy: Always name: dex ports: @@ -787,7 +882,7 @@ spec: name: argocd-dex-server-tls initContainers: - command: - - cp + - /bin/cp - -n - /usr/local/bin/argocd - /shared/argocd-dex @@ -829,6 +924,10 @@ spec: apiVersion: apps/v1 kind: Deployment metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller spec: selector: @@ -842,8 +941,33 @@ spec: app.kubernetes.io/name: argocd-notifications-controller spec: containers: - - command: - - argocd-notifications + - args: + - /usr/local/bin/argocd-notifications + env: + - name: ARGOCD_NOTIFICATIONS_CONTROLLER_LOGFORMAT + valueFrom: + configMapKeyRef: + key: notificationscontroller.log.format + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_NOTIFICATIONS_CONTROLLER_LOGLEVEL + valueFrom: + configMapKeyRef: + key: notificationscontroller.log.level + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_NAMESPACES + valueFrom: + configMapKeyRef: + key: application.namespaces + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED + valueFrom: + configMapKeyRef: + key: notificationscontroller.selfservice.enabled + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always livenessProbe: @@ -921,7 +1045,7 @@ spec: - "" - --appendonly - "no" - image: redis:7.0.7-alpine + image: redis:7.0.14-alpine imagePullPolicy: Always name: redis ports: @@ -931,6 +1055,7 @@ spec: capabilities: drop: - ALL + readOnlyRootFilesystem: true securityContext: runAsNonRoot: true runAsUser: 999 @@ -972,10 +1097,8 @@ spec: weight: 5 automountServiceAccountToken: false containers: - - command: - - sh - - -c - - entrypoint.sh argocd-repo-server --redis argocd-redis:6379 + - args: + - /usr/local/bin/argocd-repo-server env: - name: ARGOCD_RECONCILIATION_TIMEOUT valueFrom: @@ -1001,6 +1124,18 @@ spec: key: reposerver.parallelism.limit name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: reposerver.listen.address + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_LISTEN_METRICS_ADDRESS + valueFrom: + configMapKeyRef: + key: reposerver.metrics.listen.address + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_DISABLE_TLS valueFrom: configMapKeyRef: @@ -1061,6 +1196,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE valueFrom: configMapKeyRef: @@ -1091,12 +1238,42 @@ spec: key: reposerver.streamed.manifest.max.extracted.size name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE + valueFrom: + configMapKeyRef: + key: reposerver.helm.manifest.max.extracted.size + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE + valueFrom: + configMapKeyRef: + key: reposerver.disable.helm.manifest.max.extracted.size + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REVISION_CACHE_LOCK_TIMEOUT + valueFrom: + configMapKeyRef: + key: reposerver.revision.cache.lock.timeout + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_GIT_MODULES_ENABLED valueFrom: configMapKeyRef: key: reposerver.enable.git.submodule name: argocd-cmd-params-cm optional: true + - name: ARGOCD_GIT_LS_REMOTE_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + key: reposerver.git.lsremote.parallelism.limit + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_GIT_REQUEST_TIMEOUT + valueFrom: + configMapKeyRef: + key: reposerver.git.request.timeout + name: argocd-cmd-params-cm + optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME @@ -1151,7 +1328,7 @@ spec: name: plugins initContainers: - command: - - cp + - /bin/cp - -n - /usr/local/bin/argocd - /var/run/argocd/argocd-cmp-server @@ -1235,8 +1412,8 @@ spec: topologyKey: kubernetes.io/hostname weight: 5 containers: - - command: - - argocd-server + - args: + - /usr/local/bin/argocd-server env: - name: ARGOCD_SERVER_INSECURE valueFrom: @@ -1412,12 +1589,36 @@ spec: key: server.http.cookie.maxnumber name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: server.listen.address + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_SERVER_METRICS_LISTEN_ADDRESS + valueFrom: + configMapKeyRef: + key: server.metrics.listen.address + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_SERVER_OTLP_ADDRESS valueFrom: configMapKeyRef: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: @@ -1430,6 +1631,24 @@ spec: key: server.enable.proxy.extension name: argocd-cmd-params-cm optional: true + - name: ARGOCD_K8SCLIENT_RETRY_MAX + valueFrom: + configMapKeyRef: + key: server.k8sclient.retry.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF + valueFrom: + configMapKeyRef: + key: server.k8sclient.retry.base.backoff + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_API_CONTENT_TYPES + valueFrom: + configMapKeyRef: + key: server.api.content.types + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always livenessProbe: @@ -1539,8 +1758,8 @@ spec: topologyKey: kubernetes.io/hostname weight: 5 containers: - - command: - - argocd-application-controller + - args: + - /usr/local/bin/argocd-application-controller env: - name: ARGOCD_CONTROLLER_REPLICAS value: "1" @@ -1556,6 +1775,18 @@ spec: key: timeout.hard.reconciliation name: argocd-cm optional: true + - name: ARGOCD_RECONCILIATION_JITTER + valueFrom: + configMapKeyRef: + key: timeout.reconciliation.jitter + name: argocd-cm + optional: true + - name: ARGOCD_REPO_ERROR_GRACE_PERIOD_SECONDS + valueFrom: + configMapKeyRef: + key: controller.repo.error.grace.period.seconds + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER valueFrom: configMapKeyRef: @@ -1658,12 +1889,54 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: key: application.namespaces name: argocd-cmd-params-cm optional: true + - name: ARGOCD_CONTROLLER_SHARDING_ALGORITHM + valueFrom: + configMapKeyRef: + key: controller.sharding.algorithm + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT + valueFrom: + configMapKeyRef: + key: controller.kubectl.parallelism.limit + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_MAX + valueFrom: + configMapKeyRef: + key: controller.k8sclient.retry.max + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF + valueFrom: + configMapKeyRef: + key: controller.k8sclient.retry.base.backoff + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + key: controller.diff.server.side + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller @@ -1770,6 +2043,10 @@ spec: apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: + labels: + app.kubernetes.io/component: notifications-controller + app.kubernetes.io/name: argocd-notifications-controller + app.kubernetes.io/part-of: argocd name: argocd-notifications-controller-network-policy spec: ingress: @@ -1832,6 +2109,9 @@ spec: - podSelector: matchLabels: app.kubernetes.io/name: argocd-notifications-controller + - podSelector: + matchLabels: + app.kubernetes.io/name: argocd-applicationset-controller ports: - port: 8081 protocol: TCP diff --git a/mkdocs.yml b/mkdocs.yml index ec626be23bb1c..a7e8f86e216cc 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,10 +8,12 @@ extra_javascript: - assets/versions.js markdown_extensions: - markdown_include.include -- codehilite +- codehilite: + css_class: highlight - admonition - toc: permalink: true +- pymdownx.superfences nav: - Overview: index.md - understand_the_basics.md @@ -21,9 +23,13 @@ nav: - operator-manual/index.md - operator-manual/architecture.md - operator-manual/installation.md + - operator-manual/core.md - operator-manual/declarative-setup.md - operator-manual/app-any-namespace.md - operator-manual/ingress.md + - High Availability: + - Overview: operator-manual/high_availability.md + - Dynamic Cluster Distribution: operator-manual/dynamic-cluster-distribution.md - User Management: - operator-manual/user-management/index.md - operator-manual/user-management/auth0.md @@ -33,6 +39,8 @@ nav: - operator-manual/user-management/keycloak.md - operator-manual/user-management/openunison.md - operator-manual/user-management/google.md + - operator-manual/user-management/zitadel.md + - operator-manual/user-management/identity-center.md - operator-manual/rbac.md - Security: - Overview: operator-manual/security.md @@ -41,18 +49,19 @@ nav: - operator-manual/tls.md - operator-manual/cluster-bootstrapping.md - operator-manual/secret-management.md - - operator-manual/high_availability.md - operator-manual/disaster_recovery.md + - operator-manual/reconcile.md - operator-manual/webhook.md - operator-manual/health.md - operator-manual/resource_actions.md - operator-manual/custom_tools.md - operator-manual/custom-styles.md + - operator-manual/ui-customization.md - operator-manual/metrics.md - operator-manual/web_based_terminal.md - operator-manual/config-management-plugins.md - operator-manual/deep_links.md - - Notification: + - Notifications: - Overview: operator-manual/notifications/index.md - operator-manual/notifications/triggers.md - operator-manual/notifications/templates.md @@ -65,6 +74,7 @@ nav: - operator-manual/notifications/troubleshooting-errors.md - Notification Services: - operator-manual/notifications/services/alertmanager.md + - operator-manual/notifications/services/awssqs.md - operator-manual/notifications/services/email.md - operator-manual/notifications/services/github.md - operator-manual/notifications/services/googlechat.md @@ -74,6 +84,7 @@ nav: - operator-manual/notifications/services/opsgenie.md - operator-manual/notifications/services/overview.md - operator-manual/notifications/services/pagerduty.md + - operator-manual/notifications/services/pagerduty_v2.md - operator-manual/notifications/services/pushover.md - operator-manual/notifications/services/rocketchat.md - operator-manual/notifications/services/slack.md @@ -98,12 +109,17 @@ nav: - operator-manual/applicationset/Generators-SCM-Provider.md - operator-manual/applicationset/Generators-Cluster-Decision-Resource.md - operator-manual/applicationset/Generators-Pull-Request.md + - operator-manual/applicationset/Generators-Post-Selector.md + - operator-manual/applicationset/Generators-Plugin.md - Template fields: - operator-manual/applicationset/Template.md - operator-manual/applicationset/GoTemplate.md - Controlling Resource Modification: operator-manual/applicationset/Controlling-Resource-Modification.md - Application Pruning & Resource Deletion: operator-manual/applicationset/Application-Deletion.md - - Progressive Rollouts: operator-manual/applicationset/Progressive-Rollouts.md + - Progressive Syncs: operator-manual/applicationset/Progressive-Syncs.md + - Git File Generator Globbing: operator-manual/applicationset/Generators-Git-File-Globbing.md + - ApplicationSet Specification Reference: operator-manual/applicationset/applicationset-specification.md + - ApplicationSet in any namespace: operator-manual/applicationset/Appset-Any-Namespace.md - Server Configuration Parameters: - operator-manual/server-commands/argocd-server.md - operator-manual/server-commands/argocd-application-controller.md @@ -112,6 +128,12 @@ nav: - operator-manual/server-commands/additional-configuration-method.md - Upgrading: - operator-manual/upgrading/overview.md + - operator-manual/upgrading/2.10-2.11.md + - operator-manual/upgrading/2.9-2.10.md + - operator-manual/upgrading/2.8-2.9.md + - operator-manual/upgrading/2.7-2.8.md + - operator-manual/upgrading/2.6-2.7.md + - operator-manual/upgrading/2.5-2.6.md - operator-manual/upgrading/2.4-2.5.md - operator-manual/upgrading/2.3-2.4.md - operator-manual/upgrading/2.2-2.3.md @@ -126,6 +148,7 @@ nav: - operator-manual/upgrading/1.2-1.3.md - operator-manual/upgrading/1.1-1.2.md - operator-manual/upgrading/1.0-1.1.md + - Project Specification Reference: operator-manual/project-specification.md - User Guide: - user-guide/index.md - user-guide/application_sources.md @@ -140,7 +163,9 @@ nav: - user-guide/multiple_sources.md - GnuPG verification: user-guide/gpg-verification.md - user-guide/auto_sync.md - - user-guide/diffing.md + - Diffing: + - Diff Strategies: user-guide/diff-strategies.md + - Diff Customization: user-guide/diffing.md - user-guide/orphaned-resources.md - user-guide/compare-options.md - user-guide/sync-options.md @@ -153,16 +178,24 @@ nav: - user-guide/selective_sync.md - user-guide/sync-waves.md - user-guide/sync_windows.md + - user-guide/sync-kubectl.md + - user-guide/skip_reconcile.md - Generating Applications with ApplicationSet: user-guide/application-set.md - user-guide/ci_automation.md - user-guide/app_deletion.md - user-guide/best_practices.md - user-guide/status-badge.md - user-guide/external-url.md + - user-guide/extra_info.md - Notification subscriptions: user-guide/subscriptions.md + - user-guide/annotations-and-labels.md - Command Reference: user-guide/commands/argocd.md + - Application Specification Reference: user-guide/application-specification.md - Developer Guide: - developer-guide/index.md + - Architecture: + - developer-guide/architecture/authz-authn.md + - developer-guide/architecture/components.md - Code Contribution Guide: developer-guide/code-contributions.md - Toolchain Guide: developer-guide/toolchain-guide.md - developer-guide/contributors-quickstart.md @@ -177,7 +210,9 @@ nav: - developer-guide/releasing.md - developer-guide/site.md - developer-guide/static-code-analysis.md - - developer-guide/ui-extensions.md + - Extensions: + - developer-guide/extensions/ui-extensions.md + - developer-guide/extensions/proxy-extensions.md - developer-guide/faq.md - faq.md - security_considerations.md @@ -196,4 +231,15 @@ theme: logo: assets/logo.png name: material palette: + - media: '(prefers-color-scheme: light)' primary: teal + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - media: '(prefers-color-scheme: dark)' + primary: teal + scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to light mode diff --git a/notification_controller/controller/controller.go b/notification_controller/controller/controller.go index e9f33bdc95b60..7d871af4c44a3 100644 --- a/notification_controller/controller/controller.go +++ b/notification_controller/controller/controller.go @@ -6,18 +6,24 @@ import ( "fmt" "time" + "github.com/argoproj/argo-cd/v2/util/glob" + "github.com/argoproj/argo-cd/v2/util/notification/k8s" service "github.com/argoproj/argo-cd/v2/util/notification/argocd" + argocert "github.com/argoproj/argo-cd/v2/util/cert" + "k8s.io/apimachinery/pkg/runtime/schema" "github.com/argoproj/argo-cd/v2/util/notification/settings" + "github.com/argoproj/argo-cd/v2/pkg/apis/application" "github.com/argoproj/notifications-engine/pkg/api" "github.com/argoproj/notifications-engine/pkg/controller" "github.com/argoproj/notifications-engine/pkg/services" "github.com/argoproj/notifications-engine/pkg/subscriptions" + httputil "github.com/argoproj/notifications-engine/pkg/util/http" log "github.com/sirupsen/logrus" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -33,8 +39,8 @@ const ( ) var ( - applications = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"} - appProjects = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "appprojects"} + applications = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.ApplicationPlural} + appProjects = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.AppProjectPlural} ) func newAppProjClient(client dynamic.Interface, namespace string) dynamic.ResourceInterface { @@ -52,17 +58,32 @@ func NewController( client dynamic.Interface, argocdService service.Service, namespace string, + applicationNamespaces []string, appLabelSelector string, registry *controller.MetricsRegistry, secretName string, configMapName string, + selfServiceNotificationEnabled bool, ) *notificationController { - appClient := client.Resource(applications) - appInformer := newInformer(appClient.Namespace(namespace), appLabelSelector) - appProjInformer := newInformer(newAppProjClient(client, namespace), "") - secretInformer := k8s.NewSecretInformer(k8sClient, namespace, secretName) - configMapInformer := k8s.NewConfigMapInformer(k8sClient, namespace, configMapName) - apiFactory := api.NewFactory(settings.GetFactorySettings(argocdService, secretName, configMapName), namespace, secretInformer, configMapInformer) + var appClient dynamic.ResourceInterface + + namespaceableAppClient := client.Resource(applications) + appClient = namespaceableAppClient + + if len(applicationNamespaces) == 0 { + appClient = namespaceableAppClient.Namespace(namespace) + } + appInformer := newInformer(appClient, namespace, applicationNamespaces, appLabelSelector) + appProjInformer := newInformer(newAppProjClient(client, namespace), namespace, []string{namespace}, "") + var notificationConfigNamespace string + if selfServiceNotificationEnabled { + notificationConfigNamespace = v1.NamespaceAll + } else { + notificationConfigNamespace = namespace + } + secretInformer := k8s.NewSecretInformer(k8sClient, notificationConfigNamespace, secretName) + configMapInformer := k8s.NewConfigMapInformer(k8sClient, notificationConfigNamespace, configMapName) + apiFactory := api.NewFactory(settings.GetFactorySettings(argocdService, secretName, configMapName, selfServiceNotificationEnabled), namespace, secretInformer, configMapInformer) res := ¬ificationController{ secretInformer: secretInformer, @@ -70,19 +91,38 @@ func NewController( appInformer: appInformer, appProjInformer: appProjInformer, apiFactory: apiFactory} - res.ctrl = controller.NewController(appClient, appInformer, apiFactory, - controller.WithSkipProcessing(func(obj v1.Object) (bool, string) { - app, ok := (obj).(*unstructured.Unstructured) - if !ok { - return false, "" - } - return !isAppSyncStatusRefreshed(app, log.WithField("app", obj.GetName())), "sync status out of date" - }), - controller.WithMetricsRegistry(registry), - controller.WithAlterDestinations(res.alterDestinations)) + skipProcessingOpt := controller.WithSkipProcessing(func(obj v1.Object) (bool, string) { + app, ok := (obj).(*unstructured.Unstructured) + if !ok { + return false, "" + } + if checkAppNotInAdditionalNamespaces(app, namespace, applicationNamespaces) { + return true, "app is not in one of the application-namespaces, nor the notification controller namespace" + } + return !isAppSyncStatusRefreshed(app, log.WithField("app", obj.GetName())), "sync status out of date" + }) + metricsRegistryOpt := controller.WithMetricsRegistry(registry) + alterDestinationsOpt := controller.WithAlterDestinations(res.alterDestinations) + + if !selfServiceNotificationEnabled { + res.ctrl = controller.NewController(namespaceableAppClient, appInformer, apiFactory, + skipProcessingOpt, + metricsRegistryOpt, + alterDestinationsOpt) + } else { + res.ctrl = controller.NewControllerWithNamespaceSupport(namespaceableAppClient, appInformer, apiFactory, + skipProcessingOpt, + metricsRegistryOpt, + alterDestinationsOpt) + } return res } +// Check if app is not in the namespace where the controller is in, and also app is not in one of the applicationNamespaces +func checkAppNotInAdditionalNamespaces(app *unstructured.Unstructured, namespace string, applicationNamespaces []string) bool { + return namespace != app.GetNamespace() && !glob.MatchStringInList(applicationNamespaces, app.GetNamespace(), false) +} + func (c *notificationController) alterDestinations(obj v1.Object, destinations services.Destinations, cfg api.Config) services.Destinations { app, ok := (obj).(*unstructured.Unstructured) if !ok { @@ -96,21 +136,39 @@ func (c *notificationController) alterDestinations(obj v1.Object, destinations s return destinations } -func newInformer(resClient dynamic.ResourceInterface, selector string) cache.SharedIndexInformer { +func newInformer(resClient dynamic.ResourceInterface, controllerNamespace string, applicationNamespaces []string, selector string) cache.SharedIndexInformer { + informer := cache.NewSharedIndexInformer( &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (object runtime.Object, err error) { + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + // We are only interested in apps that exist in namespaces the + // user wants to be enabled. options.LabelSelector = selector - return resClient.List(context.Background(), options) + appList, err := resClient.List(context.TODO(), options) + if err != nil { + return nil, fmt.Errorf("failed to list applications: %w", err) + } + newItems := []unstructured.Unstructured{} + for _, res := range appList.Items { + if controllerNamespace == res.GetNamespace() || glob.MatchStringInList(applicationNamespaces, res.GetNamespace(), false) { + newItems = append(newItems, res) + } + } + appList.Items = newItems + return appList, nil }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { options.LabelSelector = selector - return resClient.Watch(context.Background(), options) + return resClient.Watch(context.TODO(), options) }, }, &unstructured.Unstructured{}, resyncPeriod, - cache.Indexers{}, + cache.Indexers{ + cache.NamespaceIndex: func(obj interface{}) ([]string, error) { + return cache.MetaNamespaceIndexFunc(obj) + }, + }, ) return informer } @@ -125,6 +183,9 @@ type notificationController struct { } func (c *notificationController) Init(ctx context.Context) error { + // resolve certificates using injected "argocd-tls-certs-cm" ConfigMap + httputil.SetCertResolver(argocert.GetCertificateForConnect) + go c.appInformer.Run(ctx.Done()) go c.appProjInformer.Run(ctx.Done()) go c.secretInformer.Run(ctx.Done()) diff --git a/notification_controller/controller/controller_test.go b/notification_controller/controller/controller_test.go new file mode 100644 index 0000000000000..4eedb28f5e001 --- /dev/null +++ b/notification_controller/controller/controller_test.go @@ -0,0 +1,197 @@ +package controller + +import ( + "context" + "testing" + "time" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/dynamic/fake" + k8sfake "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/tools/cache" +) + +func TestIsAppSyncStatusRefreshed(t *testing.T) { + logger, _ := test.NewNullLogger() + logEntry := logger.WithField("", "") + + tests := []struct { + name string + app *unstructured.Unstructured + expectedValue bool + }{ + { + name: "No OperationState", + app: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "status": map[string]interface{}{}, + }, + }, + expectedValue: true, + }, + { + name: "No FinishedAt, Completed Phase", + app: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "status": map[string]interface{}{ + "operationState": map[string]interface{}{ + "phase": "Succeeded", + }, + }, + }, + }, + expectedValue: false, + }, + { + name: "FinishedAt After ReconciledAt & ObservedAt", + app: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "status": map[string]interface{}{ + "operationState": map[string]interface{}{ + "finishedAt": "2021-01-01T01:05:00Z", + "phase": "Succeeded", + }, + "reconciledAt": "2021-01-01T01:02:00Z", + "observedAt": "2021-01-01T01:04:00Z", + }, + }, + }, + expectedValue: false, + }, + { + name: "FinishedAt Before ReconciledAt & ObservedAt", + app: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "status": map[string]interface{}{ + "operationState": map[string]interface{}{ + "finishedAt": "2021-01-01T01:02:00Z", + "phase": "Succeeded", + }, + "reconciledAt": "2021-01-01T01:04:00Z", + "observedAt": "2021-01-01T01:06:00Z", + }, + }, + }, + expectedValue: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actualValue := isAppSyncStatusRefreshed(test.app, logEntry) + assert.Equal(t, test.expectedValue, actualValue) + }) + } +} + +func TestGetAppProj_invalidProjectNestedString(t *testing.T) { + app := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "spec": map[string]interface{}{}, + }, + } + informer := cache.NewSharedIndexInformer(nil, nil, 0, nil) + proj := getAppProj(app, informer) + + assert.Nil(t, proj) +} + +func TestInit(t *testing.T) { + scheme := runtime.NewScheme() + err := v1alpha1.SchemeBuilder.AddToScheme(scheme) + if err != nil { + t.Fatalf("Error registering the resource: %v", err) + } + dynamicClient := fake.NewSimpleDynamicClient(scheme) + k8sClient := k8sfake.NewSimpleClientset() + appLabelSelector := "app=test" + + selfServiceNotificationEnabledFlags := []bool{false, true} + for _, selfServiceNotificationEnabled := range selfServiceNotificationEnabledFlags { + nc := NewController( + k8sClient, + dynamicClient, + nil, + "default", + []string{}, + appLabelSelector, + nil, + "my-secret", + "my-configmap", + selfServiceNotificationEnabled, + ) + + assert.NotNil(t, nc) + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + err = nc.Init(ctx) + + assert.NoError(t, err) + } +} + +func TestInitTimeout(t *testing.T) { + scheme := runtime.NewScheme() + err := v1alpha1.SchemeBuilder.AddToScheme(scheme) + if err != nil { + t.Fatalf("Error registering the resource: %v", err) + } + dynamicClient := fake.NewSimpleDynamicClient(scheme) + k8sClient := k8sfake.NewSimpleClientset() + appLabelSelector := "app=test" + + nc := NewController( + k8sClient, + dynamicClient, + nil, + "default", + []string{}, + appLabelSelector, + nil, + "my-secret", + "my-configmap", + false, + ) + + assert.NotNil(t, nc) + + // Use a short timeout to simulate a timeout during cache synchronization + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond) + defer cancel() + + err = nc.Init(ctx) + + // Expect an error & add assertion for the error message + assert.Error(t, err) + assert.Equal(t, "Timed out waiting for caches to sync", err.Error()) +} + +func TestCheckAppNotInAdditionalNamespaces(t *testing.T) { + app := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "spec": map[string]interface{}{}, + }, + } + namespace := "argocd" + var applicationNamespaces []string + applicationNamespaces = append(applicationNamespaces, "namespace1") + applicationNamespaces = append(applicationNamespaces, "namespace2") + + // app is in same namespace as controller's namespace + app.SetNamespace(namespace) + assert.False(t, checkAppNotInAdditionalNamespaces(app, namespace, applicationNamespaces)) + + // app is not in the namespace as controller's namespace, but it is in one of the applicationNamespaces + app.SetNamespace("namespace2") + assert.False(t, checkAppNotInAdditionalNamespaces(app, "", applicationNamespaces)) + + // app is not in the namespace as controller's namespace, and it is not in any of the applicationNamespaces + app.SetNamespace("namespace3") + assert.True(t, checkAppNotInAdditionalNamespaces(app, "", applicationNamespaces)) +} diff --git a/notifications_catalog/install.yaml b/notifications_catalog/install.yaml index d399dcf7a3fbd..7457b25ddad89 100644 --- a/notifications_catalog/install.yaml +++ b/notifications_catalog/install.yaml @@ -40,8 +40,7 @@ data: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -68,8 +67,7 @@ data: "value": "{{.app.status.sync.revision}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" @@ -119,8 +117,7 @@ data: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -143,8 +140,7 @@ data: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" @@ -194,8 +190,7 @@ data: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -222,8 +217,7 @@ data: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" @@ -273,8 +267,7 @@ data: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -301,8 +294,7 @@ data: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" @@ -356,8 +348,7 @@ data: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -380,8 +371,7 @@ data: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" @@ -430,8 +420,7 @@ data: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -458,8 +447,7 @@ data: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" @@ -499,11 +487,11 @@ data: when: app.metadata.deletionTimestamp != nil trigger.on-deployed: | - description: Application is synced and healthy. Triggered once per commit. - oncePer: app.status.operationState.syncResult.revision + oncePer: app.status.operationState?.syncResult?.revision send: - app-deployed - when: app.status.operationState.phase in ['Succeeded'] and app.status.health.status - == 'Healthy' + when: app.status.operationState != nil and app.status.operationState.phase in ['Succeeded'] + and app.status.health.status == 'Healthy' trigger.on-health-degraded: | - description: Application has degraded send: @@ -513,12 +501,13 @@ data: - description: Application syncing has failed send: - app-sync-failed - when: app.status.operationState.phase in ['Error', 'Failed'] + when: app.status.operationState != nil and app.status.operationState.phase in ['Error', + 'Failed'] trigger.on-sync-running: | - description: Application is being synced send: - app-sync-running - when: app.status.operationState.phase in ['Running'] + when: app.status.operationState != nil and app.status.operationState.phase in ['Running'] trigger.on-sync-status-unknown: | - description: Application status is 'Unknown' send: @@ -528,7 +517,7 @@ data: - description: Application syncing has succeeded send: - app-sync-succeeded - when: app.status.operationState.phase in ['Succeeded'] + when: app.status.operationState != nil and app.status.operationState.phase in ['Succeeded'] kind: ConfigMap metadata: creationTimestamp: null diff --git a/notifications_catalog/templates/app-deployed.yaml b/notifications_catalog/templates/app-deployed.yaml index 843bf57e21a89..ee58c775f1fd8 100644 --- a/notifications_catalog/templates/app-deployed.yaml +++ b/notifications_catalog/templates/app-deployed.yaml @@ -25,8 +25,7 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -52,8 +51,7 @@ teams: "value": "{{.app.status.sync.revision}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" diff --git a/notifications_catalog/templates/app-health-degraded.yaml b/notifications_catalog/templates/app-health-degraded.yaml index 46c39b2e9ca0c..59115c9a14935 100644 --- a/notifications_catalog/templates/app-health-degraded.yaml +++ b/notifications_catalog/templates/app-health-degraded.yaml @@ -21,8 +21,7 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -44,8 +43,7 @@ teams: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" diff --git a/notifications_catalog/templates/app-sync-failed.yaml b/notifications_catalog/templates/app-sync-failed.yaml index 4a5ece85ba541..a4c23787dde8b 100644 --- a/notifications_catalog/templates/app-sync-failed.yaml +++ b/notifications_catalog/templates/app-sync-failed.yaml @@ -21,8 +21,7 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -48,8 +47,7 @@ teams: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" diff --git a/notifications_catalog/templates/app-sync-running.yaml b/notifications_catalog/templates/app-sync-running.yaml index b2a86042e3ce2..434132ad86d89 100644 --- a/notifications_catalog/templates/app-sync-running.yaml +++ b/notifications_catalog/templates/app-sync-running.yaml @@ -21,8 +21,7 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -47,8 +46,7 @@ teams: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" diff --git a/notifications_catalog/templates/app-sync-status-unknown.yaml b/notifications_catalog/templates/app-sync-status-unknown.yaml index b1af244fb6d2d..c893070bfcc63 100644 --- a/notifications_catalog/templates/app-sync-status-unknown.yaml +++ b/notifications_catalog/templates/app-sync-status-unknown.yaml @@ -26,8 +26,7 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -48,8 +47,7 @@ teams: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" diff --git a/notifications_catalog/templates/app-sync-succeeded.yaml b/notifications_catalog/templates/app-sync-succeeded.yaml index d791de55149a4..76e467bd1c37d 100644 --- a/notifications_catalog/templates/app-sync-succeeded.yaml +++ b/notifications_catalog/templates/app-sync-succeeded.yaml @@ -21,8 +21,7 @@ slack: "short": true } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "title": "{{$c.type}}", "value": "{{$c.message}}", @@ -48,8 +47,7 @@ teams: "value": "{{.app.spec.source.repoURL}}" } {{range $index, $c := .app.status.conditions}} - {{if not $index}},{{end}} - {{if $index}},{{end}} + , { "name": "{{$c.type}}", "value": "{{$c.message}}" diff --git a/notifications_catalog/triggers/on-deployed.yaml b/notifications_catalog/triggers/on-deployed.yaml index 93fdb0ab8666a..486fb15bd94d8 100644 --- a/notifications_catalog/triggers/on-deployed.yaml +++ b/notifications_catalog/triggers/on-deployed.yaml @@ -1,4 +1,4 @@ -- when: app.status.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy' +- when: app.status.operationState != nil and app.status.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy' description: Application is synced and healthy. Triggered once per commit. send: [app-deployed] - oncePer: app.status.operationState.syncResult.revision \ No newline at end of file + oncePer: app.status.operationState?.syncResult?.revision diff --git a/notifications_catalog/triggers/on-sync-failed.yaml b/notifications_catalog/triggers/on-sync-failed.yaml index 888a007f39247..b19afc561b0d5 100644 --- a/notifications_catalog/triggers/on-sync-failed.yaml +++ b/notifications_catalog/triggers/on-sync-failed.yaml @@ -1,3 +1,3 @@ -- when: app.status.operationState.phase in ['Error', 'Failed'] +- when: app.status.operationState != nil and app.status.operationState.phase in ['Error', 'Failed'] description: Application syncing has failed send: [app-sync-failed] diff --git a/notifications_catalog/triggers/on-sync-running.yaml b/notifications_catalog/triggers/on-sync-running.yaml index 005d06177051e..8ed62c9bf9fe5 100644 --- a/notifications_catalog/triggers/on-sync-running.yaml +++ b/notifications_catalog/triggers/on-sync-running.yaml @@ -1,3 +1,3 @@ -- when: app.status.operationState.phase in ['Running'] +- when: app.status.operationState != nil and app.status.operationState.phase in ['Running'] description: Application is being synced send: [app-sync-running] diff --git a/notifications_catalog/triggers/on-sync-succeeded.yaml b/notifications_catalog/triggers/on-sync-succeeded.yaml index 9e1c9fef5af3b..c3eb0e1aead70 100644 --- a/notifications_catalog/triggers/on-sync-succeeded.yaml +++ b/notifications_catalog/triggers/on-sync-succeeded.yaml @@ -1,3 +1,3 @@ -- when: app.status.operationState.phase in ['Succeeded'] +- when: app.status.operationState != nil and app.status.operationState.phase in ['Succeeded'] description: Application syncing has succeeded send: [app-sync-succeeded] diff --git a/pkg/apiclient/apiclient.go b/pkg/apiclient/apiclient.go index 5f122433ee285..83e841dd99bea 100644 --- a/pkg/apiclient/apiclient.go +++ b/pkg/apiclient/apiclient.go @@ -22,7 +22,6 @@ import ( grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" "github.com/hashicorp/go-retryablehttp" log "github.com/sirupsen/logrus" - "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "golang.org/x/oauth2" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -46,7 +45,6 @@ import ( settingspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/settings" versionpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/version" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/env" grpc_util "github.com/argoproj/argo-cd/v2/util/grpc" @@ -104,7 +102,7 @@ type Client interface { NewProjectClientOrDie() (io.Closer, projectpkg.ProjectServiceClient) NewAccountClient() (io.Closer, accountpkg.AccountServiceClient, error) NewAccountClientOrDie() (io.Closer, accountpkg.AccountServiceClient) - WatchApplicationWithRetry(ctx context.Context, appName string, revision string) chan *argoappv1.ApplicationWatchEvent + WatchApplicationWithRetry(ctx context.Context, appName string, revision string) chan *v1alpha1.ApplicationWatchEvent } // ClientOptions hold address, security, and other settings for the API client. @@ -127,6 +125,11 @@ type ClientOptions struct { Headers []string HttpRetryMax int KubeOverrides *clientcmd.ConfigOverrides + AppControllerName string + ServerName string + RedisHaProxyName string + RedisName string + RepoServerName string } type client struct { @@ -203,14 +206,13 @@ func NewClient(opts *ClientOptions) (Client, error) { c.UserAgent = fmt.Sprintf("%s/%s", common.ArgoCDUserAgentName, common.GetVersion().Version) } // Override server address if specified in env or CLI flag - if serverFromEnv := os.Getenv(EnvArgoCDServer); serverFromEnv != "" { - c.ServerAddr = serverFromEnv - } + c.ServerAddr = env.StringFromEnv(EnvArgoCDServer, c.ServerAddr) if opts.PortForward || opts.PortForwardNamespace != "" { if opts.KubeOverrides == nil { opts.KubeOverrides = &clientcmd.ConfigOverrides{} } - port, err := kube.PortForward(8080, opts.PortForwardNamespace, opts.KubeOverrides, "app.kubernetes.io/name=argocd-server") + serverPodLabelSelector := common.LabelKeyAppName + "=" + opts.ServerName + port, err := kube.PortForward(8080, opts.PortForwardNamespace, opts.KubeOverrides, serverPodLabelSelector) if err != nil { return nil, err } @@ -224,14 +226,8 @@ func NewClient(opts *ClientOptions) (Client, error) { if c.ServerAddr == "" { return nil, errors.New("Argo CD server address unspecified") } - if parts := strings.Split(c.ServerAddr, ":"); len(parts) == 1 { - // If port is unspecified, assume the most likely port - c.ServerAddr += ":443" - } // Override auth-token if specified in env variable or CLI flag - if authFromEnv := os.Getenv(EnvArgoCDAuthToken); authFromEnv != "" { - c.AuthToken = authFromEnv - } + c.AuthToken = env.StringFromEnv(EnvArgoCDAuthToken, c.AuthToken) if opts.AuthToken != "" { c.AuthToken = strings.TrimSpace(opts.AuthToken) } @@ -285,8 +281,12 @@ func NewClient(opts *ClientOptions) (Client, error) { } } if !c.GRPCWeb { - //test if we need to set it to true - //if a call to grpc failed, then try again with GRPCWeb + if parts := strings.Split(c.ServerAddr, ":"); len(parts) == 1 { + // If port is unspecified, assume the most likely port + c.ServerAddr += ":443" + } + // test if we need to set it to true + // if a call to grpc failed, then try again with GRPCWeb conn, versionIf, err := c.NewVersionClient() if err == nil { defer argoio.Close(conn) @@ -524,8 +524,8 @@ func (c *client) newConn() (*grpc.ClientConn, io.Closer, error) { dialOpts = append(dialOpts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(MaxGRPCMessageSize), grpc.MaxCallSendMsgSize(MaxGRPCMessageSize))) dialOpts = append(dialOpts, grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...))) dialOpts = append(dialOpts, grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(grpc_retry.UnaryClientInterceptor(retryOpts...)))) - dialOpts = append(dialOpts, grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor())) - dialOpts = append(dialOpts, grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor())) + dialOpts = append(dialOpts, grpc.WithUnaryInterceptor(grpc_util.OTELUnaryClientInterceptor())) + dialOpts = append(dialOpts, grpc.WithStreamInterceptor(grpc_util.OTELStreamClientInterceptor())) ctx := context.Background() @@ -806,10 +806,10 @@ func (c *client) NewAccountClientOrDie() (io.Closer, accountpkg.AccountServiceCl // WatchApplicationWithRetry returns a channel of watch events for an application, retrying the // watch upon errors. Closes the returned channel when the context is cancelled. -func (c *client) WatchApplicationWithRetry(ctx context.Context, appName string, revision string) chan *argoappv1.ApplicationWatchEvent { - appEventsCh := make(chan *argoappv1.ApplicationWatchEvent) +func (c *client) WatchApplicationWithRetry(ctx context.Context, appName string, revision string) chan *v1alpha1.ApplicationWatchEvent { + appEventsCh := make(chan *v1alpha1.ApplicationWatchEvent) cancelled := false - appName, appNs := argo.ParseAppQualifiedName(appName, "") + appName, appNs := argo.ParseFromQualifiedName(appName, "") go func() { defer close(appEventsCh) for !cancelled { diff --git a/pkg/apiclient/apiclient_test.go b/pkg/apiclient/apiclient_test.go index 7bb3b36befdde..b4b35d0b80d48 100644 --- a/pkg/apiclient/apiclient_test.go +++ b/pkg/apiclient/apiclient_test.go @@ -7,10 +7,34 @@ import ( ) func Test_parseHeaders(t *testing.T) { - headerString := []string{"foo:", "foo1:bar1", "foo2:bar2:bar2"} - headers, err := parseHeaders(headerString) - assert.NoError(t, err) - assert.Equal(t, headers.Get("foo"), "") - assert.Equal(t, headers.Get("foo1"), "bar1") - assert.Equal(t, headers.Get("foo2"), "bar2:bar2") + t.Run("Header parsed successfully", func(t *testing.T) { + headerString := []string{"foo:", "foo1:bar1", "foo2:bar2:bar2"} + headers, err := parseHeaders(headerString) + assert.NoError(t, err) + assert.Equal(t, headers.Get("foo"), "") + assert.Equal(t, headers.Get("foo1"), "bar1") + assert.Equal(t, headers.Get("foo2"), "bar2:bar2") + }) + + t.Run("Header parsed error", func(t *testing.T) { + headerString := []string{"foo"} + _, err := parseHeaders(headerString) + assert.ErrorContains(t, err, "additional headers must be colon(:)-separated: foo") + }) +} + +func Test_parseGRPCHeaders(t *testing.T) { + t.Run("Header parsed successfully", func(t *testing.T) { + headerStrings := []string{"origin: https://foo.bar", "content-length: 123"} + headers, err := parseGRPCHeaders(headerStrings) + assert.NoError(t, err) + assert.Equal(t, headers.Get("origin"), []string{" https://foo.bar"}) + assert.Equal(t, headers.Get("content-length"), []string{" 123"}) + }) + + t.Run("Header parsed error", func(t *testing.T) { + headerString := []string{"foo"} + _, err := parseGRPCHeaders(headerString) + assert.ErrorContains(t, err, "additional headers must be colon(:)-separated: foo") + }) } diff --git a/pkg/apiclient/application/application.pb.go b/pkg/apiclient/application/application.pb.go index 0de79ae034d70..70c63c36bc333 100644 --- a/pkg/apiclient/application/application.pb.go +++ b/pkg/apiclient/application/application.pb.go @@ -36,11 +36,15 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// ApplicationQuery is a query for application resources +// ApplicationQuery is a query for application resources. When getting multiple applications, the "projects" field acts +// as a filter. When getting a single application, you may specify either zero or one project. If you specify zero +// projects, the application will be returned regardless of which project it belongs to (assuming you have access). If +// you specify one project, the application will only be returned if it exists and belongs to the specified project. +// Otherwise you will receive a 404. type ApplicationQuery struct { // the application's name Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - // forces application reconciliation if set to true + // forces application reconciliation if set to 'hard' Refresh *string `protobuf:"bytes,2,opt,name=refresh" json:"refresh,omitempty"` // the project names to restrict returned list applications Projects []string `protobuf:"bytes,3,rep,name=projects" json:"projects,omitempty"` @@ -51,7 +55,9 @@ type ApplicationQuery struct { // the repoURL to restrict returned list applications Repo *string `protobuf:"bytes,6,opt,name=repo" json:"repo,omitempty"` // the application's namespace - AppNamespace *string `protobuf:"bytes,7,opt,name=appNamespace" json:"appNamespace,omitempty"` + AppNamespace *string `protobuf:"bytes,7,opt,name=appNamespace" json:"appNamespace,omitempty"` + // the project names to restrict returned list applications (legacy name for backwards-compatibility) + Project []string `protobuf:"bytes,8,rep,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -139,6 +145,13 @@ func (m *ApplicationQuery) GetAppNamespace() string { return "" } +func (m *ApplicationQuery) GetProject() []string { + if m != nil { + return m.Project + } + return nil +} + type NodeQuery struct { // the application's name Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` @@ -202,6 +215,7 @@ type RevisionMetadataQuery struct { Revision *string `protobuf:"bytes,2,req,name=revision" json:"revision,omitempty"` // the application's namespace AppNamespace *string `protobuf:"bytes,3,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,4,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -261,6 +275,13 @@ func (m *RevisionMetadataQuery) GetAppNamespace() string { return "" } +func (m *RevisionMetadataQuery) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + // ApplicationEventsQuery is a query for application resource events type ApplicationResourceEventsQuery struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` @@ -268,6 +289,7 @@ type ApplicationResourceEventsQuery struct { ResourceName *string `protobuf:"bytes,3,opt,name=resourceName" json:"resourceName,omitempty"` ResourceUID *string `protobuf:"bytes,4,opt,name=resourceUID" json:"resourceUID,omitempty"` AppNamespace *string `protobuf:"bytes,5,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,6,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -341,11 +363,19 @@ func (m *ApplicationResourceEventsQuery) GetAppNamespace() string { return "" } +func (m *ApplicationResourceEventsQuery) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + // ManifestQuery is a query for manifest resources type ApplicationManifestQuery struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Revision *string `protobuf:"bytes,2,opt,name=revision" json:"revision,omitempty"` AppNamespace *string `protobuf:"bytes,3,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,4,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -405,6 +435,13 @@ func (m *ApplicationManifestQuery) GetAppNamespace() string { return "" } +func (m *ApplicationManifestQuery) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type FileChunk struct { Chunk []byte `protobuf:"bytes,1,req,name=chunk" json:"chunk,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -456,6 +493,7 @@ type ApplicationManifestQueryWithFiles struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Checksum *string `protobuf:"bytes,2,req,name=checksum" json:"checksum,omitempty"` AppNamespace *string `protobuf:"bytes,3,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,4,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -515,6 +553,13 @@ func (m *ApplicationManifestQueryWithFiles) GetAppNamespace() string { return "" } +func (m *ApplicationManifestQueryWithFiles) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type ApplicationManifestQueryWithFilesWrapper struct { // Types that are valid to be assigned to Part: // *ApplicationManifestQueryWithFilesWrapper_Query @@ -712,6 +757,7 @@ func (m *ApplicationCreateRequest) GetValidate() bool { type ApplicationUpdateRequest struct { Application *v1alpha1.Application `protobuf:"bytes,1,req,name=application" json:"application,omitempty"` Validate *bool `protobuf:"varint,2,opt,name=validate" json:"validate,omitempty"` + Project *string `protobuf:"bytes,3,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -764,11 +810,19 @@ func (m *ApplicationUpdateRequest) GetValidate() bool { return false } +func (m *ApplicationUpdateRequest) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type ApplicationDeleteRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Cascade *bool `protobuf:"varint,2,opt,name=cascade" json:"cascade,omitempty"` PropagationPolicy *string `protobuf:"bytes,3,opt,name=propagationPolicy" json:"propagationPolicy,omitempty"` AppNamespace *string `protobuf:"bytes,4,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,5,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -835,6 +889,13 @@ func (m *ApplicationDeleteRequest) GetAppNamespace() string { return "" } +func (m *ApplicationDeleteRequest) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type SyncOptions struct { Items []string `protobuf:"bytes,1,rep,name=items" json:"items,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -895,6 +956,7 @@ type ApplicationSyncRequest struct { RetryStrategy *v1alpha1.RetryStrategy `protobuf:"bytes,10,opt,name=retryStrategy" json:"retryStrategy,omitempty"` SyncOptions *SyncOptions `protobuf:"bytes,11,opt,name=syncOptions" json:"syncOptions,omitempty"` AppNamespace *string `protobuf:"bytes,12,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,13,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1010,12 +1072,20 @@ func (m *ApplicationSyncRequest) GetAppNamespace() string { return "" } +func (m *ApplicationSyncRequest) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + // ApplicationUpdateSpecRequest is a request to update application spec type ApplicationUpdateSpecRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Spec *v1alpha1.ApplicationSpec `protobuf:"bytes,2,req,name=spec" json:"spec,omitempty"` Validate *bool `protobuf:"varint,3,opt,name=validate" json:"validate,omitempty"` AppNamespace *string `protobuf:"bytes,4,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,5,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1082,12 +1152,20 @@ func (m *ApplicationUpdateSpecRequest) GetAppNamespace() string { return "" } +func (m *ApplicationUpdateSpecRequest) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + // ApplicationPatchRequest is a request to patch an application type ApplicationPatchRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Patch *string `protobuf:"bytes,2,req,name=patch" json:"patch,omitempty"` PatchType *string `protobuf:"bytes,3,req,name=patchType" json:"patchType,omitempty"` AppNamespace *string `protobuf:"bytes,5,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,6,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1154,12 +1232,20 @@ func (m *ApplicationPatchRequest) GetAppNamespace() string { return "" } +func (m *ApplicationPatchRequest) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type ApplicationRollbackRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Id *int64 `protobuf:"varint,2,req,name=id" json:"id,omitempty"` DryRun *bool `protobuf:"varint,3,opt,name=dryRun" json:"dryRun,omitempty"` Prune *bool `protobuf:"varint,4,opt,name=prune" json:"prune,omitempty"` AppNamespace *string `protobuf:"bytes,6,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,7,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1233,6 +1319,13 @@ func (m *ApplicationRollbackRequest) GetAppNamespace() string { return "" } +func (m *ApplicationRollbackRequest) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type ApplicationResourceRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"` @@ -1241,6 +1334,7 @@ type ApplicationResourceRequest struct { Group *string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"` Kind *string `protobuf:"bytes,6,req,name=kind" json:"kind,omitempty"` AppNamespace *string `protobuf:"bytes,7,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,8,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1328,6 +1422,13 @@ func (m *ApplicationResourceRequest) GetAppNamespace() string { return "" } +func (m *ApplicationResourceRequest) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type ApplicationResourcePatchRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"` @@ -1338,6 +1439,7 @@ type ApplicationResourcePatchRequest struct { Patch *string `protobuf:"bytes,7,req,name=patch" json:"patch,omitempty"` PatchType *string `protobuf:"bytes,8,req,name=patchType" json:"patchType,omitempty"` AppNamespace *string `protobuf:"bytes,9,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,10,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1439,6 +1541,13 @@ func (m *ApplicationResourcePatchRequest) GetAppNamespace() string { return "" } +func (m *ApplicationResourcePatchRequest) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type ApplicationResourceDeleteRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"` @@ -1449,6 +1558,7 @@ type ApplicationResourceDeleteRequest struct { Force *bool `protobuf:"varint,7,opt,name=force" json:"force,omitempty"` Orphan *bool `protobuf:"varint,8,opt,name=orphan" json:"orphan,omitempty"` AppNamespace *string `protobuf:"bytes,9,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,10,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1550,6 +1660,13 @@ func (m *ApplicationResourceDeleteRequest) GetAppNamespace() string { return "" } +func (m *ApplicationResourceDeleteRequest) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type ResourceActionRunRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"` @@ -1559,6 +1676,7 @@ type ResourceActionRunRequest struct { Kind *string `protobuf:"bytes,6,req,name=kind" json:"kind,omitempty"` Action *string `protobuf:"bytes,7,req,name=action" json:"action,omitempty"` AppNamespace *string `protobuf:"bytes,8,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,9,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1653,6 +1771,13 @@ func (m *ResourceActionRunRequest) GetAppNamespace() string { return "" } +func (m *ResourceActionRunRequest) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type ResourceActionsListResponse struct { Actions []*v1alpha1.ResourceAction `protobuf:"bytes,1,rep,name=actions" json:"actions,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -1763,6 +1888,7 @@ type ApplicationPodLogsQuery struct { ResourceName *string `protobuf:"bytes,13,opt,name=resourceName" json:"resourceName,omitempty"` Previous *bool `protobuf:"varint,14,opt,name=previous" json:"previous,omitempty"` AppNamespace *string `protobuf:"bytes,15,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,16,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1906,6 +2032,13 @@ func (m *ApplicationPodLogsQuery) GetAppNamespace() string { return "" } +func (m *ApplicationPodLogsQuery) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type LogEntry struct { Content *string `protobuf:"bytes,1,req,name=content" json:"content,omitempty"` // deprecated in favor of timeStampStr since meta.v1.Time don't support nano time @@ -1989,6 +2122,7 @@ func (m *LogEntry) GetPodName() string { type OperationTerminateRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` AppNamespace *string `protobuf:"bytes,2,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,3,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2041,9 +2175,17 @@ func (m *OperationTerminateRequest) GetAppNamespace() string { return "" } +func (m *OperationTerminateRequest) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type ApplicationSyncWindowsQuery struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` AppNamespace *string `protobuf:"bytes,2,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,3,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2096,6 +2238,13 @@ func (m *ApplicationSyncWindowsQuery) GetAppNamespace() string { return "" } +func (m *ApplicationSyncWindowsQuery) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type ApplicationSyncWindowsResponse struct { ActiveWindows []*ApplicationSyncWindow `protobuf:"bytes,1,rep,name=activeWindows" json:"activeWindows,omitempty"` AssignedWindows []*ApplicationSyncWindow `protobuf:"bytes,2,rep,name=assignedWindows" json:"assignedWindows,omitempty"` @@ -2277,6 +2426,7 @@ type ResourcesQuery struct { Group *string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"` Kind *string `protobuf:"bytes,6,opt,name=kind" json:"kind,omitempty"` AppNamespace *string `protobuf:"bytes,7,opt,name=appNamespace" json:"appNamespace,omitempty"` + Project *string `protobuf:"bytes,8,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2364,6 +2514,13 @@ func (m *ResourcesQuery) GetAppNamespace() string { return "" } +func (m *ResourcesQuery) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + type ManagedResourcesResponse struct { Items []*v1alpha1.ResourceDiff `protobuf:"bytes,1,rep,name=items" json:"items,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -2532,6 +2689,7 @@ func (m *LinksResponse) GetItems() []*LinkInfo { type ListAppLinksRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Namespace *string `protobuf:"bytes,3,opt,name=namespace" json:"namespace,omitempty"` + Project *string `protobuf:"bytes,4,opt,name=project" json:"project,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2584,6 +2742,13 @@ func (m *ListAppLinksRequest) GetNamespace() string { return "" } +func (m *ListAppLinksRequest) GetProject() string { + if m != nil && m.Project != nil { + return *m.Project + } + return "" +} + func init() { proto.RegisterType((*ApplicationQuery)(nil), "application.ApplicationQuery") proto.RegisterType((*NodeQuery)(nil), "application.NodeQuery") @@ -2627,169 +2792,175 @@ func init() { } var fileDescriptor_df6e82b174b5eaec = []byte{ - // 2581 bytes of a gzipped FileDescriptorProto + // 2673 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x8f, 0x1c, 0x47, - 0x15, 0xa7, 0x66, 0xbf, 0x66, 0xde, 0xac, 0xbf, 0x2a, 0xf1, 0xd2, 0x69, 0xaf, 0xcd, 0xba, 0xfd, - 0xb5, 0x5e, 0x7b, 0x67, 0xec, 0xc1, 0x20, 0x67, 0x13, 0x04, 0xb6, 0xe3, 0x2f, 0x58, 0x3b, 0xa6, - 0xd7, 0xc6, 0x28, 0x1c, 0xa0, 0xd2, 0x53, 0x3b, 0xdb, 0x6c, 0x4f, 0x77, 0xbb, 0xbb, 0x67, 0xac, - 0x91, 0xf1, 0x25, 0x88, 0x13, 0x51, 0x90, 0x92, 0x1c, 0x50, 0x14, 0x21, 0x94, 0x28, 0x17, 0x2e, - 0xdc, 0x10, 0x12, 0x17, 0xb8, 0x20, 0x90, 0x38, 0x20, 0x3e, 0x2e, 0x39, 0x21, 0x8b, 0x1b, 0x17, - 0xfe, 0x04, 0x54, 0xd5, 0x55, 0xdd, 0xd5, 0x33, 0x3d, 0x3d, 0xbd, 0xec, 0x46, 0xf1, 0xad, 0x5e, - 0x4d, 0xd5, 0x7b, 0xbf, 0x7a, 0xf5, 0xbe, 0xea, 0xf5, 0xc0, 0xc9, 0x90, 0x06, 0x7d, 0x1a, 0x34, - 0x89, 0xef, 0x3b, 0xb6, 0x45, 0x22, 0xdb, 0x73, 0xd5, 0x71, 0xc3, 0x0f, 0xbc, 0xc8, 0xc3, 0x75, - 0x65, 0x4a, 0x5f, 0xec, 0x78, 0x5e, 0xc7, 0xa1, 0x4d, 0xe2, 0xdb, 0x4d, 0xe2, 0xba, 0x5e, 0xc4, - 0xa7, 0xc3, 0x78, 0xa9, 0x6e, 0x6c, 0x5f, 0x0e, 0x1b, 0xb6, 0xc7, 0x7f, 0xb5, 0xbc, 0x80, 0x36, - 0xfb, 0x17, 0x9b, 0x1d, 0xea, 0xd2, 0x80, 0x44, 0xb4, 0x2d, 0xd6, 0x5c, 0x4a, 0xd7, 0x74, 0x89, - 0xb5, 0x65, 0xbb, 0x34, 0x18, 0x34, 0xfd, 0xed, 0x0e, 0x9b, 0x08, 0x9b, 0x5d, 0x1a, 0x91, 0xbc, - 0x5d, 0xeb, 0x1d, 0x3b, 0xda, 0xea, 0xbd, 0xd9, 0xb0, 0xbc, 0x6e, 0x93, 0x04, 0x1d, 0xcf, 0x0f, - 0xbc, 0x1f, 0xf2, 0xc1, 0xaa, 0xd5, 0x6e, 0xf6, 0x5b, 0x29, 0x03, 0xf5, 0x2c, 0xfd, 0x8b, 0xc4, - 0xf1, 0xb7, 0xc8, 0x28, 0xb7, 0xeb, 0x13, 0xb8, 0x05, 0xd4, 0xf7, 0x84, 0x6e, 0xf8, 0xd0, 0x8e, - 0xbc, 0x60, 0xa0, 0x0c, 0x63, 0x36, 0xc6, 0xa7, 0x08, 0x0e, 0x5e, 0x49, 0xe5, 0x7d, 0xbb, 0x47, - 0x83, 0x01, 0xc6, 0x30, 0xed, 0x92, 0x2e, 0xd5, 0xd0, 0x12, 0x5a, 0xae, 0x99, 0x7c, 0x8c, 0x35, - 0x98, 0x0b, 0xe8, 0x66, 0x40, 0xc3, 0x2d, 0xad, 0xc2, 0xa7, 0x25, 0x89, 0x75, 0xa8, 0x32, 0xe1, - 0xd4, 0x8a, 0x42, 0x6d, 0x6a, 0x69, 0x6a, 0xb9, 0x66, 0x26, 0x34, 0x5e, 0x86, 0x03, 0x01, 0x0d, - 0xbd, 0x5e, 0x60, 0xd1, 0xef, 0xd0, 0x20, 0xb4, 0x3d, 0x57, 0x9b, 0xe6, 0xbb, 0x87, 0xa7, 0x19, - 0x97, 0x90, 0x3a, 0xd4, 0x8a, 0xbc, 0x40, 0x9b, 0xe1, 0x4b, 0x12, 0x9a, 0xe1, 0x61, 0xc0, 0xb5, - 0xd9, 0x18, 0x0f, 0x1b, 0x63, 0x03, 0xe6, 0x89, 0xef, 0xdf, 0x25, 0x5d, 0x1a, 0xfa, 0xc4, 0xa2, - 0xda, 0x1c, 0xff, 0x2d, 0x33, 0x67, 0x5c, 0x83, 0xda, 0x5d, 0xaf, 0x4d, 0xc7, 0x1f, 0x6a, 0x98, - 0x49, 0x25, 0x87, 0xc9, 0x36, 0x1c, 0x36, 0x69, 0xdf, 0x66, 0x20, 0xef, 0xd0, 0x88, 0xb4, 0x49, - 0x44, 0x86, 0x19, 0x56, 0x12, 0x86, 0x3a, 0x54, 0x03, 0xb1, 0x58, 0xab, 0xf0, 0xf9, 0x84, 0x1e, - 0x11, 0x36, 0x95, 0x23, 0xec, 0x2f, 0x08, 0x8e, 0x29, 0xd7, 0x61, 0x0a, 0x25, 0x5d, 0xef, 0x53, - 0x37, 0x0a, 0xc7, 0x8b, 0x3d, 0x0f, 0x87, 0xa4, 0x3e, 0x87, 0x0f, 0x33, 0xfa, 0x03, 0x03, 0xa2, - 0x4e, 0x4a, 0x20, 0xea, 0x1c, 0x5e, 0x82, 0xba, 0xa4, 0x1f, 0xdc, 0x7e, 0x4d, 0x5c, 0x9a, 0x3a, - 0x35, 0x72, 0x9c, 0x99, 0x9c, 0xe3, 0xb8, 0xa0, 0x29, 0xa7, 0xb9, 0x43, 0x5c, 0x7b, 0x93, 0x86, - 0x51, 0x59, 0xf5, 0xa1, 0x1d, 0xab, 0xef, 0x38, 0xd4, 0x6e, 0xd8, 0x0e, 0xbd, 0xb6, 0xd5, 0x73, - 0xb7, 0xf1, 0x8b, 0x30, 0x63, 0xb1, 0x01, 0x97, 0x30, 0x6f, 0xc6, 0x84, 0xf1, 0x18, 0x8e, 0x8f, - 0x83, 0xf4, 0xd0, 0x8e, 0xb6, 0xd8, 0xf6, 0x70, 0x1c, 0x36, 0x6b, 0x8b, 0x5a, 0xdb, 0x61, 0xaf, - 0x2b, 0xaf, 0x56, 0xd2, 0xa5, 0xb0, 0xfd, 0x0a, 0xc1, 0xf2, 0x44, 0xc9, 0x0f, 0x03, 0xe2, 0xfb, - 0x34, 0xc0, 0x37, 0x60, 0xe6, 0x11, 0xfb, 0x81, 0x5b, 0x6b, 0xbd, 0xd5, 0x68, 0xa8, 0x31, 0x6d, - 0x22, 0x97, 0x5b, 0x5f, 0x30, 0xe3, 0xed, 0xb8, 0x21, 0x75, 0x50, 0xe1, 0x7c, 0x16, 0x32, 0x7c, - 0x12, 0x55, 0xb1, 0xf5, 0x7c, 0xd9, 0xd5, 0x59, 0x98, 0xf6, 0x49, 0x10, 0x19, 0x87, 0xe1, 0x85, - 0xac, 0x19, 0xfa, 0x9e, 0x1b, 0x52, 0xe3, 0x77, 0x28, 0x73, 0xa1, 0xd7, 0x02, 0x4a, 0x22, 0x6a, - 0xd2, 0x47, 0x3d, 0x1a, 0x46, 0x78, 0x1b, 0xd4, 0x30, 0xcb, 0x75, 0x57, 0x6f, 0xdd, 0x6e, 0xa4, - 0x71, 0xaa, 0x21, 0xe3, 0x14, 0x1f, 0x7c, 0xdf, 0x6a, 0x37, 0xfa, 0xad, 0x86, 0xbf, 0xdd, 0x69, - 0xb0, 0xa8, 0x97, 0x41, 0x26, 0xa3, 0x9e, 0x7a, 0x54, 0x53, 0xe5, 0x8e, 0x17, 0x60, 0xb6, 0xe7, - 0x87, 0x34, 0x88, 0xf8, 0xc9, 0xaa, 0xa6, 0xa0, 0xd8, 0x2d, 0xf5, 0x89, 0x63, 0xb7, 0x49, 0x14, - 0xdf, 0x42, 0xd5, 0x4c, 0x68, 0xe3, 0xe3, 0x2c, 0xfa, 0x07, 0x7e, 0xfb, 0xf3, 0x42, 0xaf, 0xa2, - 0xac, 0x0c, 0xa1, 0xfc, 0x20, 0x8b, 0xf2, 0x35, 0xea, 0xd0, 0x14, 0x65, 0x9e, 0x61, 0x6a, 0x30, - 0x67, 0x91, 0xd0, 0x22, 0x6d, 0xc9, 0x4b, 0x92, 0x2c, 0x2c, 0xf8, 0x81, 0xe7, 0x93, 0x0e, 0xe7, - 0x74, 0xcf, 0x73, 0x6c, 0x6b, 0x20, 0x6c, 0x73, 0xf4, 0x87, 0x11, 0x23, 0x9e, 0xce, 0x31, 0xe2, - 0x13, 0x50, 0xdf, 0x18, 0xb8, 0xd6, 0xeb, 0x3e, 0x4f, 0x99, 0xcc, 0xc5, 0xec, 0x88, 0x76, 0x43, - 0x0d, 0xf1, 0xb8, 0x1f, 0x13, 0xc6, 0x87, 0x33, 0xb0, 0xa0, 0x9c, 0x80, 0x6d, 0x28, 0xc2, 0x5f, - 0xe4, 0xf4, 0x0b, 0x30, 0xdb, 0x0e, 0x06, 0x66, 0xcf, 0x15, 0x97, 0x29, 0x28, 0x26, 0xd8, 0x0f, - 0x7a, 0x6e, 0x0c, 0xb2, 0x6a, 0xc6, 0x04, 0xde, 0x84, 0x6a, 0x18, 0xb1, 0x24, 0xd9, 0x19, 0xf0, - 0x70, 0x54, 0x6f, 0x7d, 0x73, 0x77, 0x17, 0xc8, 0xa0, 0x6f, 0x08, 0x8e, 0x66, 0xc2, 0x1b, 0x3f, - 0x82, 0x9a, 0x8c, 0x84, 0xa1, 0x36, 0xb7, 0x34, 0xb5, 0x5c, 0x6f, 0x6d, 0xec, 0x5e, 0xd0, 0xeb, - 0x3e, 0x4b, 0xf0, 0x4a, 0xd4, 0x37, 0x53, 0x29, 0x78, 0x11, 0x6a, 0x5d, 0xe1, 0xeb, 0xa1, 0x56, - 0xe5, 0xda, 0x4e, 0x27, 0xf0, 0x77, 0x61, 0xc6, 0x76, 0x37, 0xbd, 0x50, 0xab, 0x71, 0x30, 0x57, - 0x77, 0x07, 0xe6, 0xb6, 0xbb, 0xe9, 0x99, 0x31, 0x43, 0xfc, 0x08, 0xf6, 0x05, 0x34, 0x0a, 0x06, - 0x52, 0x0b, 0x1a, 0x70, 0xbd, 0x7e, 0x6b, 0x77, 0x12, 0x4c, 0x95, 0xa5, 0x99, 0x95, 0x80, 0xd7, - 0xa0, 0x1e, 0xa6, 0x36, 0xa6, 0xd5, 0xb9, 0x40, 0x2d, 0xc3, 0x48, 0xb1, 0x41, 0x53, 0x5d, 0x3c, - 0x62, 0xc3, 0xf3, 0x39, 0x36, 0xfc, 0x4f, 0x04, 0x8b, 0x23, 0x61, 0x60, 0xc3, 0xa7, 0x85, 0x46, - 0x4a, 0x60, 0x3a, 0xf4, 0xa9, 0xc5, 0x23, 0x7f, 0xbd, 0x75, 0x67, 0xcf, 0xe2, 0x02, 0x97, 0xcb, - 0x59, 0x17, 0x85, 0xae, 0x52, 0xbe, 0xf9, 0x13, 0x04, 0x5f, 0x54, 0x38, 0xdf, 0x23, 0x91, 0xb5, - 0x55, 0x74, 0x24, 0xe6, 0x43, 0x6c, 0x8d, 0xc8, 0x66, 0x31, 0xc1, 0x0c, 0x8d, 0x0f, 0xee, 0x0f, - 0x7c, 0x06, 0x83, 0xfd, 0x92, 0x4e, 0x94, 0x4a, 0xfa, 0xef, 0x22, 0xd0, 0xd5, 0xc8, 0xe7, 0x39, - 0xce, 0x9b, 0xc4, 0xda, 0x2e, 0x82, 0xb2, 0x1f, 0x2a, 0x76, 0x9b, 0xe3, 0x98, 0x32, 0x2b, 0x76, - 0x7b, 0x87, 0x6e, 0x3f, 0x0c, 0x6a, 0x36, 0x07, 0xd4, 0xa7, 0x43, 0xa0, 0xa4, 0x8b, 0x15, 0x80, - 0x5a, 0x84, 0x9a, 0x3b, 0x54, 0x4c, 0xa5, 0x13, 0x39, 0x45, 0x54, 0x65, 0xa4, 0x88, 0xd2, 0x60, - 0xae, 0x9f, 0x54, 0xbd, 0xec, 0x67, 0x49, 0xb2, 0x83, 0x74, 0x02, 0xaf, 0xe7, 0x0b, 0x05, 0xc6, - 0x04, 0x43, 0xb1, 0x6d, 0xbb, 0x6d, 0x6d, 0x36, 0x46, 0xc1, 0xc6, 0xa5, 0xea, 0xdc, 0xf7, 0x2a, - 0xf0, 0xa5, 0x9c, 0xc3, 0x4d, 0xb4, 0x80, 0xe7, 0xe3, 0x84, 0x89, 0x1d, 0xce, 0x8d, 0xb5, 0xc3, - 0xea, 0x24, 0x3b, 0xac, 0xe5, 0x68, 0xe5, 0x9d, 0x0a, 0x2c, 0xe5, 0x68, 0x65, 0x72, 0x42, 0x7d, - 0x6e, 0xd4, 0xb2, 0xe9, 0x05, 0xe2, 0xc6, 0xab, 0x66, 0x4c, 0x30, 0xcf, 0xf0, 0x02, 0x7f, 0x8b, - 0xb8, 0x5a, 0x35, 0xf6, 0x8c, 0x98, 0x2a, 0xa5, 0x90, 0xff, 0x22, 0xd0, 0xa4, 0x16, 0xae, 0x58, - 0x5c, 0x27, 0x3d, 0xf7, 0xf9, 0x57, 0xc4, 0x02, 0xcc, 0x12, 0x8e, 0x56, 0x18, 0x88, 0xa0, 0x46, - 0x8e, 0x5c, 0xcd, 0x8f, 0x89, 0x47, 0xb2, 0x47, 0x0e, 0xd7, 0xed, 0x30, 0x92, 0x05, 0x2d, 0xde, - 0x84, 0xb9, 0x98, 0x5b, 0x5c, 0xc2, 0xd4, 0x5b, 0xeb, 0xbb, 0x4d, 0x6c, 0x19, 0xf5, 0x4a, 0xe6, - 0xc6, 0xcb, 0x70, 0x24, 0x37, 0xfa, 0x08, 0x18, 0x3a, 0x54, 0x65, 0x32, 0x17, 0x17, 0x90, 0xd0, - 0xc6, 0x7f, 0xa6, 0xb2, 0x61, 0xdd, 0x6b, 0xaf, 0x7b, 0x9d, 0x82, 0xb7, 0x60, 0xf1, 0xa5, 0x69, - 0x30, 0xe7, 0x7b, 0x6d, 0xe5, 0xd9, 0x27, 0x49, 0xb6, 0xcf, 0xf2, 0xdc, 0x88, 0xd8, 0x2e, 0x0d, - 0x44, 0x7e, 0x49, 0x27, 0x98, 0xb2, 0x43, 0xdb, 0xb5, 0xe8, 0x06, 0xb5, 0x3c, 0xb7, 0x1d, 0xf2, - 0x5b, 0x9b, 0x32, 0x33, 0x73, 0xf8, 0x16, 0xd4, 0x38, 0x7d, 0xdf, 0xee, 0xc6, 0x41, 0xb8, 0xde, - 0x5a, 0x69, 0xc4, 0xad, 0x92, 0x86, 0xda, 0x2a, 0x49, 0x75, 0xd8, 0xa5, 0x11, 0x69, 0xf4, 0x2f, - 0x36, 0xd8, 0x0e, 0x33, 0xdd, 0xcc, 0xb0, 0x44, 0xc4, 0x76, 0xd6, 0x6d, 0x97, 0x17, 0x58, 0x4c, - 0x54, 0x3a, 0xc1, 0x0c, 0x62, 0xd3, 0x73, 0x1c, 0xef, 0xb1, 0xf4, 0x81, 0x98, 0x62, 0xbb, 0x7a, - 0x6e, 0x64, 0x3b, 0x5c, 0x7e, 0xec, 0x00, 0xe9, 0x04, 0xdf, 0x65, 0x3b, 0x11, 0x0d, 0x78, 0x09, - 0x53, 0x33, 0x05, 0x95, 0x98, 0x5c, 0x3d, 0xee, 0x0b, 0x48, 0xdf, 0x8b, 0x8d, 0x73, 0x5e, 0x35, - 0xce, 0x61, 0x83, 0xdf, 0x97, 0xf3, 0x6e, 0xe6, 0xcd, 0x10, 0xda, 0xb7, 0xbd, 0x5e, 0xa8, 0xed, - 0x8f, 0x93, 0xb8, 0xa4, 0x47, 0x0c, 0xf6, 0x40, 0x8e, 0xc1, 0xfe, 0x1e, 0x41, 0x75, 0xdd, 0xeb, - 0x5c, 0x77, 0xa3, 0x60, 0xc0, 0x2b, 0x7b, 0xcf, 0x8d, 0xa8, 0x2b, 0xad, 0x42, 0x92, 0x4c, 0xd5, - 0x91, 0xdd, 0xa5, 0x1b, 0x11, 0xe9, 0xfa, 0xa2, 0x26, 0xd9, 0x91, 0xaa, 0x93, 0xcd, 0xec, 0xf8, - 0x0e, 0x09, 0x23, 0xee, 0xbd, 0x55, 0x93, 0x8f, 0x19, 0xd0, 0x64, 0xc1, 0x46, 0x14, 0x08, 0xd7, - 0xcd, 0xcc, 0xa9, 0x86, 0x34, 0x13, 0x63, 0x13, 0xa4, 0xb1, 0x01, 0x2f, 0x25, 0xa5, 0xec, 0x7d, - 0x1a, 0x74, 0x6d, 0x97, 0x14, 0xc7, 0xdb, 0x32, 0x5d, 0x98, 0x07, 0x19, 0x07, 0x62, 0xf5, 0xdf, - 0x43, 0xdb, 0x6d, 0x7b, 0x8f, 0x0b, 0x1c, 0xa1, 0x0c, 0xdb, 0xbf, 0x65, 0xfb, 0x2d, 0x0a, 0xdf, - 0xc4, 0x37, 0x6f, 0xc1, 0x3e, 0xe6, 0xc5, 0x7d, 0x2a, 0x7e, 0x10, 0x81, 0xc2, 0x18, 0xf7, 0x24, - 0x4f, 0x79, 0x98, 0xd9, 0x8d, 0x78, 0x1d, 0x0e, 0x90, 0x30, 0xb4, 0x3b, 0x2e, 0x6d, 0x4b, 0x5e, - 0x95, 0xd2, 0xbc, 0x86, 0xb7, 0xc6, 0xcf, 0x3e, 0xbe, 0x42, 0xdc, 0x9d, 0x24, 0x8d, 0x1f, 0x23, - 0x38, 0x9c, 0xcb, 0x24, 0xb1, 0x75, 0xa4, 0x84, 0x57, 0x1d, 0xaa, 0xa1, 0xb5, 0x45, 0xdb, 0x3d, - 0x87, 0xca, 0xbe, 0x86, 0xa4, 0xd9, 0x6f, 0xed, 0x5e, 0x7c, 0x93, 0x22, 0xbc, 0x27, 0x34, 0x3e, - 0x06, 0xd0, 0x25, 0x6e, 0x8f, 0x38, 0x1c, 0xc2, 0x34, 0x87, 0xa0, 0xcc, 0x18, 0x8b, 0xa0, 0xe7, - 0x99, 0x81, 0xe8, 0x24, 0xfc, 0x03, 0xc1, 0x7e, 0x19, 0x06, 0xc5, 0x1d, 0x2e, 0xc3, 0x01, 0x45, - 0x0d, 0x77, 0xd3, 0xeb, 0x1c, 0x9e, 0x9e, 0x10, 0xe2, 0xa4, 0x2d, 0x4c, 0x65, 0xbb, 0x97, 0xfd, - 0x4c, 0xff, 0xb1, 0x74, 0x1e, 0x42, 0x3b, 0xaa, 0xc4, 0x7e, 0x04, 0xda, 0x1d, 0xe2, 0x92, 0x0e, - 0x6d, 0x27, 0x87, 0x4b, 0x0c, 0xe9, 0x07, 0xea, 0x63, 0x79, 0xd7, 0x4f, 0xd3, 0xa4, 0x9c, 0xb1, - 0x37, 0x37, 0xe5, 0xc3, 0x3b, 0x80, 0xea, 0xba, 0xed, 0x6e, 0xb3, 0xf7, 0x1b, 0x3b, 0x57, 0x64, - 0x47, 0x8e, 0xd4, 0x61, 0x4c, 0xe0, 0x83, 0x30, 0xd5, 0x0b, 0x1c, 0x71, 0xcf, 0x6c, 0x88, 0x97, - 0xa0, 0xde, 0xa6, 0xa1, 0x15, 0xd8, 0xbe, 0xb8, 0x65, 0xde, 0xe8, 0x53, 0xa6, 0x98, 0xb6, 0x6d, - 0xcb, 0x73, 0xaf, 0x39, 0x24, 0x0c, 0x65, 0x62, 0x48, 0x26, 0x8c, 0x57, 0x61, 0x1f, 0x93, 0x99, - 0x1e, 0xf3, 0x5c, 0xf6, 0x98, 0x87, 0x33, 0xf0, 0x25, 0x3c, 0x89, 0xf8, 0x26, 0xbc, 0xc0, 0xf2, - 0xf1, 0x15, 0xdf, 0x17, 0x4c, 0x4a, 0x16, 0x23, 0x53, 0x43, 0x97, 0xde, 0xfa, 0xa9, 0x01, 0x58, - 0xb5, 0x79, 0x1a, 0xf4, 0x6d, 0x8b, 0xe2, 0x77, 0x11, 0x4c, 0x33, 0x01, 0xf8, 0xe8, 0x38, 0x17, - 0xe3, 0xb6, 0xa7, 0xef, 0xdd, 0x83, 0x8e, 0x49, 0x33, 0x16, 0xdf, 0xfa, 0xfb, 0xbf, 0xdf, 0xab, - 0x2c, 0xe0, 0x17, 0xf9, 0x67, 0x84, 0xfe, 0x45, 0xb5, 0xa5, 0x1f, 0xe2, 0xb7, 0x11, 0x60, 0x51, - 0x85, 0x28, 0xdd, 0x5d, 0x7c, 0x6e, 0x1c, 0xc4, 0x9c, 0x2e, 0xb0, 0x7e, 0x54, 0x89, 0xf6, 0x0d, - 0xcb, 0x0b, 0x28, 0x8b, 0xed, 0x7c, 0x01, 0x07, 0xb0, 0xc2, 0x01, 0x9c, 0xc4, 0x46, 0x1e, 0x80, - 0xe6, 0x13, 0xa6, 0xb7, 0xa7, 0x4d, 0x1a, 0xcb, 0xfd, 0x08, 0xc1, 0xcc, 0x43, 0x5e, 0x73, 0x4f, - 0x50, 0xd2, 0xc6, 0x9e, 0x29, 0x89, 0x8b, 0xe3, 0x68, 0x8d, 0x13, 0x1c, 0xe9, 0x51, 0x7c, 0x44, - 0x22, 0x0d, 0xa3, 0x80, 0x92, 0x6e, 0x06, 0xf0, 0x05, 0x84, 0x3f, 0x41, 0x30, 0x1b, 0xb7, 0x1b, - 0xf1, 0xa9, 0x71, 0x28, 0x33, 0xed, 0x48, 0x7d, 0xef, 0x7a, 0x77, 0xc6, 0x59, 0x8e, 0xf1, 0x84, - 0x91, 0x7b, 0x9d, 0x6b, 0x99, 0xce, 0xde, 0xfb, 0x08, 0xa6, 0x6e, 0xd2, 0x89, 0xf6, 0xb6, 0x87, - 0xe0, 0x46, 0x14, 0x98, 0x73, 0xd5, 0xf8, 0x63, 0x04, 0x2f, 0xdd, 0xa4, 0x51, 0x7e, 0xaa, 0xc3, - 0xcb, 0x93, 0xf3, 0x8f, 0x30, 0xbb, 0x73, 0x25, 0x56, 0x26, 0x31, 0xbe, 0xc9, 0x91, 0x9d, 0xc5, - 0x67, 0x8a, 0x8c, 0x30, 0x1c, 0xb8, 0xd6, 0x63, 0x81, 0xe3, 0xcf, 0x08, 0x0e, 0x0e, 0x7f, 0x6b, - 0xc1, 0xd9, 0xe4, 0x98, 0xfb, 0x29, 0x46, 0xbf, 0xbb, 0xdb, 0x58, 0x9a, 0x65, 0x6a, 0x5c, 0xe1, - 0xc8, 0x5f, 0xc1, 0x2f, 0x17, 0x21, 0x97, 0x4d, 0xca, 0xb0, 0xf9, 0x44, 0x0e, 0x9f, 0xf2, 0x8f, - 0x7f, 0x1c, 0xf6, 0x5b, 0x08, 0xe6, 0x6f, 0xd2, 0xe8, 0x4e, 0xd2, 0xa3, 0x3b, 0x55, 0xaa, 0x87, - 0xaf, 0x2f, 0x36, 0x94, 0x6f, 0x74, 0xf2, 0xa7, 0x44, 0xa5, 0xab, 0x1c, 0xd8, 0x19, 0x7c, 0xaa, - 0x08, 0x58, 0xda, 0x17, 0xfc, 0x08, 0xc1, 0x61, 0x15, 0x44, 0xfa, 0x85, 0xe3, 0x2b, 0x3b, 0xfb, - 0xa2, 0x20, 0xbe, 0x4b, 0x4c, 0x40, 0xd7, 0xe2, 0xe8, 0xce, 0x1b, 0xf9, 0x17, 0xde, 0x1d, 0x41, - 0xb1, 0x86, 0x56, 0x96, 0x11, 0xfe, 0x03, 0x82, 0xd9, 0xb8, 0x09, 0x37, 0x5e, 0x47, 0x99, 0x5e, - 0xfd, 0x5e, 0x7a, 0xcf, 0x75, 0x0e, 0xf9, 0xeb, 0xfa, 0x85, 0x7c, 0x85, 0xaa, 0xfb, 0xe5, 0xd5, - 0x36, 0xb8, 0x96, 0xb3, 0x6e, 0xff, 0x1b, 0x04, 0x90, 0x36, 0x12, 0xf1, 0xd9, 0xe2, 0x73, 0x28, - 0xcd, 0x46, 0x7d, 0x6f, 0x5b, 0x89, 0x46, 0x83, 0x9f, 0x67, 0x59, 0x5f, 0x2a, 0xf4, 0x39, 0x9f, - 0x5a, 0x6b, 0x71, 0xd3, 0xf1, 0x97, 0x08, 0x66, 0x78, 0x9f, 0x08, 0x9f, 0x1c, 0x87, 0x59, 0x6d, - 0x23, 0xed, 0xa5, 0xea, 0x4f, 0x73, 0xa8, 0x4b, 0xad, 0xa2, 0xc0, 0xb5, 0x86, 0x56, 0x70, 0x1f, - 0x66, 0xe3, 0x9e, 0xcd, 0x78, 0xf3, 0xc8, 0xf4, 0x74, 0xf4, 0xa5, 0x82, 0x44, 0x1a, 0x1b, 0xaa, - 0x88, 0x99, 0x2b, 0x93, 0x62, 0xe6, 0x34, 0x0b, 0x6b, 0xf8, 0x44, 0x51, 0xd0, 0xfb, 0x0c, 0x14, - 0x73, 0x8e, 0xa3, 0x3b, 0x65, 0x2c, 0x4d, 0x8a, 0x9b, 0x4c, 0x3b, 0x3f, 0x47, 0x70, 0x70, 0xb8, - 0xe4, 0xc4, 0x47, 0x86, 0x62, 0xa6, 0x5a, 0x67, 0xeb, 0x59, 0x2d, 0x8e, 0x2b, 0x57, 0x8d, 0x6f, - 0x70, 0x14, 0x6b, 0xf8, 0xf2, 0x44, 0xcf, 0xb8, 0x2b, 0xa3, 0x0e, 0x63, 0xb4, 0x9a, 0x7e, 0xb3, - 0xf8, 0x2d, 0x82, 0x79, 0xc9, 0xf7, 0x7e, 0x40, 0x69, 0x31, 0xac, 0xbd, 0x73, 0x04, 0x26, 0xcb, - 0x78, 0x95, 0xc3, 0xff, 0x2a, 0xbe, 0x54, 0x12, 0xbe, 0x84, 0xbd, 0x1a, 0x31, 0xa4, 0x7f, 0x44, - 0x70, 0xe8, 0x61, 0x6c, 0xf7, 0x9f, 0x13, 0xfe, 0x6b, 0x1c, 0xff, 0xd7, 0xf0, 0x2b, 0x05, 0x75, - 0xd1, 0xa4, 0x63, 0x5c, 0x40, 0xf8, 0xd7, 0x08, 0xaa, 0xb2, 0x03, 0x8f, 0xcf, 0x8c, 0x75, 0x8c, - 0x6c, 0x8f, 0x7e, 0x2f, 0x8d, 0x59, 0x14, 0x01, 0xc6, 0xc9, 0xc2, 0x54, 0x2a, 0xe4, 0x33, 0x83, - 0x7e, 0x1f, 0x01, 0x4e, 0xde, 0x8b, 0xc9, 0x0b, 0x12, 0x9f, 0xce, 0x88, 0x1a, 0xdb, 0x60, 0xd0, - 0xcf, 0x4c, 0x5c, 0x97, 0x4d, 0xa5, 0x2b, 0x85, 0xa9, 0xd4, 0x4b, 0xe4, 0xbf, 0x83, 0xa0, 0x7e, - 0x93, 0x26, 0x35, 0x7b, 0x81, 0x2e, 0xb3, 0x9f, 0x16, 0xf4, 0xe5, 0xc9, 0x0b, 0x05, 0xa2, 0xf3, - 0x1c, 0xd1, 0x69, 0x5c, 0xac, 0x2a, 0x09, 0xe0, 0x43, 0x04, 0xfb, 0xee, 0xa9, 0x26, 0x8a, 0xcf, - 0x4f, 0x92, 0x94, 0x89, 0xe4, 0xe5, 0x71, 0x7d, 0x99, 0xe3, 0x5a, 0x35, 0x4a, 0xe1, 0x5a, 0x13, - 0xfd, 0xfb, 0x5f, 0xa0, 0xf8, 0x69, 0x37, 0xd4, 0x7d, 0xfd, 0x7f, 0xf5, 0x56, 0xd0, 0xc4, 0x35, - 0x2e, 0x71, 0x7c, 0x0d, 0x7c, 0xbe, 0x0c, 0xbe, 0xa6, 0x68, 0xc9, 0xe2, 0x0f, 0x10, 0x1c, 0xe2, - 0xfd, 0x6f, 0x95, 0xf1, 0x50, 0x8a, 0x19, 0xd7, 0x2d, 0x2f, 0x91, 0x62, 0x44, 0xfc, 0x31, 0x76, - 0x04, 0x6a, 0x4d, 0xf6, 0xb6, 0x7f, 0x86, 0x60, 0xbf, 0x4c, 0x6a, 0xe2, 0x76, 0x57, 0x27, 0x29, - 0x6e, 0xa7, 0x49, 0x50, 0x98, 0xdb, 0x4a, 0x39, 0x73, 0xfb, 0x04, 0xc1, 0x9c, 0xe8, 0x3d, 0x17, - 0x94, 0x0a, 0x4a, 0x73, 0x5a, 0x1f, 0x7a, 0xf9, 0x8b, 0xa6, 0xa6, 0xf1, 0x3d, 0x2e, 0xf6, 0x01, - 0x6e, 0x16, 0x89, 0xf5, 0xbd, 0x76, 0xd8, 0x7c, 0x22, 0x3a, 0x8a, 0x4f, 0x9b, 0x8e, 0xd7, 0x09, - 0xdf, 0x30, 0x70, 0x61, 0x42, 0x64, 0x6b, 0x2e, 0x20, 0x1c, 0x41, 0x8d, 0x19, 0x07, 0x6f, 0x27, - 0xe0, 0xa5, 0xa1, 0xe6, 0xc3, 0x48, 0xa7, 0x41, 0xd7, 0x47, 0xda, 0x13, 0x69, 0x06, 0x14, 0xcf, - 0x3e, 0x7c, 0xbc, 0x50, 0x2c, 0x17, 0xf4, 0x36, 0x82, 0x43, 0xaa, 0xb5, 0xc7, 0xe2, 0x4b, 0xdb, - 0x7a, 0x11, 0x0a, 0x51, 0x54, 0xe3, 0x95, 0x52, 0x86, 0xc4, 0xe1, 0x5c, 0xbd, 0xf1, 0xa7, 0x67, - 0xc7, 0xd0, 0x5f, 0x9f, 0x1d, 0x43, 0xff, 0x7a, 0x76, 0x0c, 0xbd, 0x71, 0xb9, 0xdc, 0x1f, 0x0f, - 0x2d, 0xc7, 0xa6, 0x6e, 0xa4, 0xb2, 0xff, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x83, 0x0c, 0x3f, - 0x61, 0x5e, 0x29, 0x00, 0x00, + 0x15, 0xa7, 0x66, 0xbf, 0x66, 0xde, 0xec, 0xfa, 0xa3, 0x12, 0x2f, 0x9d, 0xf6, 0xc6, 0x6c, 0xda, + 0x76, 0xbc, 0x59, 0x7b, 0x67, 0xec, 0xc1, 0x20, 0x67, 0x93, 0x08, 0xec, 0xf5, 0x27, 0xac, 0x1d, + 0xd3, 0x6b, 0x63, 0x14, 0x0e, 0x50, 0xe9, 0xae, 0x9d, 0x6d, 0xb6, 0xa7, 0xbb, 0xdd, 0xdd, 0x33, + 0xd6, 0xca, 0xf8, 0x12, 0x64, 0x09, 0xa1, 0x08, 0x04, 0xe4, 0x80, 0x10, 0x02, 0x14, 0x14, 0x09, + 0x21, 0x10, 0x17, 0x14, 0x21, 0x21, 0x24, 0xb8, 0x20, 0x38, 0x20, 0x21, 0x38, 0x72, 0x41, 0x16, + 0xe2, 0x08, 0x97, 0xfc, 0x01, 0xa8, 0xaa, 0xab, 0xba, 0xab, 0xe7, 0xa3, 0x67, 0x96, 0x19, 0x14, + 0xdf, 0xfa, 0xd5, 0x54, 0xbd, 0xf7, 0xab, 0x57, 0xbf, 0x7a, 0xaf, 0xea, 0xd5, 0xc0, 0x89, 0x88, + 0x86, 0x1d, 0x1a, 0xd6, 0x49, 0x10, 0xb8, 0x8e, 0x45, 0x62, 0xc7, 0xf7, 0xd4, 0xef, 0x5a, 0x10, + 0xfa, 0xb1, 0x8f, 0xab, 0x4a, 0x93, 0xbe, 0xd4, 0xf4, 0xfd, 0xa6, 0x4b, 0xeb, 0x24, 0x70, 0xea, + 0xc4, 0xf3, 0xfc, 0x98, 0x37, 0x47, 0x49, 0x57, 0xdd, 0xd8, 0xbd, 0x10, 0xd5, 0x1c, 0x9f, 0xff, + 0x6a, 0xf9, 0x21, 0xad, 0x77, 0xce, 0xd5, 0x9b, 0xd4, 0xa3, 0x21, 0x89, 0xa9, 0x2d, 0xfa, 0x9c, + 0xcf, 0xfa, 0xb4, 0x88, 0xb5, 0xe3, 0x78, 0x34, 0xdc, 0xab, 0x07, 0xbb, 0x4d, 0xd6, 0x10, 0xd5, + 0x5b, 0x34, 0x26, 0xfd, 0x46, 0x6d, 0x36, 0x9d, 0x78, 0xa7, 0xfd, 0x66, 0xcd, 0xf2, 0x5b, 0x75, + 0x12, 0x36, 0xfd, 0x20, 0xf4, 0xbf, 0xc2, 0x3f, 0xd6, 0x2c, 0xbb, 0xde, 0x69, 0x64, 0x0a, 0xd4, + 0xb9, 0x74, 0xce, 0x11, 0x37, 0xd8, 0x21, 0xbd, 0xda, 0xae, 0x0c, 0xd1, 0x16, 0xd2, 0xc0, 0x17, + 0xbe, 0xe1, 0x9f, 0x4e, 0xec, 0x87, 0x7b, 0xca, 0x67, 0xa2, 0xc6, 0xf8, 0x00, 0xc1, 0xa1, 0x8b, + 0x99, 0xbd, 0xcf, 0xb5, 0x69, 0xb8, 0x87, 0x31, 0x4c, 0x7b, 0xa4, 0x45, 0x35, 0xb4, 0x8c, 0x56, + 0x2a, 0x26, 0xff, 0xc6, 0x1a, 0xcc, 0x85, 0x74, 0x3b, 0xa4, 0xd1, 0x8e, 0x56, 0xe2, 0xcd, 0x52, + 0xc4, 0x3a, 0x94, 0x99, 0x71, 0x6a, 0xc5, 0x91, 0x36, 0xb5, 0x3c, 0xb5, 0x52, 0x31, 0x53, 0x19, + 0xaf, 0xc0, 0xc1, 0x90, 0x46, 0x7e, 0x3b, 0xb4, 0xe8, 0xe7, 0x69, 0x18, 0x39, 0xbe, 0xa7, 0x4d, + 0xf3, 0xd1, 0xdd, 0xcd, 0x4c, 0x4b, 0x44, 0x5d, 0x6a, 0xc5, 0x7e, 0xa8, 0xcd, 0xf0, 0x2e, 0xa9, + 0xcc, 0xf0, 0x30, 0xe0, 0xda, 0x6c, 0x82, 0x87, 0x7d, 0x63, 0x03, 0xe6, 0x49, 0x10, 0xdc, 0x22, + 0x2d, 0x1a, 0x05, 0xc4, 0xa2, 0xda, 0x1c, 0xff, 0x2d, 0xd7, 0xc6, 0x30, 0x0b, 0x24, 0x5a, 0x99, + 0x03, 0x93, 0xa2, 0xb1, 0x01, 0x95, 0x5b, 0xbe, 0x4d, 0x07, 0x4f, 0xb7, 0x5b, 0x7d, 0xa9, 0x57, + 0xbd, 0xf1, 0x18, 0xc1, 0x11, 0x93, 0x76, 0x1c, 0x86, 0xff, 0x26, 0x8d, 0x89, 0x4d, 0x62, 0xd2, + 0xad, 0xb1, 0x94, 0x6a, 0xd4, 0xa1, 0x1c, 0x8a, 0xce, 0x5a, 0x89, 0xb7, 0xa7, 0x72, 0x8f, 0xb5, + 0xa9, 0xe2, 0xc9, 0x24, 0x2e, 0x4c, 0x27, 0xf3, 0x2f, 0x04, 0xc7, 0x94, 0x35, 0x34, 0x85, 0x67, + 0xaf, 0x74, 0xa8, 0x17, 0x47, 0x83, 0x01, 0x9d, 0x81, 0xc3, 0x72, 0x11, 0xba, 0xe7, 0xd9, 0xfb, + 0x03, 0x83, 0xa8, 0x36, 0x4a, 0x88, 0x6a, 0x1b, 0x5e, 0x86, 0xaa, 0x94, 0xef, 0xde, 0xb8, 0x2c, + 0x60, 0xaa, 0x4d, 0x3d, 0x13, 0x9d, 0x29, 0x9e, 0xe8, 0x6c, 0x7e, 0xa2, 0x5f, 0x47, 0xa0, 0x29, + 0x13, 0xbd, 0x49, 0x3c, 0x67, 0x9b, 0x46, 0xf1, 0xa8, 0x3e, 0x47, 0x13, 0xf4, 0xf9, 0x0b, 0x50, + 0xb9, 0xea, 0xb8, 0x74, 0x63, 0xa7, 0xed, 0xed, 0xe2, 0x67, 0x61, 0xc6, 0x62, 0x1f, 0xdc, 0xf6, + 0xbc, 0x99, 0x08, 0xc6, 0xb7, 0x11, 0xbc, 0x30, 0x08, 0xed, 0x3d, 0x27, 0xde, 0x61, 0xe3, 0xa3, + 0x41, 0xb0, 0xad, 0x1d, 0x6a, 0xed, 0x46, 0xed, 0x96, 0xa4, 0x8a, 0x94, 0xc7, 0x84, 0xfd, 0x33, + 0x04, 0x2b, 0x43, 0x31, 0xdd, 0x0b, 0x49, 0x10, 0xd0, 0x10, 0x5f, 0x85, 0x99, 0xfb, 0xec, 0x07, + 0xbe, 0x31, 0xaa, 0x8d, 0x5a, 0x4d, 0x0d, 0xac, 0x43, 0xb5, 0x5c, 0xff, 0x88, 0x99, 0x0c, 0xc7, + 0x35, 0xe9, 0x9e, 0x12, 0xd7, 0xb3, 0x98, 0xd3, 0x93, 0x7a, 0x91, 0xf5, 0xe7, 0xdd, 0x2e, 0xcd, + 0xc2, 0x74, 0x40, 0xc2, 0xd8, 0x38, 0x02, 0xcf, 0xe4, 0x69, 0x1d, 0xf8, 0x5e, 0x44, 0x8d, 0xdf, + 0xe4, 0x59, 0xb0, 0x11, 0x52, 0x12, 0x53, 0x93, 0xde, 0x6f, 0xd3, 0x28, 0xc6, 0xbb, 0xa0, 0xc6, + 0x7a, 0xee, 0xd5, 0x6a, 0xe3, 0x46, 0x2d, 0x0b, 0x96, 0x35, 0x19, 0x2c, 0xf9, 0xc7, 0x97, 0x2c, + 0xbb, 0xd6, 0x69, 0xd4, 0x82, 0xdd, 0x66, 0x8d, 0x85, 0xde, 0x1c, 0x32, 0x19, 0x7a, 0xd5, 0xa9, + 0x9a, 0xaa, 0x76, 0xbc, 0x08, 0xb3, 0xed, 0x20, 0xa2, 0x61, 0xcc, 0x67, 0x56, 0x36, 0x85, 0xc4, + 0xd6, 0xaf, 0x43, 0x5c, 0xc7, 0x26, 0x71, 0xb2, 0x3e, 0x65, 0x33, 0x95, 0x8d, 0xdf, 0xe6, 0xd1, + 0xdf, 0x0d, 0xec, 0x0f, 0x0b, 0xbd, 0x8a, 0xb2, 0x94, 0x47, 0xa9, 0x32, 0x68, 0x2a, 0xcf, 0xa0, + 0x5f, 0xe5, 0xf1, 0x5f, 0xa6, 0x2e, 0xcd, 0xf0, 0xf7, 0x23, 0xb3, 0x06, 0x73, 0x16, 0x89, 0x2c, + 0x62, 0x4b, 0x2b, 0x52, 0x64, 0x01, 0x28, 0x08, 0xfd, 0x80, 0x34, 0xb9, 0xa6, 0xdb, 0xbe, 0xeb, + 0x58, 0x7b, 0xc2, 0x5c, 0xef, 0x0f, 0x3d, 0xc4, 0x9f, 0x2e, 0x26, 0xfe, 0x4c, 0x1e, 0xf6, 0x71, + 0xa8, 0x6e, 0xed, 0x79, 0xd6, 0xeb, 0x01, 0xcf, 0xf5, 0x6c, 0xc7, 0x3a, 0x31, 0x6d, 0x45, 0x1a, + 0xe2, 0x79, 0x21, 0x11, 0x8c, 0xf7, 0x67, 0x60, 0x51, 0x99, 0x1b, 0x1b, 0x50, 0x34, 0xb3, 0xa2, + 0xe8, 0xb2, 0x08, 0xb3, 0x76, 0xb8, 0x67, 0xb6, 0x3d, 0x41, 0x00, 0x21, 0x31, 0xc3, 0x41, 0xd8, + 0xf6, 0x12, 0xf8, 0x65, 0x33, 0x11, 0xf0, 0x36, 0x94, 0xa3, 0x98, 0x65, 0xf7, 0xe6, 0x1e, 0x07, + 0x5e, 0x6d, 0x7c, 0x66, 0xbc, 0x45, 0x67, 0xd0, 0xb7, 0x84, 0x46, 0x33, 0xd5, 0x8d, 0xef, 0x43, + 0x45, 0x46, 0xe3, 0x48, 0x9b, 0x5b, 0x9e, 0x5a, 0xa9, 0x36, 0xb6, 0xc6, 0x37, 0xf4, 0x7a, 0xc0, + 0x4e, 0x26, 0x4a, 0xe6, 0x31, 0x33, 0x2b, 0x78, 0x09, 0x2a, 0x2d, 0x11, 0x1f, 0x22, 0x91, 0x85, + 0xb3, 0x06, 0xfc, 0x05, 0x98, 0x71, 0xbc, 0x6d, 0x3f, 0xd2, 0x2a, 0x1c, 0xcc, 0xa5, 0xf1, 0xc0, + 0xdc, 0xf0, 0xb6, 0x7d, 0x33, 0x51, 0x88, 0xef, 0xc3, 0x42, 0x48, 0xe3, 0x70, 0x4f, 0x7a, 0x41, + 0x03, 0xee, 0xd7, 0xcf, 0x8e, 0x67, 0xc1, 0x54, 0x55, 0x9a, 0x79, 0x0b, 0x78, 0x1d, 0xaa, 0x51, + 0xc6, 0x31, 0xad, 0xca, 0x0d, 0x6a, 0x39, 0x45, 0x0a, 0x07, 0x4d, 0xb5, 0x73, 0x0f, 0xbb, 0xe7, + 0x8b, 0xd9, 0xbd, 0x90, 0x67, 0xf7, 0x7f, 0x10, 0x2c, 0xf5, 0x04, 0x95, 0xad, 0x80, 0x16, 0xd2, + 0x97, 0xc0, 0x74, 0x14, 0x50, 0x8b, 0x67, 0x98, 0x6a, 0xe3, 0xe6, 0xc4, 0xa2, 0x0c, 0xb7, 0xcb, + 0x55, 0x17, 0x05, 0xc2, 0x31, 0xf7, 0xf3, 0x8f, 0x10, 0x7c, 0x54, 0xb1, 0x79, 0x9b, 0xc4, 0xd6, + 0x4e, 0xd1, 0x64, 0xd9, 0xbe, 0x63, 0x7d, 0x44, 0x3e, 0x4d, 0x04, 0x46, 0x4e, 0xfe, 0x71, 0x67, + 0x2f, 0x60, 0x00, 0xd9, 0x2f, 0x59, 0xc3, 0x98, 0x87, 0x95, 0x9f, 0x23, 0xd0, 0xd5, 0xd8, 0xeb, + 0xbb, 0xee, 0x9b, 0xc4, 0xda, 0x2d, 0x02, 0x79, 0x00, 0x4a, 0x8e, 0xcd, 0x11, 0x4e, 0x99, 0x25, + 0xc7, 0xde, 0x67, 0x10, 0xe9, 0x86, 0x3b, 0x5b, 0x0c, 0x77, 0x2e, 0x0f, 0xf7, 0x83, 0x2e, 0xb8, + 0x72, 0x2b, 0x17, 0xc0, 0x5d, 0x82, 0x8a, 0xd7, 0x75, 0x70, 0xcc, 0x1a, 0xfa, 0x1c, 0x18, 0x4b, + 0x3d, 0x07, 0x46, 0x0d, 0xe6, 0x3a, 0xe9, 0xb5, 0x80, 0xfd, 0x2c, 0x45, 0x36, 0xc5, 0x66, 0xe8, + 0xb7, 0x03, 0xe1, 0xf4, 0x44, 0x60, 0x28, 0x76, 0x1d, 0xcf, 0xd6, 0x66, 0x13, 0x14, 0xec, 0x7b, + 0xff, 0x17, 0x81, 0xdc, 0xb4, 0x7f, 0x51, 0x82, 0x8f, 0xf5, 0x99, 0xf6, 0x50, 0x3e, 0x3d, 0x1d, + 0x73, 0x4f, 0x59, 0x3d, 0x37, 0x90, 0xd5, 0xe5, 0x61, 0xac, 0xae, 0x14, 0xfb, 0x0b, 0xf2, 0xfe, + 0xfa, 0x69, 0x09, 0x96, 0xfb, 0xf8, 0x6b, 0xf8, 0x31, 0xe0, 0xa9, 0x71, 0xd8, 0xb6, 0x1f, 0x0a, + 0x96, 0x94, 0xcd, 0x44, 0x60, 0xfb, 0xcc, 0x0f, 0x83, 0x1d, 0xe2, 0x71, 0x76, 0x94, 0x4d, 0x21, + 0x8d, 0xe9, 0xaa, 0x6f, 0x94, 0x40, 0x93, 0xfe, 0xb9, 0x68, 0x71, 0x6f, 0xb5, 0xbd, 0xa7, 0xdf, + 0x45, 0x8b, 0x30, 0x4b, 0x38, 0x5a, 0x41, 0x2a, 0x21, 0xf5, 0x38, 0xa3, 0x5c, 0xec, 0x8c, 0x4a, + 0xde, 0x19, 0x8f, 0x11, 0x1c, 0xcd, 0x3b, 0x23, 0xda, 0x74, 0xa2, 0x58, 0x1e, 0xea, 0xf1, 0x36, + 0xcc, 0x25, 0x76, 0x92, 0x23, 0x59, 0xb5, 0xb1, 0x39, 0x6e, 0xa2, 0xce, 0x39, 0x5e, 0x2a, 0x37, + 0x5e, 0x86, 0xa3, 0x7d, 0xa3, 0x9c, 0x80, 0xa1, 0x43, 0x59, 0x1e, 0x4e, 0xc4, 0xd2, 0xa4, 0xb2, + 0xf1, 0x78, 0x3a, 0x9f, 0x72, 0x7c, 0x7b, 0xd3, 0x6f, 0x16, 0xdc, 0xaf, 0x8b, 0x97, 0x93, 0xb9, + 0xca, 0xb7, 0x95, 0xab, 0xb4, 0x14, 0xd9, 0x38, 0xcb, 0xf7, 0x62, 0xe2, 0x78, 0x34, 0x14, 0x59, + 0x31, 0x6b, 0x60, 0xcb, 0x10, 0x39, 0x9e, 0x45, 0xb7, 0xa8, 0xe5, 0x7b, 0x76, 0xc4, 0xd7, 0x73, + 0xca, 0xcc, 0xb5, 0xe1, 0xeb, 0x50, 0xe1, 0xf2, 0x1d, 0xa7, 0x95, 0xa4, 0x81, 0x6a, 0x63, 0xb5, + 0x96, 0xd4, 0xac, 0x6a, 0x6a, 0xcd, 0x2a, 0xf3, 0x61, 0x8b, 0xc6, 0xa4, 0xd6, 0x39, 0x57, 0x63, + 0x23, 0xcc, 0x6c, 0x30, 0xc3, 0x12, 0x13, 0xc7, 0xdd, 0x74, 0x3c, 0x7e, 0x60, 0x64, 0xa6, 0xb2, + 0x06, 0x46, 0x95, 0x6d, 0xdf, 0x75, 0xfd, 0x07, 0x72, 0xdf, 0x24, 0x12, 0x1b, 0xd5, 0xf6, 0x62, + 0xc7, 0xe5, 0xf6, 0x13, 0x22, 0x64, 0x0d, 0x7c, 0x94, 0xe3, 0xc6, 0x34, 0x14, 0x1b, 0x46, 0x48, + 0x29, 0x19, 0xab, 0x49, 0x19, 0x46, 0xee, 0xd7, 0x84, 0xb6, 0xf3, 0x2a, 0x6d, 0xbb, 0xb7, 0xc2, + 0x42, 0x9f, 0x5a, 0x04, 0xaf, 0x4a, 0xd1, 0x8e, 0xe3, 0xb7, 0x23, 0xed, 0x40, 0x72, 0xf4, 0x90, + 0x72, 0x0f, 0x95, 0x0f, 0x16, 0x53, 0xf9, 0x50, 0x9e, 0xca, 0xbf, 0x43, 0x50, 0xde, 0xf4, 0x9b, + 0x57, 0xbc, 0x38, 0xdc, 0xe3, 0xb7, 0x1b, 0xdf, 0x8b, 0xa9, 0x27, 0xf9, 0x22, 0x45, 0xb6, 0x08, + 0xb1, 0xd3, 0xa2, 0x5b, 0x31, 0x69, 0x05, 0xe2, 0x8c, 0xb5, 0xaf, 0x45, 0x48, 0x07, 0x33, 0xc7, + 0xb8, 0x24, 0x8a, 0xf9, 0x8e, 0x2f, 0x9b, 0xfc, 0x9b, 0x4d, 0x21, 0xed, 0xb0, 0x15, 0x87, 0x62, + 0xbb, 0xe7, 0xda, 0x54, 0x8a, 0xcd, 0x24, 0xd8, 0x84, 0x68, 0xb4, 0xe0, 0xb9, 0xf4, 0xd0, 0x7e, + 0x87, 0x86, 0x2d, 0xc7, 0x23, 0xc5, 0xd1, 0x7b, 0x84, 0x72, 0x58, 0xc1, 0x9d, 0xd1, 0xcf, 0x6d, + 0x3a, 0x76, 0x06, 0xbe, 0xe7, 0x78, 0xb6, 0xff, 0xa0, 0x60, 0xf3, 0x8c, 0x67, 0xf0, 0xaf, 0xf9, + 0x8a, 0x98, 0x62, 0x31, 0xdd, 0xe9, 0xd7, 0x61, 0x81, 0xc5, 0x84, 0x0e, 0x15, 0x3f, 0x88, 0xb0, + 0x63, 0x0c, 0x2a, 0x72, 0x64, 0x3a, 0xcc, 0xfc, 0x40, 0xbc, 0x09, 0x07, 0x49, 0x14, 0x39, 0x4d, + 0x8f, 0xda, 0x52, 0x57, 0x69, 0x64, 0x5d, 0xdd, 0x43, 0x93, 0xeb, 0x32, 0xef, 0x21, 0xd6, 0x5b, + 0x8a, 0xc6, 0xd7, 0x10, 0x1c, 0xe9, 0xab, 0x24, 0xdd, 0x39, 0x48, 0x09, 0xe3, 0x3a, 0x94, 0x23, + 0x6b, 0x87, 0xda, 0x6d, 0x97, 0xca, 0x1a, 0x92, 0x94, 0xd9, 0x6f, 0x76, 0x3b, 0x59, 0x7d, 0x91, + 0x46, 0x52, 0x19, 0x1f, 0x03, 0x68, 0x11, 0xaf, 0x4d, 0x5c, 0x0e, 0x61, 0x9a, 0x43, 0x50, 0x5a, + 0x8c, 0x25, 0xd0, 0xfb, 0x51, 0x47, 0xd4, 0x66, 0xfe, 0x8d, 0xe0, 0x80, 0x0c, 0xaa, 0x62, 0x75, + 0x57, 0xe0, 0xa0, 0xe2, 0x86, 0x5b, 0xd9, 0x42, 0x77, 0x37, 0x0f, 0x09, 0x98, 0x92, 0x25, 0x53, + 0xf9, 0xa2, 0x74, 0x27, 0x57, 0x56, 0x1e, 0x39, 0xdf, 0xa1, 0x09, 0x9d, 0x1f, 0xbf, 0x0a, 0xda, + 0x4d, 0xe2, 0x91, 0x26, 0xb5, 0xd3, 0x69, 0xa7, 0x14, 0xfb, 0xb2, 0x5a, 0x64, 0x18, 0xfb, 0x4a, + 0x9f, 0x1e, 0xb5, 0x9c, 0xed, 0x6d, 0x59, 0xb0, 0x08, 0xa1, 0xbc, 0xe9, 0x78, 0xbb, 0xec, 0xde, + 0xcb, 0x66, 0x1c, 0x3b, 0xb1, 0x2b, 0xbd, 0x9b, 0x08, 0xf8, 0x10, 0x4c, 0xb5, 0x43, 0x57, 0x30, + 0x80, 0x7d, 0xe2, 0x65, 0xa8, 0xda, 0x34, 0xb2, 0x42, 0x27, 0x10, 0xeb, 0xcf, 0x8b, 0xb4, 0x4a, + 0x13, 0x5b, 0x07, 0xc7, 0xf2, 0xbd, 0x0d, 0x97, 0x44, 0x91, 0x4c, 0x40, 0x69, 0x83, 0xf1, 0x2a, + 0x2c, 0x30, 0x9b, 0xd9, 0x34, 0x4f, 0xe7, 0xa7, 0x79, 0x24, 0x07, 0x5f, 0xc2, 0x93, 0x88, 0x09, + 0x3c, 0xc3, 0xf2, 0xfe, 0xc5, 0x20, 0x10, 0x4a, 0x46, 0x3c, 0x0e, 0x4d, 0xf5, 0xcb, 0x9f, 0x7d, + 0x6b, 0x9c, 0x8d, 0xbf, 0x1f, 0x07, 0xac, 0xee, 0x13, 0x1a, 0x76, 0x1c, 0x8b, 0xe2, 0xef, 0x20, + 0x98, 0x66, 0xa6, 0xf1, 0xf3, 0x83, 0xb6, 0x25, 0xe7, 0xab, 0x3e, 0xb9, 0x8b, 0x30, 0xb3, 0x66, + 0x2c, 0xbd, 0xf5, 0xb7, 0x7f, 0x7e, 0xb7, 0xb4, 0x88, 0x9f, 0xe5, 0x2f, 0x4a, 0x9d, 0x73, 0xea, + 0xeb, 0x4e, 0x84, 0xdf, 0x46, 0x80, 0xc5, 0x39, 0x48, 0xa9, 0xd9, 0xe3, 0xd3, 0x83, 0x20, 0xf6, + 0xa9, 0xed, 0xeb, 0xcf, 0x2b, 0x59, 0xa5, 0x66, 0xf9, 0x21, 0x65, 0x39, 0x84, 0x77, 0xe0, 0x00, + 0x56, 0x39, 0x80, 0x13, 0xd8, 0xe8, 0x07, 0xa0, 0xfe, 0x90, 0x79, 0xf4, 0x51, 0x9d, 0x26, 0x76, + 0xdf, 0x45, 0x30, 0x73, 0x8f, 0xdf, 0x21, 0x86, 0x38, 0x69, 0x6b, 0x62, 0x4e, 0xe2, 0xe6, 0x38, + 0x5a, 0xe3, 0x38, 0x47, 0xfa, 0x3c, 0x3e, 0x2a, 0x91, 0x46, 0x71, 0x48, 0x49, 0x2b, 0x07, 0xf8, + 0x2c, 0xc2, 0xef, 0x21, 0x98, 0x4d, 0x8a, 0xbe, 0xf8, 0xe4, 0x20, 0x94, 0xb9, 0xa2, 0xb0, 0x3e, + 0xb9, 0x0a, 0xaa, 0xf1, 0x12, 0xc7, 0x78, 0xdc, 0xe8, 0xbb, 0x9c, 0xeb, 0xb9, 0xfa, 0xea, 0x3b, + 0x08, 0xa6, 0xae, 0xd1, 0xa1, 0x7c, 0x9b, 0x20, 0xb8, 0x1e, 0x07, 0xf6, 0x59, 0x6a, 0xfc, 0x13, + 0x04, 0xcf, 0x5d, 0xa3, 0x71, 0xff, 0xf4, 0x88, 0x57, 0x86, 0xe7, 0x2c, 0x41, 0xbb, 0xd3, 0x23, + 0xf4, 0x4c, 0xf3, 0x42, 0x9d, 0x23, 0x7b, 0x09, 0x9f, 0x2a, 0x22, 0x61, 0xb4, 0xe7, 0x59, 0x0f, + 0x04, 0x8e, 0x3f, 0x21, 0x38, 0xd4, 0xfd, 0xb6, 0x86, 0xf3, 0x09, 0xb5, 0xef, 0xd3, 0x9b, 0x7e, + 0x6b, 0xdc, 0x28, 0x9b, 0x57, 0x6a, 0x5c, 0xe4, 0xc8, 0x5f, 0xc1, 0x2f, 0x17, 0x21, 0x97, 0x65, + 0xdf, 0xa8, 0xfe, 0x50, 0x7e, 0x3e, 0xe2, 0xef, 0xc0, 0x1c, 0xf6, 0x9f, 0x11, 0x3c, 0x2b, 0xf5, + 0x6e, 0xec, 0x90, 0x30, 0xbe, 0x4c, 0xd9, 0x19, 0x3a, 0x1a, 0x69, 0x3e, 0x63, 0x66, 0x0d, 0xd5, + 0x9e, 0x71, 0x85, 0xcf, 0xe5, 0x53, 0xf8, 0xb5, 0x7d, 0xcf, 0xc5, 0x62, 0x6a, 0x6c, 0x01, 0xfb, + 0x2d, 0x04, 0xf3, 0xd7, 0x68, 0x7c, 0x33, 0xad, 0xe2, 0x9e, 0x1c, 0xe9, 0x65, 0x48, 0x5f, 0xaa, + 0x29, 0xcf, 0xcf, 0xf2, 0xa7, 0x94, 0x22, 0x6b, 0x1c, 0xdc, 0x29, 0x7c, 0xb2, 0x08, 0x5c, 0x56, + 0x39, 0x7e, 0x17, 0xc1, 0x11, 0x15, 0x44, 0xf6, 0xa2, 0xf6, 0x89, 0xfd, 0xbd, 0x53, 0x89, 0xd7, + 0xae, 0x21, 0xe8, 0x1a, 0x1c, 0xdd, 0x19, 0xa3, 0x3f, 0x81, 0x5b, 0x3d, 0x28, 0xd6, 0xd1, 0xea, + 0x0a, 0xc2, 0xbf, 0x47, 0x30, 0x9b, 0x14, 0x63, 0x07, 0xfb, 0x28, 0xf7, 0x02, 0x34, 0xc9, 0x68, + 0x20, 0x56, 0x5b, 0x3f, 0xdb, 0xdf, 0xa1, 0xea, 0x78, 0x49, 0xd5, 0x1a, 0xf7, 0x72, 0x3e, 0x8c, + 0xbd, 0x8f, 0x00, 0xb2, 0x82, 0x32, 0x7e, 0xa9, 0x78, 0x1e, 0x4a, 0xd1, 0x59, 0x9f, 0x6c, 0x49, + 0xd9, 0xa8, 0xf1, 0xf9, 0xac, 0xe8, 0xcb, 0x85, 0x31, 0x24, 0xa0, 0xd6, 0x7a, 0x52, 0x7c, 0xfe, + 0x31, 0x82, 0x19, 0x5e, 0xc7, 0xc3, 0x27, 0x06, 0x61, 0x56, 0xcb, 0x7c, 0x93, 0x74, 0xfd, 0x8b, + 0x1c, 0xea, 0x72, 0xa3, 0x28, 0x10, 0xaf, 0xa3, 0x55, 0xdc, 0x81, 0xd9, 0xa4, 0x72, 0x36, 0x98, + 0x1e, 0xb9, 0xca, 0x9a, 0xbe, 0x5c, 0x70, 0x30, 0x48, 0x88, 0x2a, 0x72, 0xc0, 0xea, 0xb0, 0x1c, + 0x30, 0xcd, 0xc2, 0x34, 0x3e, 0x5e, 0x14, 0xc4, 0xff, 0x0f, 0x8e, 0x39, 0xcd, 0xd1, 0x9d, 0x34, + 0x96, 0x87, 0xe5, 0x01, 0xe6, 0x9d, 0xef, 0x21, 0x38, 0xd4, 0x7d, 0xb8, 0xc6, 0x47, 0xbb, 0x62, + 0xa6, 0x7a, 0xd7, 0xd0, 0xf3, 0x5e, 0x1c, 0x74, 0x30, 0x37, 0x3e, 0xcd, 0x51, 0xac, 0xe3, 0x0b, + 0x43, 0x77, 0xc6, 0x2d, 0x19, 0x75, 0x98, 0xa2, 0xb5, 0xec, 0x55, 0xeb, 0xd7, 0x08, 0xe6, 0xa5, + 0xde, 0x3b, 0x21, 0xa5, 0xc5, 0xb0, 0x26, 0xb7, 0x11, 0x98, 0x2d, 0xe3, 0x55, 0x0e, 0xff, 0x93, + 0xf8, 0xfc, 0x88, 0xf0, 0x25, 0xec, 0xb5, 0x98, 0x21, 0xfd, 0x03, 0x82, 0xc3, 0xf7, 0x12, 0xde, + 0x7f, 0x48, 0xf8, 0x37, 0x38, 0xfe, 0xd7, 0xf0, 0x2b, 0x05, 0xe7, 0xbc, 0x61, 0xd3, 0x38, 0x8b, + 0xf0, 0x2f, 0x11, 0x94, 0xe5, 0xab, 0x0a, 0x3e, 0x35, 0x70, 0x63, 0xe4, 0xdf, 0x5d, 0x26, 0x49, + 0x66, 0x71, 0xa8, 0x31, 0x4e, 0x14, 0xa6, 0x53, 0x61, 0x9f, 0x11, 0xfa, 0x1d, 0x04, 0x38, 0xbd, + 0x33, 0xa7, 0xb7, 0x68, 0xfc, 0x62, 0xce, 0xd4, 0xc0, 0xc2, 0x8c, 0x7e, 0x6a, 0x68, 0xbf, 0x7c, + 0x2a, 0x5d, 0x2d, 0x4c, 0xa5, 0x7e, 0x6a, 0xff, 0x9b, 0x08, 0xaa, 0xd7, 0x68, 0x7a, 0x07, 0x29, + 0xf0, 0x65, 0xfe, 0x51, 0x48, 0x5f, 0x19, 0xde, 0x51, 0x20, 0x3a, 0xc3, 0x11, 0xbd, 0x88, 0x8b, + 0x5d, 0x25, 0x01, 0xfc, 0x00, 0xc1, 0xc2, 0x6d, 0x95, 0xa2, 0xf8, 0xcc, 0x30, 0x4b, 0xb9, 0x48, + 0x3e, 0x3a, 0xae, 0x8f, 0x73, 0x5c, 0x6b, 0xc6, 0x48, 0xb8, 0xd6, 0xc5, 0xfb, 0xca, 0x0f, 0x51, + 0x72, 0x89, 0xed, 0xaa, 0x67, 0xff, 0xaf, 0x7e, 0x2b, 0x28, 0x8b, 0x1b, 0xe7, 0x39, 0xbe, 0x1a, + 0x3e, 0x33, 0x0a, 0xbe, 0xba, 0x28, 0x72, 0xe3, 0xef, 0x23, 0x38, 0xcc, 0xdf, 0x1a, 0x54, 0xc5, + 0x5d, 0x29, 0x66, 0xd0, 0xcb, 0xc4, 0x08, 0x29, 0x46, 0xc4, 0x1f, 0x63, 0x5f, 0xa0, 0xd6, 0xe5, + 0x3b, 0xc2, 0xb7, 0x10, 0x1c, 0x90, 0x49, 0x4d, 0xac, 0xee, 0xda, 0x30, 0xc7, 0xed, 0x37, 0x09, + 0x0a, 0xba, 0xad, 0x8e, 0x46, 0xb7, 0xf7, 0x10, 0xcc, 0x89, 0x6a, 0x7e, 0xc1, 0x51, 0x41, 0x29, + 0xf7, 0xeb, 0x5d, 0x35, 0x0e, 0x51, 0x0c, 0x36, 0xbe, 0xc8, 0xcd, 0xde, 0xc5, 0xf5, 0x22, 0xb3, + 0x81, 0x6f, 0x47, 0xf5, 0x87, 0xa2, 0x12, 0xfb, 0xa8, 0xee, 0xfa, 0xcd, 0xe8, 0x0d, 0x03, 0x17, + 0x26, 0x44, 0xd6, 0xe7, 0x2c, 0xc2, 0x31, 0x54, 0x18, 0x39, 0x78, 0xe1, 0x04, 0x2f, 0x77, 0x95, + 0x59, 0x7a, 0x6a, 0x2a, 0xba, 0xde, 0x53, 0x88, 0xc9, 0x32, 0xa0, 0xb8, 0xc6, 0xe2, 0x17, 0x0a, + 0xcd, 0x72, 0x43, 0x6f, 0x23, 0x38, 0xac, 0xb2, 0x3d, 0x31, 0x3f, 0x32, 0xd7, 0x8b, 0x50, 0x88, + 0x43, 0x35, 0x5e, 0x1d, 0x89, 0x48, 0x1c, 0xce, 0xa5, 0xab, 0x7f, 0x7c, 0x72, 0x0c, 0xfd, 0xe5, + 0xc9, 0x31, 0xf4, 0x8f, 0x27, 0xc7, 0xd0, 0x1b, 0x17, 0x46, 0xfb, 0x4f, 0xad, 0xe5, 0x3a, 0xd4, + 0x8b, 0x55, 0xf5, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x30, 0xc0, 0x40, 0x7a, 0x39, 0x2c, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2818,6 +2989,8 @@ type ApplicationServiceClient interface { GetApplicationSyncWindows(ctx context.Context, in *ApplicationSyncWindowsQuery, opts ...grpc.CallOption) (*ApplicationSyncWindowsResponse, error) // Get the meta-data (author, date, tags, message) for a specific revision of the application RevisionMetadata(ctx context.Context, in *RevisionMetadataQuery, opts ...grpc.CallOption) (*v1alpha1.RevisionMetadata, error) + // Get the chart metadata (description, maintainers, home) for a specific revision of the application + RevisionChartDetails(ctx context.Context, in *RevisionMetadataQuery, opts ...grpc.CallOption) (*v1alpha1.ChartDetails, error) // GetManifests returns application manifests GetManifests(ctx context.Context, in *ApplicationManifestQuery, opts ...grpc.CallOption) (*apiclient.ManifestResponse, error) // GetManifestsWithFiles returns application manifests using provided files to generate them @@ -2954,6 +3127,15 @@ func (c *applicationServiceClient) RevisionMetadata(ctx context.Context, in *Rev return out, nil } +func (c *applicationServiceClient) RevisionChartDetails(ctx context.Context, in *RevisionMetadataQuery, opts ...grpc.CallOption) (*v1alpha1.ChartDetails, error) { + out := new(v1alpha1.ChartDetails) + err := c.cc.Invoke(ctx, "/application.ApplicationService/RevisionChartDetails", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *applicationServiceClient) GetManifests(ctx context.Context, in *ApplicationManifestQuery, opts ...grpc.CallOption) (*apiclient.ManifestResponse, error) { out := new(apiclient.ManifestResponse) err := c.cc.Invoke(ctx, "/application.ApplicationService/GetManifests", in, out, opts...) @@ -3221,6 +3403,8 @@ type ApplicationServiceServer interface { GetApplicationSyncWindows(context.Context, *ApplicationSyncWindowsQuery) (*ApplicationSyncWindowsResponse, error) // Get the meta-data (author, date, tags, message) for a specific revision of the application RevisionMetadata(context.Context, *RevisionMetadataQuery) (*v1alpha1.RevisionMetadata, error) + // Get the chart metadata (description, maintainers, home) for a specific revision of the application + RevisionChartDetails(context.Context, *RevisionMetadataQuery) (*v1alpha1.ChartDetails, error) // GetManifests returns application manifests GetManifests(context.Context, *ApplicationManifestQuery) (*apiclient.ManifestResponse, error) // GetManifestsWithFiles returns application manifests using provided files to generate them @@ -3288,6 +3472,9 @@ func (*UnimplementedApplicationServiceServer) GetApplicationSyncWindows(ctx cont func (*UnimplementedApplicationServiceServer) RevisionMetadata(ctx context.Context, req *RevisionMetadataQuery) (*v1alpha1.RevisionMetadata, error) { return nil, status.Errorf(codes.Unimplemented, "method RevisionMetadata not implemented") } +func (*UnimplementedApplicationServiceServer) RevisionChartDetails(ctx context.Context, req *RevisionMetadataQuery) (*v1alpha1.ChartDetails, error) { + return nil, status.Errorf(codes.Unimplemented, "method RevisionChartDetails not implemented") +} func (*UnimplementedApplicationServiceServer) GetManifests(ctx context.Context, req *ApplicationManifestQuery) (*apiclient.ManifestResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetManifests not implemented") } @@ -3482,6 +3669,24 @@ func _ApplicationService_RevisionMetadata_Handler(srv interface{}, ctx context.C return interceptor(ctx, in, info, handler) } +func _ApplicationService_RevisionChartDetails_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RevisionMetadataQuery) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApplicationServiceServer).RevisionChartDetails(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/application.ApplicationService/RevisionChartDetails", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApplicationServiceServer).RevisionChartDetails(ctx, req.(*RevisionMetadataQuery)) + } + return interceptor(ctx, in, info, handler) +} + func _ApplicationService_GetManifests_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ApplicationManifestQuery) if err := dec(in); err != nil { @@ -3884,6 +4089,10 @@ var _ApplicationService_serviceDesc = grpc.ServiceDesc{ MethodName: "RevisionMetadata", Handler: _ApplicationService_RevisionMetadata_Handler, }, + { + MethodName: "RevisionChartDetails", + Handler: _ApplicationService_RevisionChartDetails_Handler, + }, { MethodName: "GetManifests", Handler: _ApplicationService_GetManifests_Handler, @@ -4002,6 +4211,15 @@ func (m *ApplicationQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.Project) > 0 { + for iNdEx := len(m.Project) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Project[iNdEx]) + copy(dAtA[i:], m.Project[iNdEx]) + i = encodeVarintApplication(dAtA, i, uint64(len(m.Project[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -4121,6 +4339,13 @@ func (m *RevisionMetadataQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x22 + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -4173,6 +4398,13 @@ func (m *ApplicationResourceEventsQuery) MarshalToSizedBuffer(dAtA []byte) (int, i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x32 + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -4237,6 +4469,13 @@ func (m *ApplicationManifestQuery) MarshalToSizedBuffer(dAtA []byte) (int, error i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x22 + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -4323,6 +4562,13 @@ func (m *ApplicationManifestQueryWithFiles) MarshalToSizedBuffer(dAtA []byte) (i i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x22 + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -4541,6 +4787,13 @@ func (m *ApplicationUpdateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x1a + } if m.Validate != nil { i-- if *m.Validate { @@ -4592,6 +4845,13 @@ func (m *ApplicationDeleteRequest) MarshalToSizedBuffer(dAtA []byte) (int, error i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x2a + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -4688,6 +4948,13 @@ func (m *ApplicationSyncRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x6a + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -4831,6 +5098,13 @@ func (m *ApplicationUpdateSpecRequest) MarshalToSizedBuffer(dAtA []byte) (int, e i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x2a + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -4898,6 +5172,13 @@ func (m *ApplicationPatchRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x32 + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -4959,6 +5240,13 @@ func (m *ApplicationRollbackRequest) MarshalToSizedBuffer(dAtA []byte) (int, err i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x3a + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -5029,6 +5317,13 @@ func (m *ApplicationResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, err i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x42 + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -5113,6 +5408,13 @@ func (m *ApplicationResourcePatchRequest) MarshalToSizedBuffer(dAtA []byte) (int i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x52 + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -5215,6 +5517,13 @@ func (m *ApplicationResourceDeleteRequest) MarshalToSizedBuffer(dAtA []byte) (in i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x52 + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -5319,6 +5628,13 @@ func (m *ResourceActionRunRequest) MarshalToSizedBuffer(dAtA []byte) (int, error i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x4a + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -5489,6 +5805,15 @@ func (m *ApplicationPodLogsQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x82 + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -5710,6 +6035,13 @@ func (m *OperationTerminateRequest) MarshalToSizedBuffer(dAtA []byte) (int, erro i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x1a + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -5753,6 +6085,13 @@ func (m *ApplicationSyncWindowsQuery) MarshalToSizedBuffer(dAtA []byte) (int, er i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x1a + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -5956,6 +6295,13 @@ func (m *ResourcesQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x42 + } if m.AppNamespace != nil { i -= len(*m.AppNamespace) copy(dAtA[i:], *m.AppNamespace) @@ -6175,6 +6521,13 @@ func (m *ListAppLinksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Project != nil { + i -= len(*m.Project) + copy(dAtA[i:], *m.Project) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.Project))) + i-- + dAtA[i] = 0x22 + } if m.Namespace != nil { i -= len(*m.Namespace) copy(dAtA[i:], *m.Namespace) @@ -6241,6 +6594,12 @@ func (m *ApplicationQuery) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if len(m.Project) > 0 { + for _, s := range m.Project { + l = len(s) + n += 1 + l + sovApplication(uint64(l)) + } + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6285,6 +6644,10 @@ func (m *RevisionMetadataQuery) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6317,6 +6680,10 @@ func (m *ApplicationResourceEventsQuery) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6341,6 +6708,10 @@ func (m *ApplicationManifestQuery) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6381,6 +6752,10 @@ func (m *ApplicationManifestQueryWithFiles) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6473,6 +6848,10 @@ func (m *ApplicationUpdateRequest) Size() (n int) { if m.Validate != nil { n += 2 } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6500,6 +6879,10 @@ func (m *ApplicationDeleteRequest) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6578,6 +6961,10 @@ func (m *ApplicationSyncRequest) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6605,6 +6992,10 @@ func (m *ApplicationUpdateSpecRequest) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6633,6 +7024,10 @@ func (m *ApplicationPatchRequest) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6662,6 +7057,10 @@ func (m *ApplicationRollbackRequest) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6702,6 +7101,10 @@ func (m *ApplicationResourceRequest) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6750,6 +7153,10 @@ func (m *ApplicationResourcePatchRequest) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6796,6 +7203,10 @@ func (m *ApplicationResourceDeleteRequest) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6840,6 +7251,10 @@ func (m *ResourceActionRunRequest) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6942,6 +7357,10 @@ func (m *ApplicationPodLogsQuery) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 2 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6993,6 +7412,10 @@ func (m *OperationTerminateRequest) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -7013,6 +7436,10 @@ func (m *ApplicationSyncWindowsQuery) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -7119,6 +7546,10 @@ func (m *ResourcesQuery) Size() (n int) { l = len(*m.AppNamespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -7203,6 +7634,10 @@ func (m *ListAppLinksRequest) Size() (n int) { l = len(*m.Namespace) n += 1 + l + sovApplication(uint64(l)) } + if m.Project != nil { + l = len(*m.Project) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -7474,6 +7909,38 @@ func (m *ApplicationQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Project = append(m.Project, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -7744,24 +8211,57 @@ func (m *RevisionMetadataQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipApplication(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthApplication - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if hasFields[0]&uint64(0x00000001) == 0 { - return github_com_gogo_protobuf_proto.NewRequiredNotSetError("name") + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApplication(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApplication + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + if hasFields[0]&uint64(0x00000001) == 0 { + return github_com_gogo_protobuf_proto.NewRequiredNotSetError("name") } if hasFields[0]&uint64(0x00000002) == 0 { return github_com_gogo_protobuf_proto.NewRequiredNotSetError("revision") @@ -7968,6 +8468,39 @@ func (m *ApplicationResourceEventsQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -8123,6 +8656,39 @@ func (m *ApplicationManifestQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -8369,6 +8935,39 @@ func (m *ApplicationManifestQueryWithFiles) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -8791,6 +9390,39 @@ func (m *ApplicationUpdateRequest) Unmarshal(dAtA []byte) error { } b := bool(v != 0) m.Validate = &b + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -8967,6 +9599,39 @@ func (m *ApplicationDeleteRequest) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -9455,6 +10120,39 @@ func (m *ApplicationSyncRequest) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -9635,6 +10333,39 @@ func (m *ApplicationUpdateSpecRequest) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -9828,6 +10559,39 @@ func (m *ApplicationPatchRequest) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -10019,6 +10783,39 @@ func (m *ApplicationRollbackRequest) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -10247,7 +11044,41 @@ func (m *ApplicationResourceRequest) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Kind = &s + iNdEx = postIndex + hasFields[0] |= uint64(0x00000008) + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -10276,12 +11107,11 @@ func (m *ApplicationResourceRequest) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } s := string(dAtA[iNdEx:postIndex]) - m.Kind = &s + m.AppNamespace = &s iNdEx = postIndex - hasFields[0] |= uint64(0x00000008) - case 7: + case 8: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -10310,7 +11140,7 @@ func (m *ApplicationResourceRequest) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } s := string(dAtA[iNdEx:postIndex]) - m.AppNamespace = &s + m.Project = &s iNdEx = postIndex default: iNdEx = preIndex @@ -10679,6 +11509,39 @@ func (m *ApplicationResourcePatchRequest) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -11026,6 +11889,39 @@ func (m *ApplicationResourceDeleteRequest) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -11359,6 +12255,39 @@ func (m *ResourceActionRunRequest) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -12049,6 +12978,39 @@ func (m *ApplicationPodLogsQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 16: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -12399,6 +13361,39 @@ func (m *OperationTerminateRequest) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -12521,6 +13516,39 @@ func (m *ApplicationSyncWindowsQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -13192,6 +14220,39 @@ func (m *ResourcesQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.AppNamespace = &s iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -13676,6 +14737,39 @@ func (m *ListAppLinksRequest) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.Namespace = &s iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.Project = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) diff --git a/pkg/apiclient/application/application.pb.gw.go b/pkg/apiclient/application/application.pb.gw.go index 80c86e4fc9a19..ed6064cadb9a2 100644 --- a/pkg/apiclient/application/application.pb.gw.go +++ b/pkg/apiclient/application/application.pb.gw.go @@ -459,6 +459,100 @@ func local_request_ApplicationService_RevisionMetadata_0(ctx context.Context, ma } +var ( + filter_ApplicationService_RevisionChartDetails_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0, "revision": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} +) + +func request_ApplicationService_RevisionChartDetails_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq RevisionMetadataQuery + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.StringP(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + val, ok = pathParams["revision"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision") + } + + protoReq.Revision, err = runtime.StringP(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationService_RevisionChartDetails_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.RevisionChartDetails(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ApplicationService_RevisionChartDetails_0(ctx context.Context, marshaler runtime.Marshaler, server ApplicationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq RevisionMetadataQuery + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.StringP(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + val, ok = pathParams["revision"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision") + } + + protoReq.Revision, err = runtime.StringP(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationService_RevisionChartDetails_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.RevisionChartDetails(ctx, &protoReq) + return msg, metadata, err + +} + var ( filter_ApplicationService_GetManifests_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} ) @@ -2085,6 +2179,29 @@ func RegisterApplicationServiceHandlerServer(ctx context.Context, mux *runtime.S }) + mux.Handle("GET", pattern_ApplicationService_RevisionChartDetails_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ApplicationService_RevisionChartDetails_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ApplicationService_RevisionChartDetails_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_ApplicationService_GetManifests_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -2685,6 +2802,26 @@ func RegisterApplicationServiceHandlerClient(ctx context.Context, mux *runtime.S }) + mux.Handle("GET", pattern_ApplicationService_RevisionChartDetails_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ApplicationService_RevisionChartDetails_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ApplicationService_RevisionChartDetails_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_ApplicationService_GetManifests_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -3123,6 +3260,8 @@ var ( pattern_ApplicationService_RevisionMetadata_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"api", "v1", "applications", "name", "revisions", "revision", "metadata"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_ApplicationService_RevisionChartDetails_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"api", "v1", "applications", "name", "revisions", "revision", "chartdetails"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_ApplicationService_GetManifests_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "applications", "name", "manifests"}, "", runtime.AssumeColonVerbOpt(true))) pattern_ApplicationService_GetManifestsWithFiles_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "applications", "manifestsWithFiles"}, "", runtime.AssumeColonVerbOpt(true))) @@ -3181,6 +3320,8 @@ var ( forward_ApplicationService_RevisionMetadata_0 = runtime.ForwardResponseMessage + forward_ApplicationService_RevisionChartDetails_0 = runtime.ForwardResponseMessage + forward_ApplicationService_GetManifests_0 = runtime.ForwardResponseMessage forward_ApplicationService_GetManifestsWithFiles_0 = runtime.ForwardResponseMessage diff --git a/pkg/apiclient/applicationset/applicationset.pb.go b/pkg/apiclient/applicationset/applicationset.pb.go index f24e802789c2d..8f717d1f6920f 100644 --- a/pkg/apiclient/applicationset/applicationset.pb.go +++ b/pkg/apiclient/applicationset/applicationset.pb.go @@ -35,7 +35,9 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // ApplicationSetGetQuery is a query for applicationset resources type ApplicationSetGetQuery struct { // the applicationsets's name - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The application set namespace. Default empty is argocd control plane namespace + AppsetNamespace string `protobuf:"bytes,2,opt,name=appsetNamespace,proto3" json:"appsetNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -81,11 +83,20 @@ func (m *ApplicationSetGetQuery) GetName() string { return "" } +func (m *ApplicationSetGetQuery) GetAppsetNamespace() string { + if m != nil { + return m.AppsetNamespace + } + return "" +} + type ApplicationSetListQuery struct { // the project names to restrict returned list applicationsets Projects []string `protobuf:"bytes,1,rep,name=projects,proto3" json:"projects,omitempty"` // the selector to restrict returned list to applications only with matched labels - Selector string `protobuf:"bytes,2,opt,name=selector,proto3" json:"selector,omitempty"` + Selector string `protobuf:"bytes,2,opt,name=selector,proto3" json:"selector,omitempty"` + // The application set namespace. Default empty is argocd control plane namespace + AppsetNamespace string `protobuf:"bytes,3,opt,name=appsetNamespace,proto3" json:"appsetNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -138,6 +149,13 @@ func (m *ApplicationSetListQuery) GetSelector() string { return "" } +func (m *ApplicationSetListQuery) GetAppsetNamespace() string { + if m != nil { + return m.AppsetNamespace + } + return "" +} + type ApplicationSetResponse struct { Project string `protobuf:"bytes,1,opt,name=project,proto3" json:"project,omitempty"` Applicationset *v1alpha1.ApplicationSet `protobuf:"bytes,2,opt,name=applicationset,proto3" json:"applicationset,omitempty"` @@ -249,7 +267,9 @@ func (m *ApplicationSetCreateRequest) GetUpsert() bool { } type ApplicationSetDeleteRequest struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The application set namespace. Default empty is argocd control plane namespace + AppsetNamespace string `protobuf:"bytes,2,opt,name=appsetNamespace,proto3" json:"appsetNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -295,6 +315,13 @@ func (m *ApplicationSetDeleteRequest) GetName() string { return "" } +func (m *ApplicationSetDeleteRequest) GetAppsetNamespace() string { + if m != nil { + return m.AppsetNamespace + } + return "" +} + func init() { proto.RegisterType((*ApplicationSetGetQuery)(nil), "applicationset.ApplicationSetGetQuery") proto.RegisterType((*ApplicationSetListQuery)(nil), "applicationset.ApplicationSetListQuery") @@ -308,39 +335,40 @@ func init() { } var fileDescriptor_eacb9df0ce5738fa = []byte{ - // 501 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x94, 0xcf, 0x6e, 0x13, 0x31, - 0x10, 0xc6, 0xe5, 0xb6, 0x84, 0xd6, 0x48, 0x1c, 0x2c, 0xd1, 0x86, 0x05, 0x85, 0x68, 0x0f, 0xa5, - 0x14, 0xb0, 0x95, 0x70, 0x83, 0x13, 0x7f, 0xa4, 0x0a, 0x29, 0x07, 0xba, 0xbd, 0x71, 0x41, 0xae, - 0x33, 0xda, 0x2e, 0xdd, 0xae, 0x8d, 0xed, 0xac, 0x84, 0x10, 0x17, 0x24, 0x9e, 0x80, 0x27, 0x00, - 0x2e, 0x48, 0x5c, 0x79, 0x08, 0x8e, 0x48, 0xbc, 0x00, 0x8a, 0x78, 0x10, 0x64, 0x6f, 0x36, 0xe9, - 0x5a, 0x69, 0xc3, 0x21, 0xbd, 0x79, 0xd6, 0xe3, 0xf1, 0x6f, 0x3f, 0x7f, 0x33, 0x78, 0xd7, 0x80, - 0x2e, 0x41, 0x33, 0xae, 0x54, 0x9e, 0x09, 0x6e, 0x33, 0x59, 0x18, 0xb0, 0x41, 0x48, 0x95, 0x96, - 0x56, 0x92, 0xab, 0xcd, 0xaf, 0xd1, 0xcd, 0x54, 0xca, 0x34, 0x07, 0xc6, 0x55, 0xc6, 0x78, 0x51, - 0x48, 0x5b, 0xed, 0x54, 0xd9, 0xd1, 0x20, 0xcd, 0xec, 0xd1, 0xe8, 0x90, 0x0a, 0x79, 0xc2, 0xb8, - 0x4e, 0xa5, 0xd2, 0xf2, 0xb5, 0x5f, 0xdc, 0x17, 0x43, 0x56, 0xf6, 0x99, 0x3a, 0x4e, 0xdd, 0x49, - 0x73, 0xfa, 0x2e, 0x56, 0xf6, 0x78, 0xae, 0x8e, 0x78, 0x8f, 0xa5, 0x50, 0x80, 0xe6, 0x16, 0x86, - 0x55, 0xb5, 0xf8, 0x1e, 0xde, 0x7c, 0x3c, 0xcb, 0x3b, 0x00, 0xbb, 0x07, 0x76, 0x7f, 0x04, 0xfa, - 0x2d, 0x21, 0x78, 0xad, 0xe0, 0x27, 0xd0, 0x46, 0x5d, 0xb4, 0xb3, 0x91, 0xf8, 0x75, 0xbc, 0x8f, - 0xb7, 0x9a, 0xd9, 0x83, 0xcc, 0x4c, 0xd2, 0x23, 0xbc, 0xee, 0x48, 0x40, 0x58, 0xd3, 0x46, 0xdd, - 0xd5, 0x9d, 0x8d, 0x64, 0x1a, 0xbb, 0x3d, 0x03, 0x39, 0x08, 0x2b, 0x75, 0x7b, 0xc5, 0x97, 0x9b, - 0xc6, 0xf1, 0x37, 0x14, 0x12, 0x24, 0x60, 0x94, 0x13, 0x82, 0xb4, 0xf1, 0xe5, 0x49, 0x89, 0x09, - 0x44, 0x1d, 0x12, 0x8b, 0x03, 0xcd, 0x7c, 0xd9, 0x2b, 0xfd, 0x01, 0x9d, 0x89, 0x43, 0x6b, 0x71, - 0xfc, 0xe2, 0x95, 0x18, 0xd2, 0xb2, 0x4f, 0xd5, 0x71, 0x4a, 0x9d, 0x38, 0xf4, 0xd4, 0x71, 0x5a, - 0x8b, 0x43, 0x03, 0x8e, 0xe0, 0x8e, 0xf8, 0x3b, 0xc2, 0x37, 0x9a, 0x29, 0x4f, 0x35, 0x70, 0x0b, - 0x09, 0xbc, 0x19, 0x81, 0x99, 0x47, 0x85, 0x2e, 0x9e, 0x8a, 0x6c, 0xe2, 0xd6, 0x48, 0x19, 0xd0, - 0x95, 0x06, 0xeb, 0xc9, 0x24, 0x8a, 0x7b, 0x21, 0xec, 0x33, 0xc8, 0x61, 0x06, 0x3b, 0xe7, 0x79, - 0xfb, 0x9f, 0x2f, 0xe1, 0x6b, 0xcd, 0x33, 0x07, 0xa0, 0xcb, 0x4c, 0x00, 0xf9, 0x8a, 0xf0, 0xea, - 0x1e, 0x58, 0xb2, 0x4d, 0x03, 0x07, 0xcf, 0x37, 0x4f, 0xb4, 0xd4, 0x5f, 0x8e, 0xb7, 0x3f, 0xfc, - 0xfe, 0xfb, 0x69, 0xa5, 0x4b, 0x3a, 0xbe, 0x25, 0xca, 0x5e, 0xd0, 0x46, 0x86, 0xbd, 0x73, 0xf8, - 0xef, 0xc9, 0x17, 0x84, 0xd7, 0x9c, 0x23, 0xc9, 0xed, 0xf3, 0x31, 0xa7, 0xae, 0x8d, 0x5e, 0x2c, - 0x93, 0xd3, 0x95, 0x8d, 0x6f, 0x79, 0xd6, 0xeb, 0x64, 0xeb, 0x0c, 0x56, 0xf2, 0x03, 0xe1, 0x56, - 0xe5, 0x1b, 0x72, 0xf7, 0x7c, 0xcc, 0x86, 0xbb, 0x96, 0x2c, 0x29, 0xf3, 0x98, 0x77, 0xe2, 0xb3, - 0x30, 0x1f, 0x86, 0x36, 0xfb, 0x88, 0x70, 0xab, 0x72, 0xd0, 0x22, 0xec, 0x86, 0xcf, 0xa2, 0x05, - 0x8e, 0xa9, 0x9b, 0xbd, 0x7e, 0xe3, 0xdd, 0x05, 0x6f, 0xfc, 0xe4, 0xf9, 0xcf, 0x71, 0x07, 0xfd, - 0x1a, 0x77, 0xd0, 0x9f, 0x71, 0x07, 0xbd, 0x7c, 0xf4, 0x7f, 0xc3, 0x50, 0xe4, 0x19, 0x14, 0xe1, - 0xf4, 0x3d, 0x6c, 0xf9, 0x11, 0xf8, 0xe0, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb1, 0x22, 0xb1, - 0x96, 0xac, 0x05, 0x00, 0x00, + // 526 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x94, 0xdf, 0x8a, 0x13, 0x31, + 0x14, 0xc6, 0xc9, 0x76, 0xad, 0xbb, 0x11, 0x14, 0x02, 0xee, 0xd6, 0x51, 0x6a, 0x99, 0x8b, 0xb5, + 0xae, 0x98, 0xd0, 0x7a, 0xa7, 0x57, 0xfe, 0x81, 0x45, 0x28, 0xa2, 0xb3, 0xe0, 0x85, 0x5e, 0x48, + 0x76, 0x7a, 0x98, 0x1d, 0x77, 0x3a, 0x89, 0x49, 0x3a, 0x20, 0x8b, 0x37, 0x82, 0x4f, 0xe0, 0x13, + 0xa8, 0x37, 0x82, 0xb7, 0x3e, 0x84, 0x97, 0x82, 0x2f, 0x20, 0xc5, 0x07, 0x91, 0xc9, 0xcc, 0xb4, + 0x3b, 0xa1, 0xdb, 0x0a, 0x76, 0xef, 0x72, 0x26, 0x99, 0x73, 0x7e, 0xf9, 0xf2, 0x9d, 0x83, 0x77, + 0x35, 0xa8, 0x0c, 0x14, 0xe3, 0x52, 0x26, 0x71, 0xc8, 0x4d, 0x2c, 0x52, 0x0d, 0xc6, 0x09, 0xa9, + 0x54, 0xc2, 0x08, 0x72, 0xb1, 0xfe, 0xd5, 0xbb, 0x16, 0x09, 0x11, 0x25, 0xc0, 0xb8, 0x8c, 0x19, + 0x4f, 0x53, 0x61, 0x8a, 0x9d, 0xe2, 0xb4, 0x37, 0x88, 0x62, 0x73, 0x38, 0x3e, 0xa0, 0xa1, 0x18, + 0x31, 0xae, 0x22, 0x21, 0x95, 0x78, 0x6d, 0x17, 0xb7, 0xc3, 0x21, 0xcb, 0xfa, 0x4c, 0x1e, 0x45, + 0xf9, 0x9f, 0xfa, 0x64, 0x2d, 0x96, 0xf5, 0x78, 0x22, 0x0f, 0x79, 0x8f, 0x45, 0x90, 0x82, 0xe2, + 0x06, 0x86, 0x45, 0x36, 0xff, 0x39, 0xde, 0xba, 0x3f, 0x3b, 0xb7, 0x0f, 0x66, 0x0f, 0xcc, 0xb3, + 0x31, 0xa8, 0xb7, 0x84, 0xe0, 0xf5, 0x94, 0x8f, 0xa0, 0x85, 0x3a, 0xa8, 0xbb, 0x19, 0xd8, 0x35, + 0xe9, 0xe2, 0x4b, 0x5c, 0x4a, 0x0d, 0xe6, 0x09, 0x1f, 0x81, 0x96, 0x3c, 0x84, 0xd6, 0x9a, 0xdd, + 0x76, 0x3f, 0xfb, 0xc7, 0x78, 0xbb, 0x9e, 0x77, 0x10, 0xeb, 0x32, 0xb1, 0x87, 0x37, 0x72, 0x66, + 0x08, 0x8d, 0x6e, 0xa1, 0x4e, 0xa3, 0xbb, 0x19, 0x4c, 0xe3, 0x7c, 0x4f, 0x43, 0x02, 0xa1, 0x11, + 0xaa, 0xcc, 0x3c, 0x8d, 0xe7, 0x15, 0x6f, 0xcc, 0x2f, 0xfe, 0x15, 0xb9, 0xb7, 0x0a, 0x40, 0xcb, + 0x5c, 0x5c, 0xd2, 0xc2, 0xe7, 0xcb, 0x62, 0xe5, 0xc5, 0xaa, 0x90, 0x18, 0xec, 0xbc, 0x83, 0x05, + 0xb8, 0xd0, 0x1f, 0xd0, 0x99, 0xe0, 0xb4, 0x12, 0xdc, 0x2e, 0x5e, 0x85, 0x43, 0x9a, 0xf5, 0xa9, + 0x3c, 0x8a, 0x68, 0x2e, 0x38, 0x3d, 0xf1, 0x3b, 0xad, 0x04, 0xa7, 0x0e, 0x87, 0x53, 0xc3, 0xff, + 0x86, 0xf0, 0xd5, 0xfa, 0x91, 0x87, 0x0a, 0xb8, 0x81, 0x00, 0xde, 0x8c, 0x41, 0xcf, 0xa3, 0x42, + 0x67, 0x4f, 0x45, 0xb6, 0x70, 0x73, 0x2c, 0x35, 0xa8, 0x42, 0x83, 0x8d, 0xa0, 0x8c, 0xfc, 0x97, + 0x2e, 0xec, 0x23, 0x48, 0x60, 0x06, 0xfb, 0x5f, 0x96, 0xe9, 0x7f, 0x3a, 0x87, 0x2f, 0xd7, 0xb3, + 0xef, 0x83, 0xca, 0xe2, 0x10, 0xc8, 0x17, 0x84, 0x1b, 0x7b, 0x60, 0xc8, 0x0e, 0x75, 0xfa, 0x67, + 0xbe, 0x75, 0xbd, 0x95, 0x8a, 0xe3, 0xef, 0xbc, 0xff, 0xf5, 0xe7, 0xe3, 0x5a, 0x87, 0xb4, 0x6d, + 0x43, 0x66, 0x3d, 0xa7, 0x89, 0x35, 0x3b, 0xce, 0x2f, 0xfa, 0x8e, 0x7c, 0x46, 0x78, 0x3d, 0x77, + 0x39, 0xb9, 0xb1, 0x18, 0x73, 0xda, 0x09, 0xde, 0xd3, 0x55, 0x72, 0xe6, 0x69, 0xfd, 0xeb, 0x96, + 0xf5, 0x0a, 0xd9, 0x3e, 0x85, 0x95, 0x7c, 0x47, 0xb8, 0x59, 0x38, 0x8c, 0xdc, 0x5a, 0x8c, 0x59, + 0xf3, 0xe1, 0x8a, 0x25, 0x65, 0x16, 0xf3, 0xa6, 0x7f, 0x1a, 0xe6, 0x5d, 0xd7, 0x90, 0x1f, 0x10, + 0x6e, 0x16, 0x5e, 0x5b, 0x86, 0x5d, 0x73, 0xa4, 0xb7, 0xc4, 0x31, 0xd5, 0x58, 0xa8, 0xde, 0x78, + 0x77, 0xc9, 0x1b, 0x3f, 0x78, 0xfc, 0x63, 0xd2, 0x46, 0x3f, 0x27, 0x6d, 0xf4, 0x7b, 0xd2, 0x46, + 0x2f, 0xee, 0xfd, 0xdb, 0x28, 0x0e, 0x93, 0x18, 0x52, 0x77, 0xf6, 0x1f, 0x34, 0xed, 0x00, 0xbe, + 0xf3, 0x37, 0x00, 0x00, 0xff, 0xff, 0x96, 0x3f, 0x16, 0xa7, 0x2a, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -563,6 +591,13 @@ func (m *ApplicationSetGetQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.AppsetNamespace) > 0 { + i -= len(m.AppsetNamespace) + copy(dAtA[i:], m.AppsetNamespace) + i = encodeVarintApplicationset(dAtA, i, uint64(len(m.AppsetNamespace))) + i-- + dAtA[i] = 0x12 + } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) @@ -597,6 +632,13 @@ func (m *ApplicationSetListQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.AppsetNamespace) > 0 { + i -= len(m.AppsetNamespace) + copy(dAtA[i:], m.AppsetNamespace) + i = encodeVarintApplicationset(dAtA, i, uint64(len(m.AppsetNamespace))) + i-- + dAtA[i] = 0x1a + } if len(m.Selector) > 0 { i -= len(m.Selector) copy(dAtA[i:], m.Selector) @@ -735,6 +777,13 @@ func (m *ApplicationSetDeleteRequest) MarshalToSizedBuffer(dAtA []byte) (int, er i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.AppsetNamespace) > 0 { + i -= len(m.AppsetNamespace) + copy(dAtA[i:], m.AppsetNamespace) + i = encodeVarintApplicationset(dAtA, i, uint64(len(m.AppsetNamespace))) + i-- + dAtA[i] = 0x12 + } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) @@ -766,6 +815,10 @@ func (m *ApplicationSetGetQuery) Size() (n int) { if l > 0 { n += 1 + l + sovApplicationset(uint64(l)) } + l = len(m.AppsetNamespace) + if l > 0 { + n += 1 + l + sovApplicationset(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -788,6 +841,10 @@ func (m *ApplicationSetListQuery) Size() (n int) { if l > 0 { n += 1 + l + sovApplicationset(uint64(l)) } + l = len(m.AppsetNamespace) + if l > 0 { + n += 1 + l + sovApplicationset(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -843,6 +900,10 @@ func (m *ApplicationSetDeleteRequest) Size() (n int) { if l > 0 { n += 1 + l + sovApplicationset(uint64(l)) } + l = len(m.AppsetNamespace) + if l > 0 { + n += 1 + l + sovApplicationset(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -916,6 +977,38 @@ func (m *ApplicationSetGetQuery) Unmarshal(dAtA []byte) error { } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppsetNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplicationset + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplicationset + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplicationset + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AppsetNamespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplicationset(dAtA[iNdEx:]) @@ -1031,6 +1124,38 @@ func (m *ApplicationSetListQuery) Unmarshal(dAtA []byte) error { } m.Selector = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppsetNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplicationset + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplicationset + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplicationset + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AppsetNamespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplicationset(dAtA[iNdEx:]) @@ -1340,6 +1465,38 @@ func (m *ApplicationSetDeleteRequest) Unmarshal(dAtA []byte) error { } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppsetNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplicationset + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApplicationset + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplicationset + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AppsetNamespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplicationset(dAtA[iNdEx:]) diff --git a/pkg/apiclient/applicationset/applicationset.pb.gw.go b/pkg/apiclient/applicationset/applicationset.pb.gw.go index db537f548cb30..5e4c73f7add3b 100644 --- a/pkg/apiclient/applicationset/applicationset.pb.gw.go +++ b/pkg/apiclient/applicationset/applicationset.pb.gw.go @@ -33,6 +33,10 @@ var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage var _ = metadata.Join +var ( + filter_ApplicationSetService_Get_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + func request_ApplicationSetService_Get_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationSetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ApplicationSetGetQuery var metadata runtime.ServerMetadata @@ -55,6 +59,13 @@ func request_ApplicationSetService_Get_0(ctx context.Context, marshaler runtime. return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationSetService_Get_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.Get(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -82,6 +93,13 @@ func local_request_ApplicationSetService_Get_0(ctx context.Context, marshaler ru return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationSetService_Get_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.Get(ctx, &protoReq) return msg, metadata, err @@ -175,6 +193,10 @@ func local_request_ApplicationSetService_Create_0(ctx context.Context, marshaler } +var ( + filter_ApplicationSetService_Delete_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + func request_ApplicationSetService_Delete_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationSetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ApplicationSetDeleteRequest var metadata runtime.ServerMetadata @@ -197,6 +219,13 @@ func request_ApplicationSetService_Delete_0(ctx context.Context, marshaler runti return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationSetService_Delete_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.Delete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -224,6 +253,13 @@ func local_request_ApplicationSetService_Delete_0(ctx context.Context, marshaler return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationSetService_Delete_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.Delete(ctx, &protoReq) return msg, metadata, err diff --git a/pkg/apiclient/grpcproxy.go b/pkg/apiclient/grpcproxy.go index 9e5b841ae273a..72fea42efee3f 100644 --- a/pkg/apiclient/grpcproxy.go +++ b/pkg/apiclient/grpcproxy.go @@ -13,9 +13,11 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/grpc/keepalive" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" + "github.com/argoproj/argo-cd/v2/common" argocderrors "github.com/argoproj/argo-cd/v2/util/errors" argoio "github.com/argoproj/argo-cd/v2/util/io" "github.com/argoproj/argo-cd/v2/util/rand" @@ -112,6 +114,11 @@ func (c *client) startGRPCProxy() (*grpc.Server, net.Listener, error) { } proxySrv := grpc.NewServer( grpc.ForceServerCodec(&noopCodec{}), + grpc.KeepaliveEnforcementPolicy( + keepalive.EnforcementPolicy{ + MinTime: common.GetGRPCKeepAliveEnforcementMinimum(), + }, + ), grpc.UnknownServiceHandler(func(srv interface{}, stream grpc.ServerStream) error { fullMethodName, ok := grpc.MethodFromServerStream(stream) if !ok { @@ -124,14 +131,14 @@ func (c *client) startGRPCProxy() (*grpc.Server, net.Listener, error) { } md, _ := metadata.FromIncomingContext(stream.Context()) + headersMD, err := parseGRPCHeaders(c.Headers) - for _, kv := range c.Headers { - if len(strings.Split(kv, ":"))%2 == 1 { - return fmt.Errorf("additional headers key/values must be separated by a colon(:): %s", kv) - } - md.Append(strings.Split(kv, ":")[0], strings.Split(kv, ":")[1]) + if err != nil { + return err } + md = metadata.Join(md, headersMD) + resp, err := c.executeRequest(fullMethodName, msg, md) if err != nil { return err @@ -209,3 +216,16 @@ func (c *client) useGRPCProxy() (net.Addr, io.Closer, error) { return nil }), nil } + +func parseGRPCHeaders(headerStrings []string) (metadata.MD, error) { + md := metadata.New(map[string]string{}) + for _, kv := range headerStrings { + i := strings.IndexByte(kv, ':') + // zero means meaningless empty header name + if i <= 0 { + return nil, fmt.Errorf("additional headers must be colon(:)-separated: %s", kv) + } + md.Append(kv[0:i], kv[i+1:]) + } + return md, nil +} diff --git a/pkg/apiclient/repository/repository.pb.go b/pkg/apiclient/repository/repository.pb.go index b70b3ab1b9585..5540580c21f45 100644 --- a/pkg/apiclient/repository/repository.pb.go +++ b/pkg/apiclient/repository/repository.pb.go @@ -366,7 +366,9 @@ type RepoAccessQuery struct { // Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity Project string `protobuf:"bytes,17,opt,name=project,proto3" json:"project,omitempty"` // Google Cloud Platform service account key - GcpServiceAccountKey string `protobuf:"bytes,18,opt,name=gcpServiceAccountKey,proto3" json:"gcpServiceAccountKey,omitempty"` + GcpServiceAccountKey string `protobuf:"bytes,18,opt,name=gcpServiceAccountKey,proto3" json:"gcpServiceAccountKey,omitempty"` + // Whether to force HTTP basic auth + ForceHttpBasicAuth bool `protobuf:"varint,19,opt,name=forceHttpBasicAuth,proto3" json:"forceHttpBasicAuth,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -524,6 +526,13 @@ func (m *RepoAccessQuery) GetGcpServiceAccountKey() string { return "" } +func (m *RepoAccessQuery) GetForceHttpBasicAuth() bool { + if m != nil { + return m.ForceHttpBasicAuth + } + return false +} + type RepoResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -694,78 +703,79 @@ func init() { } var fileDescriptor_8d38260443475705 = []byte{ - // 1127 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xcd, 0x6e, 0x1c, 0x45, - 0x10, 0xd6, 0xd8, 0xce, 0xda, 0x2e, 0xff, 0x64, 0xdd, 0x36, 0x61, 0xd8, 0x38, 0x8e, 0x35, 0x09, - 0x91, 0xb1, 0xc2, 0x4c, 0xbc, 0x08, 0x81, 0x82, 0x40, 0x72, 0x6c, 0x2b, 0xb1, 0xb0, 0x70, 0x98, - 0xc8, 0x1c, 0x10, 0x08, 0xb5, 0x67, 0xcb, 0xbb, 0x93, 0xcc, 0xce, 0x74, 0xba, 0x7b, 0x17, 0x56, - 0x51, 0x2e, 0x9c, 0x90, 0xe0, 0x82, 0x10, 0x12, 0x37, 0x2e, 0x48, 0x1c, 0x78, 0x01, 0x1e, 0x81, - 0x23, 0x12, 0x2f, 0x80, 0x2c, 0x5e, 0x80, 0x17, 0x40, 0xa8, 0xbb, 0x67, 0x67, 0x66, 0xbd, 0x3f, - 0x76, 0x84, 0xf1, 0xad, 0xeb, 0xab, 0x9a, 0xaa, 0xaf, 0xbf, 0xae, 0xae, 0xde, 0x05, 0x47, 0x20, - 0x6f, 0x23, 0xf7, 0x38, 0xb2, 0x44, 0x84, 0x32, 0xe1, 0x9d, 0xc2, 0xd2, 0x65, 0x3c, 0x91, 0x09, - 0x81, 0x1c, 0xa9, 0x2c, 0xd7, 0x93, 0xa4, 0x1e, 0xa1, 0x47, 0x59, 0xe8, 0xd1, 0x38, 0x4e, 0x24, - 0x95, 0x61, 0x12, 0x0b, 0x13, 0x59, 0xd9, 0xab, 0x87, 0xb2, 0xd1, 0x3a, 0x74, 0x83, 0xa4, 0xe9, - 0x51, 0x5e, 0x4f, 0x18, 0x4f, 0x1e, 0xeb, 0xc5, 0xeb, 0x41, 0xcd, 0x6b, 0x57, 0x3d, 0xf6, 0xa4, - 0xae, 0xbe, 0x14, 0x1e, 0x65, 0x2c, 0x0a, 0x03, 0xfd, 0xad, 0xd7, 0xde, 0xa0, 0x11, 0x6b, 0xd0, - 0x0d, 0xaf, 0x8e, 0x31, 0x72, 0x2a, 0xb1, 0x96, 0x66, 0xdb, 0x39, 0x25, 0x9b, 0xa6, 0x75, 0x2a, - 0x7d, 0xa7, 0x03, 0x73, 0x3e, 0xb2, 0x64, 0x93, 0x31, 0xf1, 0x61, 0x0b, 0x79, 0x87, 0x10, 0x98, - 0x50, 0x41, 0xb6, 0xb5, 0x6a, 0xad, 0x4d, 0xfb, 0x7a, 0x4d, 0x2a, 0x30, 0xc5, 0xb1, 0x1d, 0x8a, - 0x30, 0x89, 0xed, 0x31, 0x8d, 0x67, 0x36, 0xb1, 0x61, 0x92, 0x32, 0xf6, 0x01, 0x6d, 0xa2, 0x3d, - 0xae, 0x5d, 0x5d, 0x93, 0xac, 0x00, 0x50, 0xc6, 0x1e, 0xf2, 0xe4, 0x31, 0x06, 0xd2, 0x9e, 0xd0, - 0xce, 0x02, 0xe2, 0x6c, 0xc0, 0xe4, 0x26, 0x63, 0xbb, 0xf1, 0x51, 0xa2, 0x8a, 0xca, 0x0e, 0xc3, - 0x6e, 0x51, 0xb5, 0x56, 0x18, 0xa3, 0xb2, 0x91, 0x16, 0xd4, 0x6b, 0xe7, 0x57, 0x0b, 0x16, 0x53, - 0xba, 0xdb, 0x28, 0x69, 0x18, 0xa5, 0xa4, 0xeb, 0x50, 0x12, 0x49, 0x8b, 0x07, 0x26, 0xc3, 0x4c, - 0x75, 0xdf, 0xcd, 0xd5, 0x71, 0xbb, 0xea, 0xe8, 0xc5, 0x67, 0x41, 0xcd, 0x6d, 0x57, 0x5d, 0xf6, - 0xa4, 0xee, 0x2a, 0xad, 0xdd, 0x82, 0xd6, 0x6e, 0x57, 0x6b, 0x77, 0x33, 0x07, 0x1f, 0xe9, 0xb4, - 0x7e, 0x9a, 0xbe, 0xb8, 0xdb, 0xb1, 0x51, 0xbb, 0x1d, 0xef, 0xdb, 0xed, 0xbb, 0x50, 0xee, 0x0a, - 0xed, 0xa3, 0x60, 0x49, 0x2c, 0x90, 0xbc, 0x06, 0x97, 0x42, 0x89, 0x4d, 0x61, 0x5b, 0xab, 0xe3, - 0x6b, 0x33, 0xd5, 0x45, 0xb7, 0x70, 0x3c, 0xa9, 0x34, 0xbe, 0x89, 0x70, 0xb6, 0x60, 0x5a, 0x7d, - 0x3e, 0xfc, 0x8c, 0x1c, 0x98, 0x3d, 0x4a, 0x14, 0x55, 0x3c, 0xe2, 0x28, 0x8c, 0x6c, 0x53, 0x7e, - 0x0f, 0xe6, 0xfc, 0x3d, 0x01, 0x97, 0x35, 0x89, 0x20, 0x40, 0x31, 0xfa, 0xbc, 0x5b, 0x02, 0x79, - 0x9c, 0x6f, 0x33, 0xb3, 0x95, 0x8f, 0x51, 0x21, 0x3e, 0x4f, 0x78, 0x2d, 0xdd, 0x65, 0x66, 0x93, - 0x9b, 0x30, 0x27, 0x44, 0xe3, 0x21, 0x0f, 0xdb, 0x54, 0xe2, 0xfb, 0xd8, 0x49, 0x0f, 0xbd, 0x17, - 0x54, 0x19, 0xc2, 0x58, 0x60, 0xd0, 0xe2, 0x68, 0x5f, 0xd2, 0x2c, 0x33, 0x9b, 0xdc, 0x86, 0x05, - 0x19, 0x89, 0xad, 0x28, 0xc4, 0x58, 0x6e, 0x21, 0x97, 0xdb, 0x54, 0x52, 0xbb, 0xa4, 0xb3, 0xf4, - 0x3b, 0xc8, 0x3a, 0x94, 0x7b, 0x40, 0x55, 0x72, 0x52, 0x07, 0xf7, 0xe1, 0x59, 0x8b, 0x4d, 0xf7, - 0xb6, 0x98, 0xde, 0x23, 0x18, 0x4c, 0xef, 0x6f, 0x19, 0xa6, 0x31, 0xa6, 0x87, 0x11, 0xee, 0x07, - 0xa1, 0x3d, 0xa3, 0xe9, 0xe5, 0x00, 0xb9, 0x03, 0x8b, 0xa6, 0xb3, 0x36, 0xd5, 0xc9, 0x66, 0xfb, - 0x9c, 0xd5, 0x09, 0x06, 0xb9, 0xc8, 0x2a, 0xcc, 0x64, 0xf0, 0xee, 0xb6, 0x3d, 0xb7, 0x6a, 0xad, - 0x8d, 0xfb, 0x45, 0x88, 0xbc, 0x0d, 0x2f, 0xe7, 0x66, 0x2c, 0x24, 0x8d, 0x22, 0xdd, 0x7a, 0xbb, - 0xdb, 0xf6, 0xbc, 0x8e, 0x1e, 0xe6, 0x26, 0xef, 0x41, 0x25, 0x73, 0xed, 0xc4, 0x12, 0x39, 0xe3, - 0xa1, 0xc0, 0x7b, 0x54, 0xe0, 0x01, 0x8f, 0xec, 0xcb, 0x9a, 0xd4, 0x88, 0x08, 0xb2, 0x04, 0x97, - 0x18, 0x4f, 0xbe, 0xe8, 0xd8, 0x65, 0x1d, 0x6a, 0x0c, 0xd5, 0xe3, 0x2c, 0x6d, 0xe3, 0x05, 0xd3, - 0xe3, 0xa9, 0x49, 0xaa, 0xb0, 0x54, 0x0f, 0xd8, 0x23, 0xe4, 0xed, 0x30, 0xc0, 0xcd, 0x20, 0x48, - 0x5a, 0xb1, 0xd6, 0x9c, 0xe8, 0xb0, 0x81, 0x3e, 0x67, 0x1e, 0x66, 0x55, 0xcb, 0x75, 0x7b, 0xde, - 0xf9, 0xd9, 0x82, 0x05, 0x05, 0x6c, 0x71, 0xa4, 0x12, 0x7d, 0x7c, 0xda, 0x42, 0x21, 0xc9, 0x27, - 0x85, 0x2e, 0x9c, 0xa9, 0x3e, 0xf8, 0x6f, 0xd7, 0xd7, 0xcf, 0x6e, 0x51, 0xda, 0xcf, 0x57, 0xa0, - 0xd4, 0x62, 0x02, 0xb9, 0x4c, 0x6f, 0x45, 0x6a, 0xa9, 0xb3, 0x0e, 0x38, 0xd6, 0xc4, 0x7e, 0x1c, - 0x75, 0x74, 0x33, 0x4f, 0xf9, 0x39, 0xe0, 0x3c, 0x35, 0x44, 0x0f, 0x58, 0xed, 0xa2, 0x88, 0x56, - 0xff, 0x99, 0x37, 0x35, 0x0d, 0x98, 0x8a, 0x49, 0xbe, 0xb1, 0x60, 0x62, 0x2f, 0x14, 0x92, 0xbc, - 0x54, 0x1c, 0x10, 0xd9, 0x38, 0xa8, 0xec, 0x9d, 0x17, 0x0b, 0x55, 0xc4, 0xb9, 0xfe, 0xe5, 0x1f, - 0x7f, 0x7d, 0x37, 0x76, 0x85, 0x2c, 0xe9, 0x67, 0xac, 0xbd, 0x91, 0xbf, 0x19, 0x21, 0x8a, 0xaf, - 0xc6, 0x2c, 0xf2, 0xb5, 0x05, 0xe3, 0xf7, 0x71, 0x28, 0x9b, 0x73, 0xd3, 0xc4, 0xb9, 0xa1, 0x99, - 0x5c, 0x23, 0x57, 0x07, 0x31, 0xf1, 0x9e, 0x29, 0xeb, 0x39, 0xf9, 0xde, 0x82, 0xb2, 0xe2, 0xed, - 0x17, 0x7c, 0x17, 0x23, 0xd4, 0xf2, 0x28, 0xa1, 0xc8, 0xa7, 0x30, 0x65, 0x68, 0x1d, 0x0d, 0xa5, - 0x53, 0xee, 0x85, 0x8f, 0x84, 0xb3, 0xa6, 0x53, 0x3a, 0x64, 0x75, 0xc4, 0x8e, 0x3d, 0xae, 0x52, - 0x36, 0x4d, 0x7a, 0xf5, 0x9c, 0x90, 0x57, 0x4e, 0xa6, 0xcf, 0x5e, 0xf3, 0xca, 0xf2, 0x20, 0x57, - 0x76, 0x17, 0xcf, 0x54, 0x8e, 0xaa, 0x12, 0xdf, 0x5a, 0x30, 0x77, 0x1f, 0x65, 0xfe, 0xee, 0x92, - 0xeb, 0x03, 0x32, 0x17, 0xdf, 0xe4, 0x8a, 0x33, 0x3c, 0x20, 0x23, 0xf0, 0x8e, 0x26, 0xf0, 0xa6, - 0x73, 0x67, 0x30, 0x01, 0xf3, 0xe8, 0xea, 0x3c, 0x07, 0xfe, 0x9e, 0xa6, 0x52, 0x33, 0x19, 0xee, - 0x5a, 0xeb, 0xa4, 0xad, 0x29, 0x3d, 0xc0, 0xa8, 0xb9, 0xd5, 0xa0, 0x5c, 0x0e, 0x95, 0x79, 0xa5, - 0x08, 0xe7, 0xe1, 0x19, 0x09, 0x57, 0x93, 0x58, 0x23, 0xb7, 0x46, 0xa9, 0xd0, 0xc0, 0xa8, 0x19, - 0x98, 0x32, 0x3f, 0x58, 0x50, 0x32, 0xd3, 0x8b, 0x5c, 0x3b, 0x59, 0xb1, 0x67, 0xaa, 0x9d, 0xe3, - 0x55, 0x78, 0x55, 0x73, 0x5c, 0x76, 0x06, 0xf6, 0xda, 0x5d, 0x3d, 0x3c, 0xd4, 0xd5, 0xfc, 0xd1, - 0x82, 0x72, 0x97, 0x42, 0xf7, 0xdb, 0x8b, 0x23, 0xe9, 0x9c, 0x4e, 0x92, 0xfc, 0x64, 0x41, 0xc9, - 0x4c, 0xd4, 0x7e, 0x5e, 0x3d, 0x93, 0xf6, 0x1c, 0x79, 0x6d, 0x98, 0x03, 0xae, 0x8c, 0x68, 0x73, - 0x4d, 0xe5, 0x79, 0x2e, 0xe4, 0x2f, 0x16, 0x94, 0xbb, 0x74, 0x86, 0x0b, 0xf9, 0x7f, 0x11, 0x76, - 0x5f, 0x8c, 0x30, 0xa1, 0x50, 0xda, 0xc6, 0x08, 0x25, 0x0e, 0xbb, 0x02, 0xf6, 0x49, 0x38, 0x6b, - 0xfe, 0x5b, 0x66, 0xc6, 0xae, 0x8f, 0x9a, 0xb1, 0x4a, 0x90, 0x06, 0x94, 0x4d, 0x89, 0x82, 0x1e, - 0x2f, 0x5c, 0xec, 0xc6, 0x19, 0x8a, 0x91, 0x67, 0x30, 0xff, 0x11, 0x8d, 0x42, 0xa5, 0xac, 0xf9, - 0x9d, 0x4a, 0xae, 0xf6, 0x4d, 0x92, 0xfc, 0xf7, 0xeb, 0x88, 0x6a, 0x55, 0x5d, 0xed, 0xb6, 0x73, - 0x73, 0xd4, 0xbd, 0x6e, 0xa7, 0xa5, 0x8c, 0x92, 0xf7, 0x76, 0x7e, 0x3b, 0x5e, 0xb1, 0x7e, 0x3f, - 0x5e, 0xb1, 0xfe, 0x3c, 0x5e, 0xb1, 0x3e, 0x7e, 0xeb, 0x6c, 0xff, 0xd8, 0x02, 0xfd, 0x43, 0xb3, - 0xf0, 0xdf, 0xea, 0xb0, 0xa4, 0xff, 0x5c, 0xbd, 0xf1, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x89, - 0x56, 0x9b, 0x65, 0x41, 0x0e, 0x00, 0x00, + // 1146 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x5f, 0x6f, 0x1b, 0x45, + 0x10, 0xd7, 0x25, 0x8d, 0x9b, 0x4c, 0x9a, 0xd4, 0xd9, 0x84, 0x72, 0xb8, 0x69, 0x1a, 0x5d, 0x4b, + 0x15, 0xa2, 0x72, 0xd7, 0x18, 0x21, 0x50, 0x11, 0x48, 0xce, 0x1f, 0x35, 0x11, 0x11, 0x29, 0x57, + 0x85, 0x07, 0x04, 0x42, 0x9b, 0xf3, 0xc4, 0xbe, 0xf6, 0x7c, 0xb7, 0xdd, 0x5d, 0x1b, 0xac, 0xaa, + 0x2f, 0x3c, 0x21, 0xc1, 0x0b, 0x42, 0x48, 0xbc, 0x21, 0x24, 0x24, 0x1e, 0xf8, 0x02, 0x7c, 0x04, + 0x1e, 0x91, 0xf8, 0x02, 0x28, 0xe2, 0x73, 0x20, 0xb4, 0xbb, 0xe7, 0xbb, 0x73, 0x62, 0x3b, 0xa9, + 0x08, 0x79, 0xdb, 0xf9, 0xcd, 0xdc, 0xcc, 0x6f, 0x7f, 0x3b, 0x3b, 0x6b, 0x83, 0x23, 0x90, 0x77, + 0x90, 0x7b, 0x1c, 0x59, 0x22, 0x42, 0x99, 0xf0, 0x6e, 0x61, 0xe9, 0x32, 0x9e, 0xc8, 0x84, 0x40, + 0x8e, 0x54, 0x16, 0x1b, 0x49, 0xd2, 0x88, 0xd0, 0xa3, 0x2c, 0xf4, 0x68, 0x1c, 0x27, 0x92, 0xca, + 0x30, 0x89, 0x85, 0x89, 0xac, 0xec, 0x36, 0x42, 0xd9, 0x6c, 0x1f, 0xb8, 0x41, 0xd2, 0xf2, 0x28, + 0x6f, 0x24, 0x8c, 0x27, 0x8f, 0xf5, 0xe2, 0xf5, 0xa0, 0xee, 0x75, 0xaa, 0x1e, 0x7b, 0xd2, 0x50, + 0x5f, 0x0a, 0x8f, 0x32, 0x16, 0x85, 0x81, 0xfe, 0xd6, 0xeb, 0xac, 0xd1, 0x88, 0x35, 0xe9, 0x9a, + 0xd7, 0xc0, 0x18, 0x39, 0x95, 0x58, 0x4f, 0xb3, 0x6d, 0x9d, 0x92, 0x4d, 0xd3, 0x3a, 0x95, 0xbe, + 0xd3, 0x85, 0x19, 0x1f, 0x59, 0x52, 0x63, 0x4c, 0x7c, 0xd8, 0x46, 0xde, 0x25, 0x04, 0x2e, 0xa9, + 0x20, 0xdb, 0x5a, 0xb6, 0x56, 0xa6, 0x7c, 0xbd, 0x26, 0x15, 0x98, 0xe4, 0xd8, 0x09, 0x45, 0x98, + 0xc4, 0xf6, 0x98, 0xc6, 0x33, 0x9b, 0xd8, 0x70, 0x99, 0x32, 0xf6, 0x01, 0x6d, 0xa1, 0x3d, 0xae, + 0x5d, 0x3d, 0x93, 0x2c, 0x01, 0x50, 0xc6, 0x1e, 0xf2, 0xe4, 0x31, 0x06, 0xd2, 0xbe, 0xa4, 0x9d, + 0x05, 0xc4, 0x59, 0x83, 0xcb, 0x35, 0xc6, 0x76, 0xe2, 0xc3, 0x44, 0x15, 0x95, 0x5d, 0x86, 0xbd, + 0xa2, 0x6a, 0xad, 0x30, 0x46, 0x65, 0x33, 0x2d, 0xa8, 0xd7, 0xce, 0x6f, 0x16, 0xcc, 0xa7, 0x74, + 0x37, 0x51, 0xd2, 0x30, 0x4a, 0x49, 0x37, 0xa0, 0x24, 0x92, 0x36, 0x0f, 0x4c, 0x86, 0xe9, 0xea, + 0x9e, 0x9b, 0xab, 0xe3, 0xf6, 0xd4, 0xd1, 0x8b, 0xcf, 0x82, 0xba, 0xdb, 0xa9, 0xba, 0xec, 0x49, + 0xc3, 0x55, 0x5a, 0xbb, 0x05, 0xad, 0xdd, 0x9e, 0xd6, 0x6e, 0x2d, 0x07, 0x1f, 0xe9, 0xb4, 0x7e, + 0x9a, 0xbe, 0xb8, 0xdb, 0xb1, 0x51, 0xbb, 0x1d, 0x3f, 0xb1, 0xdb, 0x77, 0xa1, 0xdc, 0x13, 0xda, + 0x47, 0xc1, 0x92, 0x58, 0x20, 0x79, 0x0d, 0x26, 0x42, 0x89, 0x2d, 0x61, 0x5b, 0xcb, 0xe3, 0x2b, + 0xd3, 0xd5, 0x79, 0xb7, 0x70, 0x3c, 0xa9, 0x34, 0xbe, 0x89, 0x70, 0x36, 0x60, 0x4a, 0x7d, 0x3e, + 0xfc, 0x8c, 0x1c, 0xb8, 0x72, 0x98, 0x28, 0xaa, 0x78, 0xc8, 0x51, 0x18, 0xd9, 0x26, 0xfd, 0x3e, + 0xcc, 0xf9, 0x69, 0x02, 0xae, 0x6a, 0x12, 0x41, 0x80, 0x62, 0xf4, 0x79, 0xb7, 0x05, 0xf2, 0x38, + 0xdf, 0x66, 0x66, 0x2b, 0x1f, 0xa3, 0x42, 0x7c, 0x9e, 0xf0, 0x7a, 0xba, 0xcb, 0xcc, 0x26, 0xb7, + 0x61, 0x46, 0x88, 0xe6, 0x43, 0x1e, 0x76, 0xa8, 0xc4, 0xf7, 0xb1, 0x9b, 0x1e, 0x7a, 0x3f, 0xa8, + 0x32, 0x84, 0xb1, 0xc0, 0xa0, 0xcd, 0xd1, 0x9e, 0xd0, 0x2c, 0x33, 0x9b, 0xdc, 0x85, 0x39, 0x19, + 0x89, 0x8d, 0x28, 0xc4, 0x58, 0x6e, 0x20, 0x97, 0x9b, 0x54, 0x52, 0xbb, 0xa4, 0xb3, 0x9c, 0x74, + 0x90, 0x55, 0x28, 0xf7, 0x81, 0xaa, 0xe4, 0x65, 0x1d, 0x7c, 0x02, 0xcf, 0x5a, 0x6c, 0xaa, 0xbf, + 0xc5, 0xf4, 0x1e, 0xc1, 0x60, 0x7a, 0x7f, 0x8b, 0x30, 0x85, 0x31, 0x3d, 0x88, 0x70, 0x2f, 0x08, + 0xed, 0x69, 0x4d, 0x2f, 0x07, 0xc8, 0x3d, 0x98, 0x37, 0x9d, 0x55, 0x53, 0x27, 0x9b, 0xed, 0xf3, + 0x8a, 0x4e, 0x30, 0xc8, 0x45, 0x96, 0x61, 0x3a, 0x83, 0x77, 0x36, 0xed, 0x99, 0x65, 0x6b, 0x65, + 0xdc, 0x2f, 0x42, 0xe4, 0x6d, 0x78, 0x39, 0x37, 0x63, 0x21, 0x69, 0x14, 0xe9, 0xd6, 0xdb, 0xd9, + 0xb4, 0x67, 0x75, 0xf4, 0x30, 0x37, 0x79, 0x0f, 0x2a, 0x99, 0x6b, 0x2b, 0x96, 0xc8, 0x19, 0x0f, + 0x05, 0xae, 0x53, 0x81, 0xfb, 0x3c, 0xb2, 0xaf, 0x6a, 0x52, 0x23, 0x22, 0xc8, 0x02, 0x4c, 0x30, + 0x9e, 0x7c, 0xd1, 0xb5, 0xcb, 0x3a, 0xd4, 0x18, 0xaa, 0xc7, 0x59, 0xda, 0xc6, 0x73, 0xa6, 0xc7, + 0x53, 0x93, 0x54, 0x61, 0xa1, 0x11, 0xb0, 0x47, 0xc8, 0x3b, 0x61, 0x80, 0xb5, 0x20, 0x48, 0xda, + 0xb1, 0xd6, 0x9c, 0xe8, 0xb0, 0x81, 0x3e, 0xe2, 0x02, 0xd1, 0x3d, 0xb8, 0x2d, 0x25, 0x5b, 0xa7, + 0x22, 0x0c, 0x6a, 0x6d, 0xd9, 0xb4, 0xe7, 0xb5, 0xb0, 0x03, 0x3c, 0xce, 0x2c, 0x5c, 0x51, 0x2d, + 0xda, 0xbb, 0x23, 0xce, 0x2f, 0x16, 0xcc, 0x29, 0x60, 0x83, 0x23, 0x95, 0xe8, 0xe3, 0xd3, 0x36, + 0x0a, 0x49, 0x3e, 0x29, 0x74, 0xed, 0x74, 0x75, 0xfb, 0xbf, 0x5d, 0x77, 0x3f, 0xbb, 0x75, 0x69, + 0xff, 0x5f, 0x83, 0x52, 0x9b, 0x09, 0xe4, 0x32, 0xbd, 0x45, 0xa9, 0xa5, 0x7a, 0x23, 0xe0, 0x58, + 0x17, 0x7b, 0x71, 0xd4, 0xd5, 0xcd, 0x3f, 0xe9, 0xe7, 0x80, 0xf3, 0xd4, 0x10, 0xdd, 0x67, 0xf5, + 0x8b, 0x22, 0x5a, 0xfd, 0x67, 0xd6, 0xd4, 0x34, 0x60, 0x2a, 0x3e, 0xf9, 0xc6, 0x82, 0x4b, 0xbb, + 0xa1, 0x90, 0xe4, 0xa5, 0xe2, 0x40, 0xc9, 0xc6, 0x47, 0x65, 0xf7, 0xbc, 0x58, 0xa8, 0x22, 0xce, + 0xcd, 0x2f, 0xff, 0xfc, 0xfb, 0xbb, 0xb1, 0x6b, 0x64, 0x41, 0x3f, 0x7b, 0x9d, 0xb5, 0xfc, 0x8d, + 0x09, 0x51, 0x7c, 0x35, 0x66, 0x91, 0xaf, 0x2d, 0x18, 0x7f, 0x80, 0x43, 0xd9, 0x9c, 0x9b, 0x26, + 0xce, 0x2d, 0xcd, 0xe4, 0x06, 0xb9, 0x3e, 0x88, 0x89, 0xf7, 0x4c, 0x59, 0xcf, 0xc9, 0xf7, 0x16, + 0x94, 0x15, 0x6f, 0xbf, 0xe0, 0xbb, 0x18, 0xa1, 0x16, 0x47, 0x09, 0x45, 0x3e, 0x85, 0x49, 0x43, + 0xeb, 0x70, 0x28, 0x9d, 0x72, 0x3f, 0x7c, 0x28, 0x9c, 0x15, 0x9d, 0xd2, 0x21, 0xcb, 0x23, 0x76, + 0xec, 0x71, 0x95, 0xb2, 0x65, 0xd2, 0xab, 0xe7, 0x87, 0xbc, 0x72, 0x3c, 0x7d, 0xf6, 0xfa, 0x57, + 0x16, 0x07, 0xb9, 0xb2, 0xbb, 0x78, 0xa6, 0x72, 0x54, 0x95, 0xf8, 0xd6, 0x82, 0x99, 0x07, 0x28, + 0xf3, 0x77, 0x9a, 0xdc, 0x1c, 0x90, 0xb9, 0xf8, 0x86, 0x57, 0x9c, 0xe1, 0x01, 0x19, 0x81, 0x77, + 0x34, 0x81, 0x37, 0x9d, 0x7b, 0x83, 0x09, 0x98, 0x47, 0x5a, 0xe7, 0xd9, 0xf7, 0x77, 0x35, 0x95, + 0xba, 0xc9, 0x70, 0xdf, 0x5a, 0x25, 0x1d, 0x4d, 0x69, 0x1b, 0xa3, 0xd6, 0x46, 0x93, 0x72, 0x39, + 0x54, 0xe6, 0xa5, 0x22, 0x9c, 0x87, 0x67, 0x24, 0x5c, 0x4d, 0x62, 0x85, 0xdc, 0x19, 0xa5, 0x42, + 0x13, 0xa3, 0x56, 0x60, 0xca, 0xfc, 0x60, 0x41, 0xc9, 0x4c, 0x2f, 0x72, 0xe3, 0x78, 0xc5, 0xbe, + 0xa9, 0x76, 0x8e, 0x57, 0xe1, 0x55, 0xcd, 0x71, 0xd1, 0x19, 0xd8, 0x6b, 0xf7, 0xf5, 0xf0, 0x50, + 0x57, 0xf3, 0x47, 0x0b, 0xca, 0x3d, 0x0a, 0xbd, 0x6f, 0x2f, 0x8e, 0xa4, 0x73, 0x3a, 0x49, 0xf2, + 0xb3, 0x05, 0x25, 0x33, 0x51, 0x4f, 0xf2, 0xea, 0x9b, 0xb4, 0xe7, 0xc8, 0x6b, 0xcd, 0x1c, 0x70, + 0x65, 0x44, 0x9b, 0x6b, 0x2a, 0xcf, 0x73, 0x21, 0x7f, 0xb5, 0xa0, 0xdc, 0xa3, 0x33, 0x5c, 0xc8, + 0xff, 0x8b, 0xb0, 0xfb, 0x62, 0x84, 0x09, 0x85, 0xd2, 0x26, 0x46, 0x28, 0x71, 0xd8, 0x15, 0xb0, + 0x8f, 0xc3, 0x59, 0xf3, 0xdf, 0x31, 0x33, 0x76, 0x75, 0xd4, 0x8c, 0x55, 0x82, 0x34, 0xa1, 0x6c, + 0x4a, 0x14, 0xf4, 0x78, 0xe1, 0x62, 0xb7, 0xce, 0x50, 0x8c, 0x3c, 0x83, 0xd9, 0x8f, 0x68, 0x14, + 0x2a, 0x65, 0xcd, 0xef, 0x5a, 0x72, 0xfd, 0xc4, 0x24, 0xc9, 0x7f, 0xef, 0x8e, 0xa8, 0x56, 0xd5, + 0xd5, 0xee, 0x3a, 0xb7, 0x47, 0xdd, 0xeb, 0x4e, 0x5a, 0xca, 0x28, 0xb9, 0xbe, 0xf5, 0xfb, 0xd1, + 0x92, 0xf5, 0xc7, 0xd1, 0x92, 0xf5, 0xd7, 0xd1, 0x92, 0xf5, 0xf1, 0x5b, 0x67, 0xfb, 0x87, 0x17, + 0xe8, 0x1f, 0xa6, 0x85, 0xff, 0x62, 0x07, 0x25, 0xfd, 0x67, 0xec, 0x8d, 0x7f, 0x03, 0x00, 0x00, + 0xff, 0xff, 0x52, 0xa9, 0xe9, 0x17, 0x71, 0x0e, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1604,6 +1614,18 @@ func (m *RepoAccessQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.ForceHttpBasicAuth { + i-- + if m.ForceHttpBasicAuth { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } if len(m.GcpServiceAccountKey) > 0 { i -= len(m.GcpServiceAccountKey) copy(dAtA[i:], m.GcpServiceAccountKey) @@ -2049,6 +2071,9 @@ func (m *RepoAccessQuery) Size() (n int) { if l > 0 { n += 2 + l + sovRepository(uint64(l)) } + if m.ForceHttpBasicAuth { + n += 3 + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -3267,6 +3292,26 @@ func (m *RepoAccessQuery) Unmarshal(dAtA []byte) error { } m.GcpServiceAccountKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ForceHttpBasicAuth", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ForceHttpBasicAuth = bool(v != 0) default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) diff --git a/pkg/apiclient/settings/settings.pb.go b/pkg/apiclient/settings/settings.pb.go index 9fbbea430d549..b74110f9005d7 100644 --- a/pkg/apiclient/settings/settings.pb.go +++ b/pkg/apiclient/settings/settings.pb.go @@ -84,9 +84,10 @@ type Settings struct { GoogleAnalytics *GoogleAnalyticsConfig `protobuf:"bytes,7,opt,name=googleAnalytics,proto3" json:"googleAnalytics,omitempty"` KustomizeOptions *v1alpha1.KustomizeOptions `protobuf:"bytes,8,opt,name=kustomizeOptions,proto3" json:"kustomizeOptions,omitempty"` // Help settings - Help *Help `protobuf:"bytes,9,opt,name=help,proto3" json:"help,omitempty"` - Plugins []*Plugin `protobuf:"bytes,10,rep,name=plugins,proto3" json:"plugins,omitempty"` - UserLoginsDisabled bool `protobuf:"varint,11,opt,name=userLoginsDisabled,proto3" json:"userLoginsDisabled,omitempty"` + Help *Help `protobuf:"bytes,9,opt,name=help,proto3" json:"help,omitempty"` + Plugins []*Plugin `protobuf:"bytes,10,rep,name=plugins,proto3" json:"plugins,omitempty"` + UserLoginsDisabled bool `protobuf:"varint,11,opt,name=userLoginsDisabled,proto3" json:"userLoginsDisabled,omitempty"` + // Deprecated: use sidecar plugins instead. ConfigManagementPlugins []*v1alpha1.ConfigManagementPlugin `protobuf:"bytes,12,rep,name=configManagementPlugins,proto3" json:"configManagementPlugins,omitempty"` KustomizeVersions []string `protobuf:"bytes,13,rep,name=kustomizeVersions,proto3" json:"kustomizeVersions,omitempty"` UiCssURL string `protobuf:"bytes,14,opt,name=uiCssURL,proto3" json:"uiCssURL,omitempty"` @@ -361,6 +362,53 @@ func (m *GoogleAnalyticsConfig) GetAnonymizeUsers() bool { return false } +type SettingsPluginsResponse struct { + Plugins []*Plugin `protobuf:"bytes,1,rep,name=plugins,proto3" json:"plugins,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SettingsPluginsResponse) Reset() { *m = SettingsPluginsResponse{} } +func (m *SettingsPluginsResponse) String() string { return proto.CompactTextString(m) } +func (*SettingsPluginsResponse) ProtoMessage() {} +func (*SettingsPluginsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a480d494da040caa, []int{3} +} +func (m *SettingsPluginsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SettingsPluginsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SettingsPluginsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SettingsPluginsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SettingsPluginsResponse.Merge(m, src) +} +func (m *SettingsPluginsResponse) XXX_Size() int { + return m.Size() +} +func (m *SettingsPluginsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SettingsPluginsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SettingsPluginsResponse proto.InternalMessageInfo + +func (m *SettingsPluginsResponse) GetPlugins() []*Plugin { + if m != nil { + return m.Plugins + } + return nil +} + // Help settings type Help struct { // the URL for getting chat help, this will typically be your Slack channel for support @@ -378,7 +426,7 @@ func (m *Help) Reset() { *m = Help{} } func (m *Help) String() string { return proto.CompactTextString(m) } func (*Help) ProtoMessage() {} func (*Help) Descriptor() ([]byte, []int) { - return fileDescriptor_a480d494da040caa, []int{3} + return fileDescriptor_a480d494da040caa, []int{4} } func (m *Help) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -441,7 +489,7 @@ func (m *Plugin) Reset() { *m = Plugin{} } func (m *Plugin) String() string { return proto.CompactTextString(m) } func (*Plugin) ProtoMessage() {} func (*Plugin) Descriptor() ([]byte, []int) { - return fileDescriptor_a480d494da040caa, []int{4} + return fileDescriptor_a480d494da040caa, []int{5} } func (m *Plugin) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -488,7 +536,7 @@ func (m *DexConfig) Reset() { *m = DexConfig{} } func (m *DexConfig) String() string { return proto.CompactTextString(m) } func (*DexConfig) ProtoMessage() {} func (*DexConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_a480d494da040caa, []int{5} + return fileDescriptor_a480d494da040caa, []int{6} } func (m *DexConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -536,7 +584,7 @@ func (m *Connector) Reset() { *m = Connector{} } func (m *Connector) String() string { return proto.CompactTextString(m) } func (*Connector) ProtoMessage() {} func (*Connector) Descriptor() ([]byte, []int) { - return fileDescriptor_a480d494da040caa, []int{6} + return fileDescriptor_a480d494da040caa, []int{7} } func (m *Connector) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -580,22 +628,23 @@ func (m *Connector) GetType() string { } type OIDCConfig struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Issuer string `protobuf:"bytes,2,opt,name=issuer,proto3" json:"issuer,omitempty"` - ClientID string `protobuf:"bytes,3,opt,name=clientID,proto3" json:"clientID,omitempty"` - CLIClientID string `protobuf:"bytes,4,opt,name=cliClientID,proto3" json:"cliClientID,omitempty"` - Scopes []string `protobuf:"bytes,5,rep,name=scopes,proto3" json:"scopes,omitempty"` - IDTokenClaims map[string]*oidc.Claim `protobuf:"bytes,6,rep,name=idTokenClaims,proto3" json:"idTokenClaims,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Issuer string `protobuf:"bytes,2,opt,name=issuer,proto3" json:"issuer,omitempty"` + ClientID string `protobuf:"bytes,3,opt,name=clientID,proto3" json:"clientID,omitempty"` + CLIClientID string `protobuf:"bytes,4,opt,name=cliClientID,proto3" json:"cliClientID,omitempty"` + Scopes []string `protobuf:"bytes,5,rep,name=scopes,proto3" json:"scopes,omitempty"` + IDTokenClaims map[string]*oidc.Claim `protobuf:"bytes,6,rep,name=idTokenClaims,proto3" json:"idTokenClaims,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + EnablePKCEAuthentication bool `protobuf:"varint,7,opt,name=enablePKCEAuthentication,proto3" json:"enablePKCEAuthentication,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *OIDCConfig) Reset() { *m = OIDCConfig{} } func (m *OIDCConfig) String() string { return proto.CompactTextString(m) } func (*OIDCConfig) ProtoMessage() {} func (*OIDCConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_a480d494da040caa, []int{7} + return fileDescriptor_a480d494da040caa, []int{8} } func (m *OIDCConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -666,11 +715,19 @@ func (m *OIDCConfig) GetIDTokenClaims() map[string]*oidc.Claim { return nil } +func (m *OIDCConfig) GetEnablePKCEAuthentication() bool { + if m != nil { + return m.EnablePKCEAuthentication + } + return false +} + func init() { proto.RegisterType((*SettingsQuery)(nil), "cluster.SettingsQuery") proto.RegisterType((*Settings)(nil), "cluster.Settings") proto.RegisterMapType((map[string]*v1alpha1.ResourceOverride)(nil), "cluster.Settings.ResourceOverridesEntry") proto.RegisterType((*GoogleAnalyticsConfig)(nil), "cluster.GoogleAnalyticsConfig") + proto.RegisterType((*SettingsPluginsResponse)(nil), "cluster.SettingsPluginsResponse") proto.RegisterType((*Help)(nil), "cluster.Help") proto.RegisterMapType((map[string]string)(nil), "cluster.Help.BinaryUrlsEntry") proto.RegisterType((*Plugin)(nil), "cluster.Plugin") @@ -683,79 +740,83 @@ func init() { func init() { proto.RegisterFile("server/settings/settings.proto", fileDescriptor_a480d494da040caa) } var fileDescriptor_a480d494da040caa = []byte{ - // 1148 bytes of a gzipped FileDescriptorProto + // 1215 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x4f, 0x6f, 0x1b, 0x45, - 0x14, 0xd7, 0xd6, 0x69, 0x62, 0x3f, 0x37, 0x75, 0x32, 0x6d, 0xd3, 0xad, 0x55, 0x92, 0xe0, 0x43, - 0x65, 0x10, 0xac, 0x1b, 0x57, 0x08, 0x84, 0xa8, 0xa0, 0xb6, 0xab, 0xd6, 0xd4, 0x6d, 0xc3, 0xb6, - 0xe9, 0x01, 0x09, 0x55, 0x93, 0xdd, 0xc7, 0x66, 0xf1, 0x7a, 0x66, 0x35, 0x33, 0x6b, 0xea, 0x1e, - 0xb9, 0x71, 0xe1, 0x02, 0x9f, 0x85, 0x03, 0x9f, 0x80, 0x23, 0x12, 0xf7, 0x08, 0x59, 0x7c, 0x10, - 0x34, 0xb3, 0x7f, 0xb2, 0xb1, 0x5d, 0x40, 0xea, 0x6d, 0xe6, 0xf7, 0x7b, 0xff, 0xe6, 0xcd, 0x7b, - 0x33, 0x0f, 0x76, 0x25, 0x8a, 0x29, 0x8a, 0x8e, 0x44, 0xa5, 0x42, 0x16, 0xc8, 0x62, 0xe1, 0xc4, - 0x82, 0x2b, 0x4e, 0x36, 0xbc, 0x28, 0x91, 0x0a, 0x45, 0xf3, 0x6a, 0xc0, 0x03, 0x6e, 0xb0, 0x8e, - 0x5e, 0xa5, 0x74, 0xf3, 0x66, 0xc0, 0x79, 0x10, 0x61, 0x87, 0xc6, 0x61, 0x87, 0x32, 0xc6, 0x15, - 0x55, 0x21, 0x67, 0x99, 0x72, 0x73, 0x14, 0x84, 0xea, 0x24, 0x39, 0x76, 0x3c, 0x3e, 0xe9, 0x50, - 0x61, 0xd4, 0xbf, 0x33, 0x8b, 0x0f, 0x3d, 0xbf, 0x33, 0xed, 0x76, 0xe2, 0x71, 0xa0, 0x35, 0x65, - 0x87, 0xc6, 0x71, 0x14, 0x7a, 0x46, 0xb7, 0x33, 0x3d, 0xa0, 0x51, 0x7c, 0x42, 0x0f, 0x3a, 0x01, - 0x32, 0x14, 0x54, 0xa1, 0x9f, 0x59, 0xfb, 0xe2, 0x3f, 0xac, 0x2d, 0x9e, 0x84, 0x87, 0xbe, 0xd7, - 0xf1, 0x22, 0x1a, 0x4e, 0xb2, 0x78, 0x5a, 0x0d, 0xd8, 0x7c, 0x96, 0xb1, 0x5f, 0x25, 0x28, 0x66, - 0xad, 0x5f, 0xeb, 0x50, 0xcd, 0x11, 0x72, 0x03, 0x2a, 0x89, 0x88, 0x6c, 0x6b, 0xdf, 0x6a, 0xd7, - 0x7a, 0x1b, 0xf3, 0xd3, 0xbd, 0xca, 0x91, 0x3b, 0x72, 0x35, 0x46, 0x6e, 0x43, 0xcd, 0xc7, 0x57, - 0x7d, 0xce, 0xbe, 0x0d, 0x03, 0xfb, 0xc2, 0xbe, 0xd5, 0xae, 0x77, 0x89, 0x93, 0x65, 0xc6, 0x19, - 0xe4, 0x8c, 0x7b, 0x26, 0x44, 0xfa, 0x00, 0xda, 0x7f, 0xa6, 0x52, 0x31, 0x2a, 0x57, 0x0a, 0x95, - 0xa7, 0xc3, 0x41, 0x3f, 0xa5, 0x7a, 0x97, 0xe7, 0xa7, 0x7b, 0x70, 0xb6, 0x77, 0x4b, 0x6a, 0x64, - 0x1f, 0xea, 0x34, 0x8e, 0x47, 0xf4, 0x18, 0xa3, 0x47, 0x38, 0xb3, 0xd7, 0x74, 0x64, 0x6e, 0x19, - 0x22, 0x2f, 0x60, 0x5b, 0xa0, 0xe4, 0x89, 0xf0, 0xf0, 0xe9, 0x14, 0x85, 0x08, 0x7d, 0x94, 0xf6, - 0xc5, 0xfd, 0x4a, 0xbb, 0xde, 0x6d, 0x17, 0xde, 0xf2, 0x13, 0x3a, 0xee, 0xa2, 0xe8, 0x7d, 0xa6, - 0xc4, 0xcc, 0x5d, 0x36, 0x41, 0x1c, 0x20, 0x52, 0x51, 0x95, 0xc8, 0x1e, 0xf5, 0x03, 0xbc, 0xcf, - 0xe8, 0x71, 0x84, 0xbe, 0xbd, 0xbe, 0x6f, 0xb5, 0xab, 0xee, 0x0a, 0x86, 0x3c, 0x84, 0x46, 0x5a, - 0x09, 0xf7, 0x18, 0x8d, 0x66, 0x2a, 0xf4, 0xa4, 0xbd, 0x61, 0xce, 0xbc, 0x5b, 0x44, 0xf1, 0xe0, - 0x3c, 0x9f, 0x1d, 0x77, 0x51, 0x8d, 0xbc, 0x86, 0xad, 0x71, 0x22, 0x15, 0x9f, 0x84, 0xaf, 0xf1, - 0x69, 0x6c, 0xaa, 0xc9, 0xae, 0x1a, 0x53, 0x4f, 0x9c, 0xb3, 0x02, 0x70, 0xf2, 0x02, 0x30, 0x8b, - 0x97, 0x9e, 0xef, 0x4c, 0xbb, 0x4e, 0x3c, 0x0e, 0x1c, 0x5d, 0x4e, 0x4e, 0xa9, 0x9c, 0x9c, 0xbc, - 0x9c, 0x9c, 0x47, 0x0b, 0x56, 0xdd, 0x25, 0x3f, 0xe4, 0x5d, 0x58, 0x3b, 0xc1, 0x28, 0xb6, 0x6b, - 0xc6, 0xdf, 0x66, 0x11, 0xfa, 0x43, 0x8c, 0x62, 0xd7, 0x50, 0xe4, 0x3d, 0xd8, 0x88, 0xa3, 0x24, - 0x08, 0x99, 0xb4, 0xc1, 0xa4, 0xb9, 0x51, 0x48, 0x1d, 0x1a, 0xdc, 0xcd, 0x79, 0x9d, 0xc3, 0x44, - 0xa2, 0x18, 0x71, 0xbd, 0x1b, 0x84, 0x32, 0xcd, 0x61, 0x3d, 0xcd, 0xe1, 0x32, 0x43, 0x7e, 0xb2, - 0xe0, 0xba, 0x67, 0xb2, 0xf2, 0x98, 0x32, 0x1a, 0xe0, 0x04, 0x99, 0x3a, 0xcc, 0x7c, 0x5d, 0x32, - 0xbe, 0x9e, 0xbf, 0x5d, 0x06, 0xfa, 0x2b, 0x8d, 0xbb, 0x6f, 0x72, 0x4a, 0x3e, 0x80, 0xed, 0x22, - 0x45, 0x2f, 0x50, 0x48, 0x73, 0x17, 0x9b, 0xfb, 0x95, 0x76, 0xcd, 0x5d, 0x26, 0x48, 0x13, 0xaa, - 0x49, 0xd8, 0x97, 0xf2, 0xc8, 0x1d, 0xd9, 0x97, 0x4d, 0xa5, 0x16, 0x7b, 0xd2, 0x86, 0x46, 0x12, - 0xf6, 0x28, 0x63, 0x28, 0xfa, 0x9c, 0x29, 0x64, 0xca, 0x6e, 0x18, 0x91, 0x45, 0x58, 0x97, 0x7c, - 0x0e, 0x69, 0x43, 0x5b, 0x69, 0xc9, 0x97, 0x20, 0x6d, 0x2b, 0xa6, 0x52, 0x7e, 0xcf, 0x85, 0x7f, - 0x48, 0x95, 0x42, 0xc1, 0xec, 0xed, 0xd4, 0xd6, 0x02, 0x4c, 0x6e, 0xc1, 0x65, 0x25, 0xa8, 0x37, - 0x0e, 0x59, 0xf0, 0x18, 0xd5, 0x09, 0xf7, 0x6d, 0x62, 0x04, 0x17, 0x50, 0x7d, 0xce, 0xdc, 0xc1, - 0x21, 0x8a, 0x09, 0x65, 0x3a, 0xbe, 0x2b, 0xe6, 0x9e, 0x96, 0x09, 0xf2, 0x3e, 0x6c, 0x15, 0x20, - 0x97, 0xa1, 0x4e, 0xb1, 0x7d, 0xd5, 0xd8, 0x5d, 0xc2, 0x17, 0xda, 0xc8, 0xe5, 0x5c, 0x1d, 0x89, - 0xc8, 0xbe, 0x66, 0xa4, 0x57, 0x30, 0xfa, 0xf4, 0xf8, 0x0a, 0xbd, 0xbc, 0xdf, 0x76, 0x4c, 0x0c, - 0x65, 0x88, 0xdc, 0x86, 0x2b, 0x1e, 0x67, 0x4a, 0xf0, 0x28, 0x42, 0xf1, 0x84, 0x4e, 0x50, 0xc6, - 0xd4, 0x43, 0xfb, 0xba, 0x31, 0xb9, 0x8a, 0x22, 0x9f, 0xc1, 0x0d, 0x1a, 0xc7, 0x72, 0xc8, 0xee, - 0xb1, 0x59, 0x81, 0xe6, 0x1e, 0x6c, 0xe3, 0xe1, 0xcd, 0x02, 0xcd, 0x5f, 0x2c, 0xd8, 0x59, 0xfd, - 0x6c, 0x90, 0x2d, 0xa8, 0x8c, 0x71, 0x96, 0xbe, 0x97, 0xae, 0x5e, 0x12, 0x1f, 0x2e, 0x4e, 0x69, - 0x94, 0x60, 0xf6, 0x44, 0xbe, 0x65, 0xc3, 0x2e, 0xba, 0x75, 0x53, 0xe3, 0x9f, 0x5e, 0xf8, 0xc4, - 0x6a, 0xbd, 0x84, 0x6b, 0x2b, 0xdf, 0x13, 0xb2, 0x0b, 0x90, 0xdf, 0xee, 0x70, 0x90, 0xc5, 0x56, - 0x42, 0x74, 0x4d, 0x50, 0xc6, 0xd9, 0x4c, 0x97, 0xee, 0x91, 0x44, 0x21, 0x4d, 0xac, 0x55, 0x77, - 0x01, 0x6d, 0xfd, 0x66, 0xc1, 0x9a, 0x6e, 0x7b, 0x62, 0xc3, 0x86, 0x77, 0x42, 0xcd, 0xbd, 0xa5, - 0xd6, 0xf2, 0xad, 0x2e, 0x78, 0xbd, 0x7c, 0x8e, 0xaf, 0x94, 0x31, 0x52, 0x73, 0x8b, 0x3d, 0xb9, - 0x0b, 0x70, 0x1c, 0x32, 0x2a, 0x66, 0x47, 0x22, 0x92, 0x76, 0xc5, 0x74, 0xef, 0x3b, 0xe7, 0xde, - 0x13, 0xa7, 0x57, 0xf0, 0xe9, 0x2b, 0x5c, 0x52, 0x68, 0xde, 0x85, 0xc6, 0x02, 0xbd, 0x22, 0xdb, - 0x57, 0xcb, 0xd9, 0xae, 0x95, 0xb3, 0x73, 0x13, 0xd6, 0xd3, 0x1e, 0x26, 0x04, 0xd6, 0x18, 0x9d, - 0x60, 0xa6, 0x66, 0xd6, 0xad, 0xcf, 0xa1, 0x56, 0x7c, 0x59, 0xa4, 0x0b, 0xe0, 0x71, 0xc6, 0xd0, - 0x53, 0x5c, 0x48, 0xdb, 0x32, 0x81, 0x9e, 0x7d, 0x6d, 0xfd, 0x9c, 0x72, 0x4b, 0x52, 0xad, 0x3b, - 0x50, 0x2b, 0x88, 0x55, 0x1e, 0x34, 0xa6, 0x66, 0x71, 0x1e, 0x98, 0x59, 0xb7, 0x7e, 0xac, 0x40, - 0xe9, 0x9b, 0x5b, 0xa9, 0xb6, 0x03, 0xeb, 0xa1, 0x94, 0x09, 0x8a, 0x4c, 0x31, 0xdb, 0x91, 0x36, - 0x54, 0xbd, 0x28, 0x44, 0xa6, 0x86, 0x03, 0xf3, 0x93, 0xd6, 0x7a, 0x97, 0xe6, 0xa7, 0x7b, 0xd5, - 0x7e, 0x86, 0xb9, 0x05, 0x4b, 0x0e, 0xa0, 0xee, 0x45, 0x61, 0x4e, 0xa4, 0x1f, 0x66, 0xaf, 0x31, - 0x3f, 0xdd, 0xab, 0xf7, 0x47, 0xc3, 0x42, 0xbe, 0x2c, 0xa3, 0x9d, 0x4a, 0x8f, 0xc7, 0xd9, 0xb7, - 0x59, 0x73, 0xb3, 0x1d, 0x79, 0x09, 0x9b, 0xa1, 0xff, 0x9c, 0x8f, 0x91, 0xf5, 0xcd, 0x08, 0x61, - 0xaf, 0x9b, 0xdc, 0xdc, 0x5a, 0xf1, 0x87, 0x3b, 0xc3, 0xb2, 0xa0, 0xb9, 0xae, 0xde, 0xf6, 0xfc, - 0x74, 0x6f, 0x73, 0x38, 0x28, 0xe1, 0xee, 0x79, 0x7b, 0xcd, 0x19, 0x90, 0x65, 0xbd, 0x15, 0xd7, - 0xfc, 0xf8, 0x7c, 0x53, 0x7d, 0xfc, 0xaf, 0x4d, 0x95, 0xce, 0x40, 0x4e, 0x31, 0xc4, 0xe9, 0x61, - 0xc2, 0x31, 0xf6, 0x4b, 0xf5, 0xd1, 0xfd, 0x06, 0x1a, 0xf9, 0x4c, 0xf0, 0x0c, 0xc5, 0x34, 0xf4, - 0x90, 0x7c, 0x09, 0x95, 0x07, 0xa8, 0xc8, 0xce, 0xd2, 0xd0, 0x60, 0x06, 0xa5, 0xe6, 0xf6, 0x12, - 0xde, 0xb2, 0x7f, 0xf8, 0xf3, 0xef, 0x9f, 0x2f, 0x10, 0xb2, 0x65, 0x86, 0xbf, 0xe9, 0x41, 0x31, - 0x78, 0xf5, 0xfa, 0xbf, 0xcf, 0x77, 0xad, 0x3f, 0xe6, 0xbb, 0xd6, 0x5f, 0xf3, 0x5d, 0xeb, 0xeb, - 0x8f, 0xfe, 0xdf, 0x10, 0x98, 0xde, 0x61, 0x61, 0xe4, 0x78, 0xdd, 0x8c, 0x6c, 0x77, 0xfe, 0x09, - 0x00, 0x00, 0xff, 0xff, 0xf0, 0x33, 0x47, 0xc2, 0xa1, 0x0a, 0x00, 0x00, + 0x14, 0xd7, 0xd6, 0x69, 0x62, 0x3f, 0x37, 0x75, 0x32, 0x6d, 0xd3, 0xad, 0x55, 0x12, 0xe3, 0x43, + 0x65, 0x10, 0xac, 0x9b, 0x54, 0x08, 0x54, 0x51, 0x41, 0x6d, 0x57, 0xad, 0x69, 0xda, 0x86, 0x69, + 0xd3, 0x03, 0x97, 0x6a, 0xb2, 0x7e, 0xac, 0x97, 0xac, 0x67, 0x56, 0x33, 0xb3, 0xa6, 0xee, 0x91, + 0x0f, 0xc0, 0x05, 0x3e, 0x0b, 0x07, 0xee, 0x08, 0x8e, 0x48, 0xdc, 0x23, 0x64, 0xf1, 0x41, 0xd0, + 0xce, 0xfe, 0xc9, 0x66, 0xed, 0x14, 0xa4, 0xde, 0x66, 0x7e, 0xbf, 0xf7, 0x6f, 0xde, 0xbc, 0x37, + 0xf3, 0x60, 0x5b, 0xa1, 0x9c, 0xa2, 0xec, 0x2a, 0xd4, 0xda, 0xe7, 0x9e, 0xca, 0x17, 0x4e, 0x28, + 0x85, 0x16, 0x64, 0xcd, 0x0d, 0x22, 0xa5, 0x51, 0x36, 0xaf, 0x7a, 0xc2, 0x13, 0x06, 0xeb, 0xc6, + 0xab, 0x84, 0x6e, 0xde, 0xf4, 0x84, 0xf0, 0x02, 0xec, 0xb2, 0xd0, 0xef, 0x32, 0xce, 0x85, 0x66, + 0xda, 0x17, 0x3c, 0x55, 0x6e, 0xee, 0x7b, 0xbe, 0x1e, 0x47, 0x47, 0x8e, 0x2b, 0x26, 0x5d, 0x26, + 0x8d, 0xfa, 0x77, 0x66, 0xf1, 0xb1, 0x3b, 0xea, 0x4e, 0xf7, 0xba, 0xe1, 0xb1, 0x17, 0x6b, 0xaa, + 0x2e, 0x0b, 0xc3, 0xc0, 0x77, 0x8d, 0x6e, 0x77, 0xba, 0xcb, 0x82, 0x70, 0xcc, 0x76, 0xbb, 0x1e, + 0x72, 0x94, 0x4c, 0xe3, 0x28, 0xb5, 0xf6, 0xe5, 0x7f, 0x58, 0x2b, 0x9f, 0x44, 0xf8, 0x23, 0xb7, + 0xeb, 0x06, 0xcc, 0x9f, 0xa4, 0xf1, 0xb4, 0x1b, 0xb0, 0xfe, 0x3c, 0x65, 0xbf, 0x8e, 0x50, 0xce, + 0xda, 0xbf, 0xd4, 0xa1, 0x9a, 0x21, 0xe4, 0x06, 0x54, 0x22, 0x19, 0xd8, 0x56, 0xcb, 0xea, 0xd4, + 0x7a, 0x6b, 0xf3, 0x93, 0x9d, 0xca, 0x21, 0xdd, 0xa7, 0x31, 0x46, 0x6e, 0x43, 0x6d, 0x84, 0xaf, + 0xfb, 0x82, 0x7f, 0xeb, 0x7b, 0xf6, 0x85, 0x96, 0xd5, 0xa9, 0xef, 0x11, 0x27, 0xcd, 0x8c, 0x33, + 0xc8, 0x18, 0x7a, 0x2a, 0x44, 0xfa, 0x00, 0xb1, 0xff, 0x54, 0xa5, 0x62, 0x54, 0xae, 0xe4, 0x2a, + 0xcf, 0x86, 0x83, 0x7e, 0x42, 0xf5, 0x2e, 0xcf, 0x4f, 0x76, 0xe0, 0x74, 0x4f, 0x0b, 0x6a, 0xa4, + 0x05, 0x75, 0x16, 0x86, 0xfb, 0xec, 0x08, 0x83, 0xc7, 0x38, 0xb3, 0x57, 0xe2, 0xc8, 0x68, 0x11, + 0x22, 0x2f, 0x61, 0x53, 0xa2, 0x12, 0x91, 0x74, 0xf1, 0xd9, 0x14, 0xa5, 0xf4, 0x47, 0xa8, 0xec, + 0x8b, 0xad, 0x4a, 0xa7, 0xbe, 0xd7, 0xc9, 0xbd, 0x65, 0x27, 0x74, 0x68, 0x59, 0xf4, 0x01, 0xd7, + 0x72, 0x46, 0x17, 0x4d, 0x10, 0x07, 0x88, 0xd2, 0x4c, 0x47, 0xaa, 0xc7, 0x46, 0x1e, 0x3e, 0xe0, + 0xec, 0x28, 0xc0, 0x91, 0xbd, 0xda, 0xb2, 0x3a, 0x55, 0xba, 0x84, 0x21, 0x8f, 0xa0, 0x91, 0x54, + 0xc2, 0x7d, 0xce, 0x82, 0x99, 0xf6, 0x5d, 0x65, 0xaf, 0x99, 0x33, 0x6f, 0xe7, 0x51, 0x3c, 0x3c, + 0xcb, 0xa7, 0xc7, 0x2d, 0xab, 0x91, 0x37, 0xb0, 0x71, 0x1c, 0x29, 0x2d, 0x26, 0xfe, 0x1b, 0x7c, + 0x16, 0x9a, 0x6a, 0xb2, 0xab, 0xc6, 0xd4, 0x53, 0xe7, 0xb4, 0x00, 0x9c, 0xac, 0x00, 0xcc, 0xe2, + 0x95, 0x3b, 0x72, 0xa6, 0x7b, 0x4e, 0x78, 0xec, 0x39, 0x71, 0x39, 0x39, 0x85, 0x72, 0x72, 0xb2, + 0x72, 0x72, 0x1e, 0x97, 0xac, 0xd2, 0x05, 0x3f, 0xe4, 0x7d, 0x58, 0x19, 0x63, 0x10, 0xda, 0x35, + 0xe3, 0x6f, 0x3d, 0x0f, 0xfd, 0x11, 0x06, 0x21, 0x35, 0x14, 0xf9, 0x00, 0xd6, 0xc2, 0x20, 0xf2, + 0x7c, 0xae, 0x6c, 0x30, 0x69, 0x6e, 0xe4, 0x52, 0x07, 0x06, 0xa7, 0x19, 0x1f, 0xe7, 0x30, 0x52, + 0x28, 0xf7, 0x45, 0xbc, 0x1b, 0xf8, 0x2a, 0xc9, 0x61, 0x3d, 0xc9, 0xe1, 0x22, 0x43, 0x7e, 0xb4, + 0xe0, 0xba, 0x6b, 0xb2, 0xf2, 0x84, 0x71, 0xe6, 0xe1, 0x04, 0xb9, 0x3e, 0x48, 0x7d, 0x5d, 0x32, + 0xbe, 0x5e, 0xbc, 0x5b, 0x06, 0xfa, 0x4b, 0x8d, 0xd3, 0xf3, 0x9c, 0x92, 0x8f, 0x60, 0x33, 0x4f, + 0xd1, 0x4b, 0x94, 0xca, 0xdc, 0xc5, 0x7a, 0xab, 0xd2, 0xa9, 0xd1, 0x45, 0x82, 0x34, 0xa1, 0x1a, + 0xf9, 0x7d, 0xa5, 0x0e, 0xe9, 0xbe, 0x7d, 0xd9, 0x54, 0x6a, 0xbe, 0x27, 0x1d, 0x68, 0x44, 0x7e, + 0x8f, 0x71, 0x8e, 0xb2, 0x2f, 0xb8, 0x46, 0xae, 0xed, 0x86, 0x11, 0x29, 0xc3, 0x71, 0xc9, 0x67, + 0x50, 0x6c, 0x68, 0x23, 0x29, 0xf9, 0x02, 0x14, 0xdb, 0x0a, 0x99, 0x52, 0xdf, 0x0b, 0x39, 0x3a, + 0x60, 0x5a, 0xa3, 0xe4, 0xf6, 0x66, 0x62, 0xab, 0x04, 0x93, 0x5b, 0x70, 0x59, 0x4b, 0xe6, 0x1e, + 0xfb, 0xdc, 0x7b, 0x82, 0x7a, 0x2c, 0x46, 0x36, 0x31, 0x82, 0x25, 0x34, 0x3e, 0x67, 0xe6, 0xe0, + 0x00, 0xe5, 0x84, 0xf1, 0x38, 0xbe, 0x2b, 0xe6, 0x9e, 0x16, 0x09, 0xf2, 0x21, 0x6c, 0xe4, 0xa0, + 0x50, 0x7e, 0x9c, 0x62, 0xfb, 0xaa, 0xb1, 0xbb, 0x80, 0x97, 0xda, 0x88, 0x0a, 0xa1, 0x0f, 0x65, + 0x60, 0x5f, 0x33, 0xd2, 0x4b, 0x98, 0xf8, 0xf4, 0xf8, 0x1a, 0xdd, 0xac, 0xdf, 0xb6, 0x4c, 0x0c, + 0x45, 0x88, 0xdc, 0x86, 0x2b, 0xae, 0xe0, 0x5a, 0x8a, 0x20, 0x40, 0xf9, 0x94, 0x4d, 0x50, 0x85, + 0xcc, 0x45, 0xfb, 0xba, 0x31, 0xb9, 0x8c, 0x22, 0x9f, 0xc3, 0x0d, 0x16, 0x86, 0x6a, 0xc8, 0xef, + 0xf3, 0x59, 0x8e, 0x66, 0x1e, 0x6c, 0xe3, 0xe1, 0x7c, 0x81, 0xe6, 0xcf, 0x16, 0x6c, 0x2d, 0x7f, + 0x36, 0xc8, 0x06, 0x54, 0x8e, 0x71, 0x96, 0xbc, 0x97, 0x34, 0x5e, 0x92, 0x11, 0x5c, 0x9c, 0xb2, + 0x20, 0xc2, 0xf4, 0x89, 0x7c, 0xc7, 0x86, 0x2d, 0xbb, 0xa5, 0x89, 0xf1, 0xbb, 0x17, 0x3e, 0xb3, + 0xda, 0xaf, 0xe0, 0xda, 0xd2, 0xf7, 0x84, 0x6c, 0x03, 0x64, 0xb7, 0x3b, 0x1c, 0xa4, 0xb1, 0x15, + 0x90, 0xb8, 0x26, 0x18, 0x17, 0x7c, 0x16, 0x97, 0xee, 0xa1, 0x42, 0xa9, 0x4c, 0xac, 0x55, 0x5a, + 0x42, 0xdb, 0x03, 0xb8, 0x9e, 0x3d, 0x9b, 0x69, 0x3b, 0x50, 0x54, 0xa1, 0xe0, 0x0a, 0x8b, 0x4f, + 0x80, 0xf5, 0xf6, 0x27, 0xa0, 0xfd, 0xab, 0x05, 0x2b, 0xf1, 0xe3, 0x41, 0x6c, 0x58, 0x73, 0xc7, + 0xcc, 0xdc, 0x7e, 0x12, 0x53, 0xb6, 0x8d, 0xdb, 0x26, 0x5e, 0xbe, 0xc0, 0xd7, 0xda, 0x84, 0x52, + 0xa3, 0xf9, 0x9e, 0xdc, 0x03, 0x38, 0xf2, 0x39, 0x93, 0xb3, 0x43, 0x19, 0x28, 0xbb, 0x62, 0x9c, + 0xbd, 0x77, 0xe6, 0x55, 0x72, 0x7a, 0x39, 0x9f, 0xbc, 0xe5, 0x05, 0x85, 0xe6, 0x3d, 0x68, 0x94, + 0xe8, 0x25, 0x77, 0x76, 0xb5, 0x78, 0x67, 0xb5, 0x62, 0x8e, 0x6f, 0xc2, 0x6a, 0x72, 0x1e, 0x42, + 0x60, 0x85, 0xb3, 0x09, 0xa6, 0x6a, 0x66, 0xdd, 0xfe, 0x02, 0x6a, 0xf9, 0xc7, 0x47, 0xf6, 0x00, + 0x5c, 0xc1, 0x39, 0xba, 0x5a, 0xc8, 0x2c, 0x2b, 0xa7, 0x1f, 0x64, 0x3f, 0xa3, 0x68, 0x41, 0xaa, + 0x7d, 0x07, 0x6a, 0x39, 0xb1, 0xcc, 0x43, 0x8c, 0xe9, 0x59, 0x98, 0x05, 0x66, 0xd6, 0xed, 0xdf, + 0x2a, 0x50, 0xf8, 0x2c, 0x97, 0xaa, 0x6d, 0xc1, 0xaa, 0xaf, 0x54, 0x84, 0x32, 0x55, 0x4c, 0x77, + 0xa4, 0x03, 0x55, 0x37, 0xf0, 0x91, 0xeb, 0xe1, 0xc0, 0xfc, 0xc7, 0xb5, 0xde, 0xa5, 0xf9, 0xc9, + 0x4e, 0xb5, 0x9f, 0x62, 0x34, 0x67, 0xc9, 0x2e, 0xd4, 0xdd, 0xc0, 0xcf, 0x88, 0xe4, 0xdb, 0xed, + 0x35, 0xe6, 0x27, 0x3b, 0xf5, 0xfe, 0xfe, 0x30, 0x97, 0x2f, 0xca, 0xc4, 0x4e, 0x95, 0x2b, 0xc2, + 0xf4, 0xf3, 0xad, 0xd1, 0x74, 0x47, 0x5e, 0xc1, 0xba, 0x3f, 0x7a, 0x21, 0x8e, 0x91, 0xf7, 0xcd, + 0x20, 0x62, 0xaf, 0x9a, 0xdc, 0xdc, 0x5a, 0x32, 0x09, 0x38, 0xc3, 0xa2, 0xa0, 0xb9, 0xae, 0xde, + 0xe6, 0xfc, 0x64, 0x67, 0x7d, 0x38, 0x28, 0xe0, 0xf4, 0xac, 0x3d, 0x72, 0x17, 0x6c, 0x34, 0xad, + 0x7a, 0xf0, 0xb8, 0xff, 0xe0, 0x7e, 0xa4, 0xc7, 0xc8, 0x75, 0xda, 0x49, 0xe6, 0x07, 0xae, 0xd2, + 0x73, 0xf9, 0xe6, 0x0c, 0xc8, 0xa2, 0xcf, 0x25, 0x25, 0xf2, 0xe4, 0x6c, 0x5b, 0x7f, 0xfa, 0xd6, + 0xb6, 0x4e, 0xa6, 0x30, 0x27, 0x1f, 0x23, 0xe3, 0x71, 0xc6, 0x31, 0xf6, 0x0b, 0xb5, 0xb5, 0xf7, + 0xbb, 0x05, 0x8d, 0xac, 0xbf, 0x9e, 0xa3, 0x9c, 0xfa, 0x2e, 0x92, 0xaf, 0xa0, 0xf2, 0x10, 0x35, + 0xd9, 0x5a, 0x98, 0x5b, 0xcc, 0xac, 0xd6, 0xdc, 0x5c, 0xc0, 0xdb, 0xf6, 0x0f, 0x7f, 0xfd, 0xf3, + 0xd3, 0x05, 0x42, 0x36, 0xcc, 0xfc, 0x39, 0xdd, 0xcd, 0x67, 0x3f, 0x32, 0x06, 0x78, 0x88, 0xf9, + 0x47, 0x76, 0x9e, 0xc9, 0xd6, 0x02, 0x5e, 0xea, 0xf5, 0x76, 0xcb, 0x78, 0x68, 0x12, 0xbb, 0xec, + 0xa1, 0x9b, 0xb6, 0x78, 0xaf, 0xff, 0xc7, 0x7c, 0xdb, 0xfa, 0x73, 0xbe, 0x6d, 0xfd, 0x3d, 0xdf, + 0xb6, 0xbe, 0xf9, 0xe4, 0xff, 0x4d, 0xbc, 0x49, 0xa9, 0xe5, 0xc6, 0x8e, 0x56, 0xcd, 0x7c, 0x7a, + 0xe7, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf1, 0x4f, 0xb0, 0x2d, 0x8e, 0x0b, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -772,6 +833,8 @@ const _ = grpc.SupportPackageIsVersion4 type SettingsServiceClient interface { // Get returns Argo CD settings Get(ctx context.Context, in *SettingsQuery, opts ...grpc.CallOption) (*Settings, error) + // Get returns Argo CD plugins + GetPlugins(ctx context.Context, in *SettingsQuery, opts ...grpc.CallOption) (*SettingsPluginsResponse, error) } type settingsServiceClient struct { @@ -791,10 +854,21 @@ func (c *settingsServiceClient) Get(ctx context.Context, in *SettingsQuery, opts return out, nil } +func (c *settingsServiceClient) GetPlugins(ctx context.Context, in *SettingsQuery, opts ...grpc.CallOption) (*SettingsPluginsResponse, error) { + out := new(SettingsPluginsResponse) + err := c.cc.Invoke(ctx, "/cluster.SettingsService/GetPlugins", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // SettingsServiceServer is the server API for SettingsService service. type SettingsServiceServer interface { // Get returns Argo CD settings Get(context.Context, *SettingsQuery) (*Settings, error) + // Get returns Argo CD plugins + GetPlugins(context.Context, *SettingsQuery) (*SettingsPluginsResponse, error) } // UnimplementedSettingsServiceServer can be embedded to have forward compatible implementations. @@ -804,6 +878,9 @@ type UnimplementedSettingsServiceServer struct { func (*UnimplementedSettingsServiceServer) Get(ctx context.Context, req *SettingsQuery) (*Settings, error) { return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") } +func (*UnimplementedSettingsServiceServer) GetPlugins(ctx context.Context, req *SettingsQuery) (*SettingsPluginsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPlugins not implemented") +} func RegisterSettingsServiceServer(s *grpc.Server, srv SettingsServiceServer) { s.RegisterService(&_SettingsService_serviceDesc, srv) @@ -827,6 +904,24 @@ func _SettingsService_Get_Handler(srv interface{}, ctx context.Context, dec func return interceptor(ctx, in, info, handler) } +func _SettingsService_GetPlugins_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SettingsQuery) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SettingsServiceServer).GetPlugins(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cluster.SettingsService/GetPlugins", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SettingsServiceServer).GetPlugins(ctx, req.(*SettingsQuery)) + } + return interceptor(ctx, in, info, handler) +} + var _SettingsService_serviceDesc = grpc.ServiceDesc{ ServiceName: "cluster.SettingsService", HandlerType: (*SettingsServiceServer)(nil), @@ -835,6 +930,10 @@ var _SettingsService_serviceDesc = grpc.ServiceDesc{ MethodName: "Get", Handler: _SettingsService_Get_Handler, }, + { + MethodName: "GetPlugins", + Handler: _SettingsService_GetPlugins_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "server/settings/settings.proto", @@ -1199,6 +1298,47 @@ func (m *GoogleAnalyticsConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *SettingsPluginsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SettingsPluginsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SettingsPluginsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Plugins) > 0 { + for iNdEx := len(m.Plugins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Plugins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSettings(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *Help) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1399,6 +1539,16 @@ func (m *OIDCConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.EnablePKCEAuthentication { + i-- + if m.EnablePKCEAuthentication { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } if len(m.IDTokenClaims) > 0 { for k := range m.IDTokenClaims { v := m.IDTokenClaims[k] @@ -1625,6 +1775,24 @@ func (m *GoogleAnalyticsConfig) Size() (n int) { return n } +func (m *SettingsPluginsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Plugins) > 0 { + for _, e := range m.Plugins { + l = e.Size() + n += 1 + l + sovSettings(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *Help) Size() (n int) { if m == nil { return 0 @@ -1748,6 +1916,9 @@ func (m *OIDCConfig) Size() (n int) { n += mapEntrySize + 1 + sovSettings(uint64(mapEntrySize)) } } + if m.EnablePKCEAuthentication { + n += 2 + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -2794,6 +2965,91 @@ func (m *GoogleAnalyticsConfig) Unmarshal(dAtA []byte) error { } return nil } +func (m *SettingsPluginsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSettings + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SettingsPluginsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SettingsPluginsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Plugins", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSettings + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSettings + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSettings + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Plugins = append(m.Plugins, &Plugin{}) + if err := m.Plugins[len(m.Plugins)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSettings(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSettings + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *Help) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -3637,6 +3893,26 @@ func (m *OIDCConfig) Unmarshal(dAtA []byte) error { } m.IDTokenClaims[mapkey] = mapvalue iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EnablePKCEAuthentication", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSettings + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.EnablePKCEAuthentication = bool(v != 0) default: iNdEx = preIndex skippy, err := skipSettings(dAtA[iNdEx:]) diff --git a/pkg/apiclient/settings/settings.pb.gw.go b/pkg/apiclient/settings/settings.pb.gw.go index 67f7bd5db1547..238ec7604fe88 100644 --- a/pkg/apiclient/settings/settings.pb.gw.go +++ b/pkg/apiclient/settings/settings.pb.gw.go @@ -51,6 +51,24 @@ func local_request_SettingsService_Get_0(ctx context.Context, marshaler runtime. } +func request_SettingsService_GetPlugins_0(ctx context.Context, marshaler runtime.Marshaler, client SettingsServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SettingsQuery + var metadata runtime.ServerMetadata + + msg, err := client.GetPlugins(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_SettingsService_GetPlugins_0(ctx context.Context, marshaler runtime.Marshaler, server SettingsServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SettingsQuery + var metadata runtime.ServerMetadata + + msg, err := server.GetPlugins(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterSettingsServiceHandlerServer registers the http handlers for service SettingsService to "mux". // UnaryRPC :call SettingsServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -80,6 +98,29 @@ func RegisterSettingsServiceHandlerServer(ctx context.Context, mux *runtime.Serv }) + mux.Handle("GET", pattern_SettingsService_GetPlugins_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_SettingsService_GetPlugins_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_SettingsService_GetPlugins_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -141,13 +182,37 @@ func RegisterSettingsServiceHandlerClient(ctx context.Context, mux *runtime.Serv }) + mux.Handle("GET", pattern_SettingsService_GetPlugins_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_SettingsService_GetPlugins_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_SettingsService_GetPlugins_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } var ( pattern_SettingsService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "settings"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_SettingsService_GetPlugins_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "settings", "plugins"}, "", runtime.AssumeColonVerbOpt(true))) ) var ( forward_SettingsService_Get_0 = runtime.ForwardResponseMessage + + forward_SettingsService_GetPlugins_0 = runtime.ForwardResponseMessage ) diff --git a/pkg/apiclient/version/version.pb.go b/pkg/apiclient/version/version.pb.go index 35474d63124ed..0b58bf4a6cede 100644 --- a/pkg/apiclient/version/version.pb.go +++ b/pkg/apiclient/version/version.pb.go @@ -46,6 +46,7 @@ type VersionMessage struct { HelmVersion string `protobuf:"bytes,11,opt,name=HelmVersion,proto3" json:"HelmVersion,omitempty"` KubectlVersion string `protobuf:"bytes,12,opt,name=KubectlVersion,proto3" json:"KubectlVersion,omitempty"` JsonnetVersion string `protobuf:"bytes,13,opt,name=JsonnetVersion,proto3" json:"JsonnetVersion,omitempty"` + ExtraBuildInfo string `protobuf:"bytes,14,opt,name=ExtraBuildInfo,proto3" json:"ExtraBuildInfo,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -168,6 +169,13 @@ func (m *VersionMessage) GetJsonnetVersion() string { return "" } +func (m *VersionMessage) GetExtraBuildInfo() string { + if m != nil { + return m.ExtraBuildInfo + } + return "" +} + func init() { proto.RegisterType((*VersionMessage)(nil), "version.VersionMessage") } @@ -175,32 +183,33 @@ func init() { func init() { proto.RegisterFile("server/version/version.proto", fileDescriptor_8be80977d07a4107) } var fileDescriptor_8be80977d07a4107 = []byte{ - // 399 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x92, 0xdf, 0x6a, 0xdb, 0x30, - 0x18, 0xc5, 0x71, 0xb2, 0xe5, 0x8f, 0x92, 0x85, 0x21, 0x46, 0x66, 0xbc, 0x10, 0x42, 0x2e, 0xc6, - 0x18, 0xcc, 0x86, 0x6c, 0x4f, 0x90, 0x6c, 0x64, 0x2c, 0x0c, 0xc2, 0x32, 0x7a, 0xd1, 0x3b, 0xd9, - 0xf9, 0xe2, 0xaa, 0xb5, 0xfc, 0x19, 0x59, 0x36, 0xb4, 0x97, 0x7d, 0x85, 0x42, 0x9f, 0xa9, 0x97, - 0x85, 0xbe, 0x40, 0x09, 0x7d, 0x90, 0x62, 0xd9, 0x72, 0x9b, 0xf6, 0xca, 0x3a, 0xe7, 0xfc, 0x7c, - 0x10, 0x1c, 0x91, 0x51, 0x0a, 0x32, 0x07, 0xe9, 0xe5, 0x20, 0x53, 0x8e, 0xb1, 0xf9, 0xba, 0x89, - 0x44, 0x85, 0xb4, 0x5d, 0x49, 0x67, 0x14, 0x22, 0x86, 0x11, 0x78, 0x2c, 0xe1, 0x1e, 0x8b, 0x63, - 0x54, 0x4c, 0x71, 0x8c, 0xd3, 0x12, 0x73, 0x3e, 0x55, 0xa9, 0x56, 0x7e, 0xb6, 0xf3, 0x40, 0x24, - 0xea, 0xbc, 0x0c, 0xa7, 0xd7, 0x4d, 0x32, 0x38, 0x2a, 0x6b, 0xfe, 0x42, 0x9a, 0xb2, 0x10, 0xa8, - 0x4d, 0xda, 0x95, 0x63, 0x5b, 0x13, 0xeb, 0x4b, 0xf7, 0x9f, 0x91, 0x74, 0x44, 0xba, 0xf3, 0x8c, - 0x47, 0xdb, 0x9f, 0x4c, 0x81, 0xdd, 0xd0, 0xd9, 0x93, 0x51, 0xa4, 0x4b, 0xae, 0x16, 0x28, 0x04, - 0x57, 0x76, 0xb3, 0x4c, 0x6b, 0x83, 0x0e, 0x49, 0x6b, 0xc9, 0xd5, 0x7f, 0x16, 0xda, 0x6f, 0x74, - 0x54, 0x29, 0x3a, 0x25, 0xfd, 0xe2, 0x24, 0x01, 0x36, 0xaa, 0xa8, 0x7d, 0xab, 0xd3, 0x03, 0x4f, - 0x37, 0xa3, 0xb9, 0x53, 0xab, 0x6a, 0x36, 0x06, 0x75, 0x48, 0x67, 0x81, 0x22, 0xe1, 0x11, 0x48, - 0xbb, 0xad, 0xc3, 0x5a, 0x17, 0xd9, 0x3a, 0x62, 0x6a, 0x87, 0x52, 0xd8, 0x9d, 0x32, 0x33, 0x9a, - 0x7e, 0x25, 0xef, 0x57, 0x59, 0xaa, 0x50, 0xf0, 0x0b, 0x30, 0xe5, 0x44, 0x33, 0xaf, 0x7c, 0x3a, - 0x21, 0xbd, 0xdf, 0x10, 0x09, 0x83, 0xf5, 0x34, 0xf6, 0xdc, 0xa2, 0x9f, 0xc9, 0x60, 0x95, 0xf9, - 0x10, 0xa8, 0xc8, 0x40, 0x7d, 0x0d, 0xbd, 0x70, 0x0b, 0xee, 0x4f, 0x8a, 0x71, 0x0c, 0xca, 0x70, - 0xef, 0x4a, 0xee, 0xd0, 0x9d, 0xf9, 0xf5, 0x2e, 0x1b, 0x90, 0x39, 0x0f, 0x80, 0xae, 0xeb, 0x5d, - 0xe8, 0xd0, 0x2d, 0x37, 0x75, 0xcd, 0xa6, 0xee, 0xaf, 0x62, 0x53, 0xe7, 0xa3, 0x6b, 0x5e, 0xc8, - 0xe1, 0xa6, 0xd3, 0x0f, 0x97, 0x77, 0x0f, 0x57, 0x8d, 0x01, 0xed, 0xeb, 0x37, 0x52, 0x41, 0xf3, - 0xf9, 0xcd, 0x7e, 0x6c, 0xdd, 0xee, 0xc7, 0xd6, 0xfd, 0x7e, 0x6c, 0x1d, 0xff, 0x08, 0xb9, 0x3a, - 0xc9, 0x7c, 0x37, 0x40, 0xe1, 0x31, 0x19, 0x62, 0x22, 0xf1, 0x54, 0x1f, 0xbe, 0x05, 0x5b, 0x2f, - 0x9f, 0x79, 0xc9, 0x59, 0x58, 0xfc, 0x1d, 0x44, 0x1c, 0x62, 0x65, 0x3a, 0xfc, 0x96, 0xbe, 0xc2, - 0xf7, 0xc7, 0x00, 0x00, 0x00, 0xff, 0xff, 0x9e, 0x97, 0xec, 0x06, 0xab, 0x02, 0x00, 0x00, + // 415 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x92, 0xd1, 0x6a, 0x14, 0x31, + 0x18, 0x85, 0x99, 0x56, 0x77, 0xdb, 0x74, 0x5d, 0x24, 0x48, 0x0d, 0xeb, 0xb2, 0x94, 0xbd, 0x10, + 0x11, 0x9c, 0x81, 0xea, 0x13, 0x6c, 0x2d, 0x55, 0x8b, 0x50, 0xac, 0x78, 0xe1, 0x5d, 0x66, 0xfa, + 0xef, 0x18, 0x9d, 0xe4, 0x1f, 0x32, 0xff, 0x2c, 0xea, 0xa5, 0xaf, 0xe0, 0xfb, 0x78, 0xed, 0xa5, + 0xe0, 0x0b, 0xc8, 0xe2, 0x83, 0x48, 0x92, 0xc9, 0xe8, 0xd8, 0xab, 0xcd, 0x39, 0xe7, 0xdb, 0x43, + 0x98, 0x13, 0x36, 0x6f, 0xc0, 0x6e, 0xc0, 0x66, 0x1b, 0xb0, 0x8d, 0x42, 0x13, 0x7f, 0xd3, 0xda, + 0x22, 0x21, 0x1f, 0x77, 0x72, 0x36, 0x2f, 0x11, 0xcb, 0x0a, 0x32, 0x59, 0xab, 0x4c, 0x1a, 0x83, + 0x24, 0x49, 0xa1, 0x69, 0x02, 0x36, 0xbb, 0xd7, 0xa5, 0x5e, 0xe5, 0xed, 0x3a, 0x03, 0x5d, 0xd3, + 0xa7, 0x10, 0x2e, 0xbf, 0xed, 0xb2, 0xe9, 0x9b, 0x50, 0xf3, 0x12, 0x9a, 0x46, 0x96, 0xc0, 0x05, + 0x1b, 0x77, 0x8e, 0x48, 0x8e, 0x92, 0x07, 0xfb, 0xaf, 0xa2, 0xe4, 0x73, 0xb6, 0xbf, 0x6a, 0x55, + 0x75, 0xf5, 0x54, 0x12, 0x88, 0x1d, 0x9f, 0xfd, 0x35, 0x5c, 0x7a, 0xa6, 0xe8, 0x04, 0xb5, 0x56, + 0x24, 0x76, 0x43, 0xda, 0x1b, 0xfc, 0x90, 0x8d, 0xce, 0x14, 0xbd, 0x96, 0xa5, 0xb8, 0xe1, 0xa3, + 0x4e, 0xf1, 0x25, 0x9b, 0xb8, 0x93, 0x05, 0xb8, 0x24, 0x57, 0x7b, 0xd3, 0xa7, 0x03, 0xcf, 0x37, + 0x63, 0xbc, 0xd3, 0xa8, 0x6b, 0x8e, 0x06, 0x9f, 0xb1, 0xbd, 0x13, 0xd4, 0xb5, 0xaa, 0xc0, 0x8a, + 0xb1, 0x0f, 0x7b, 0xed, 0xb2, 0x8b, 0x4a, 0xd2, 0x1a, 0xad, 0x16, 0x7b, 0x21, 0x8b, 0x9a, 0x3f, + 0x64, 0xb7, 0xcf, 0xdb, 0x86, 0x50, 0xab, 0xcf, 0x10, 0xcb, 0x99, 0x67, 0xae, 0xf9, 0xfc, 0x88, + 0x1d, 0x3c, 0x83, 0x4a, 0x47, 0xec, 0xc0, 0x63, 0xff, 0x5a, 0xfc, 0x3e, 0x9b, 0x9e, 0xb7, 0x39, + 0x14, 0x54, 0x45, 0x68, 0xe2, 0xa1, 0xff, 0x5c, 0xc7, 0xbd, 0x68, 0xd0, 0x18, 0xa0, 0xc8, 0xdd, + 0x0a, 0xdc, 0xd0, 0x75, 0xdc, 0xe9, 0x47, 0xb2, 0xd2, 0x7f, 0xdf, 0xe7, 0x66, 0x8d, 0x62, 0x1a, + 0xb8, 0xa1, 0x7b, 0x9c, 0xf7, 0xfb, 0x5d, 0x82, 0xdd, 0xa8, 0x02, 0xf8, 0x45, 0xbf, 0x1f, 0x3f, + 0x4c, 0xc3, 0xf6, 0x69, 0xdc, 0x3e, 0x3d, 0x75, 0xdb, 0xcf, 0xee, 0xa6, 0xf1, 0x25, 0x0d, 0xb7, + 0x5f, 0xde, 0xf9, 0xf2, 0xf3, 0xf7, 0xd7, 0x9d, 0x29, 0x9f, 0xf8, 0xb7, 0xd4, 0x41, 0xab, 0xd5, + 0xf7, 0xed, 0x22, 0xf9, 0xb1, 0x5d, 0x24, 0xbf, 0xb6, 0x8b, 0xe4, 0xed, 0x93, 0x52, 0xd1, 0xbb, + 0x36, 0x4f, 0x0b, 0xd4, 0x99, 0xb4, 0x25, 0xd6, 0x16, 0xdf, 0xfb, 0xc3, 0xa3, 0xe2, 0x2a, 0xdb, + 0x1c, 0x67, 0xf5, 0x87, 0xd2, 0xfd, 0xbb, 0xa8, 0x14, 0x18, 0x8a, 0x1d, 0xf9, 0xc8, 0x5f, 0xe1, + 0xf1, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd2, 0x21, 0xc0, 0xd1, 0xd3, 0x02, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -309,6 +318,13 @@ func (m *VersionMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.ExtraBuildInfo) > 0 { + i -= len(m.ExtraBuildInfo) + copy(dAtA[i:], m.ExtraBuildInfo) + i = encodeVarintVersion(dAtA, i, uint64(len(m.ExtraBuildInfo))) + i-- + dAtA[i] = 0x72 + } if len(m.JsonnetVersion) > 0 { i -= len(m.JsonnetVersion) copy(dAtA[i:], m.JsonnetVersion) @@ -461,6 +477,10 @@ func (m *VersionMessage) Size() (n int) { if l > 0 { n += 1 + l + sovVersion(uint64(l)) } + l = len(m.ExtraBuildInfo) + if l > 0 { + n += 1 + l + sovVersion(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -886,6 +906,38 @@ func (m *VersionMessage) Unmarshal(dAtA []byte) error { } m.JsonnetVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtraBuildInfo", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVersion + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthVersion + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthVersion + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExtraBuildInfo = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipVersion(dAtA[iNdEx:]) diff --git a/pkg/apis/api-rules/violation_exceptions.list b/pkg/apis/api-rules/violation_exceptions.list index 754988e464117..2b0f2e90d00a9 100644 --- a/pkg/apis/api-rules/violation_exceptions.list +++ b/pkg/apis/api-rules/violation_exceptions.list @@ -8,9 +8,14 @@ API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/ap API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,SourceNamespaces API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,SourceRepos API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationMatchExpression,Values +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationPreservedFields,Annotations +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationPreservedFields,Labels +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSetResourceIgnoreDifferences,JQPathExpressions +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSetResourceIgnoreDifferences,JSONPointers API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSetRolloutStep,MatchExpressions API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSetRolloutStrategy,Steps API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSetSpec,Generators +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSetSpec,GoTemplateOptions API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSetStatus,ApplicationStatus API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSetStatus,Conditions API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSetTemplateMeta,Finalizers @@ -20,8 +25,7 @@ API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/ap API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourceJsonnet,ExtVars API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourceJsonnet,Libs API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourceJsonnet,TLAs -API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourcePluginParameter,Array -API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSpec,IgnoreDifferences +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourceKustomize,Components API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSpec,Info API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationStatus,Conditions API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationStatus,Resources @@ -31,6 +35,7 @@ API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/ap API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationTree,Hosts API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationTree,Nodes API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationTree,OrphanedNodes +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ChartDetails,Maintainers API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,Cluster,Namespaces API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ClusterInfo,APIVersions API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,Command,Args @@ -47,6 +52,7 @@ API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/ap API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,MergeGenerator,MergeKeys API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,NestedMergeGenerator,MergeKeys API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,Operation,Info +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,OptionalArray,Array API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,OrphanedResourcesMonitorSettings,Ignore API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,OverrideIgnoreDiff,JQPathExpressions API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,OverrideIgnoreDiff,JSONPointers @@ -55,6 +61,7 @@ API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/ap API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ProjectRole,JWTTokens API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ProjectRole,Policies API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,PullRequestGenerator,Filters +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,PullRequestGeneratorAzureDevOps,Labels API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,PullRequestGeneratorGitLab,Labels API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,PullRequestGeneratorGithub,Labels API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,RepositoryCertificate,CertData @@ -73,6 +80,7 @@ API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/ap API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,RevisionHistory,Revisions API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,RevisionMetadata,Tags API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,SCMProviderGenerator,Filters +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,SCMProviderGeneratorAWSCodeCommit,TagFilters API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,SCMProviderGeneratorFilter,PathsDoNotExist API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,SCMProviderGeneratorFilter,PathsExist API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,SyncOperation,Manifests @@ -96,6 +104,7 @@ API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/applicat API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,JWTToken,IssuedAt API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,KustomizeOptions,BinaryPath API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,KustomizeOptions,BuildOptions +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,PullRequestGenerator,AzureDevOps API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,PullRequestGenerator,GitLab API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,RefTarget,Chart API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,RefTarget,Repo @@ -112,6 +121,7 @@ API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/applicat API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceOverride,Actions API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceOverride,HealthLua API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceOverride,IgnoreDifferences +API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceOverride,IgnoreResourceUpdates API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceOverride,KnownTypeFields API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ResourceOverride,UseOpenLibs API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,objectMeta,Name diff --git a/pkg/apis/application/register.go b/pkg/apis/application/register.go index 944fe60c7f09b..66f712c619450 100644 --- a/pkg/apis/application/register.go +++ b/pkg/apis/application/register.go @@ -19,7 +19,7 @@ const ( AppProjectFullName string = AppProjectPlural + "." + Group // ApplicationSet constants - ApplicationSetKind string = "Applicationset" + ApplicationSetKind string = "ApplicationSet" ApplicationSetSingular string = "applicationset" ApplicationSetShortName string = "appset" ApplicationSetPlural string = "applicationsets" diff --git a/pkg/apis/application/v1alpha1/application_defaults.go b/pkg/apis/application/v1alpha1/application_defaults.go index 2bc9b1bd0d744..ad8112af8c88d 100644 --- a/pkg/apis/application/v1alpha1/application_defaults.go +++ b/pkg/apis/application/v1alpha1/application_defaults.go @@ -9,6 +9,9 @@ const ( // ResourcesFinalizerName is the finalizer value which we inject to finalize deletion of an application ResourcesFinalizerName string = "resources-finalizer.argocd.argoproj.io" + // PostDeleteFinalizerName is the finalizer that controls post-delete hooks execution + PostDeleteFinalizerName string = "post-delete-finalizer.argocd.argoproj.io" + // ForegroundPropagationPolicyFinalizer is the finalizer we inject to delete application with foreground propagation policy ForegroundPropagationPolicyFinalizer string = "resources-finalizer.argocd.argoproj.io/foreground" diff --git a/pkg/apis/application/v1alpha1/applicationset_types.go b/pkg/apis/application/v1alpha1/applicationset_types.go index 56b1bcfad98be..389f421fed400 100644 --- a/pkg/apis/application/v1alpha1/applicationset_types.go +++ b/pkg/apis/application/v1alpha1/applicationset_types.go @@ -22,6 +22,7 @@ import ( "sort" "github.com/argoproj/argo-cd/v2/common" + "github.com/argoproj/argo-cd/v2/util/security" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -48,17 +49,28 @@ type ApplicationSet struct { } // RBACName formats fully qualified application name for RBAC check. -func (a *ApplicationSet) RBACName() string { - return fmt.Sprintf("%s/%s", a.Spec.Template.Spec.GetProject(), a.ObjectMeta.Name) +func (a *ApplicationSet) RBACName(defaultNS string) string { + return security.RBACName(defaultNS, a.Spec.Template.Spec.GetProject(), a.Namespace, a.Name) } // ApplicationSetSpec represents a class of application set state. type ApplicationSetSpec struct { - GoTemplate bool `json:"goTemplate,omitempty" protobuf:"bytes,1,name=goTemplate"` - Generators []ApplicationSetGenerator `json:"generators" protobuf:"bytes,2,name=generators"` - Template ApplicationSetTemplate `json:"template" protobuf:"bytes,3,name=template"` - SyncPolicy *ApplicationSetSyncPolicy `json:"syncPolicy,omitempty" protobuf:"bytes,4,name=syncPolicy"` - Strategy *ApplicationSetStrategy `json:"strategy,omitempty" protobuf:"bytes,5,opt,name=strategy"` + GoTemplate bool `json:"goTemplate,omitempty" protobuf:"bytes,1,name=goTemplate"` + Generators []ApplicationSetGenerator `json:"generators" protobuf:"bytes,2,name=generators"` + Template ApplicationSetTemplate `json:"template" protobuf:"bytes,3,name=template"` + SyncPolicy *ApplicationSetSyncPolicy `json:"syncPolicy,omitempty" protobuf:"bytes,4,name=syncPolicy"` + Strategy *ApplicationSetStrategy `json:"strategy,omitempty" protobuf:"bytes,5,opt,name=strategy"` + PreservedFields *ApplicationPreservedFields `json:"preservedFields,omitempty" protobuf:"bytes,6,opt,name=preservedFields"` + GoTemplateOptions []string `json:"goTemplateOptions,omitempty" protobuf:"bytes,7,opt,name=goTemplateOptions"` + // ApplyNestedSelectors enables selectors defined within the generators of two level-nested matrix or merge generators + ApplyNestedSelectors bool `json:"applyNestedSelectors,omitempty" protobuf:"bytes,8,name=applyNestedSelectors"` + IgnoreApplicationDifferences ApplicationSetIgnoreDifferences `json:"ignoreApplicationDifferences,omitempty" protobuf:"bytes,9,name=ignoreApplicationDifferences"` + TemplatePatch *string `json:"templatePatch,omitempty" protobuf:"bytes,10,name=templatePatch"` +} + +type ApplicationPreservedFields struct { + Annotations []string `json:"annotations,omitempty" protobuf:"bytes,1,name=annotations"` + Labels []string `json:"labels,omitempty" protobuf:"bytes,2,name=labels"` } // ApplicationSetStrategy configures how generated Applications are updated in sequence. @@ -82,11 +94,72 @@ type ApplicationMatchExpression struct { Values []string `json:"values,omitempty" protobuf:"bytes,3,opt,name=values"` } +// ApplicationsSyncPolicy representation +// "create-only" means applications are only created. If the generator's result contains update, applications won't be updated +// "create-update" means applications are only created/Updated. If the generator's result contains update, applications will be updated, but not deleted +// "create-delete" means applications are only created/deleted. If the generator's result contains update, applications won't be updated, if it results in deleted applications, the applications will be deleted +// "sync" means create/update/deleted. If the generator's result contains update, applications will be updated, if it results in deleted applications, the applications will be deleted +// If no ApplicationsSyncPolicy is defined, it defaults it to sync +type ApplicationsSyncPolicy string + +// sync / create-only / create-update / create-delete +const ( + ApplicationsSyncPolicyCreateOnly ApplicationsSyncPolicy = "create-only" + ApplicationsSyncPolicyCreateUpdate ApplicationsSyncPolicy = "create-update" + ApplicationsSyncPolicyCreateDelete ApplicationsSyncPolicy = "create-delete" + ApplicationsSyncPolicySync ApplicationsSyncPolicy = "sync" +) + +func (s ApplicationsSyncPolicy) AllowUpdate() bool { + return s == ApplicationsSyncPolicyCreateUpdate || s == ApplicationsSyncPolicySync +} + +func (s ApplicationsSyncPolicy) AllowDelete() bool { + return s == ApplicationsSyncPolicySync || s == ApplicationsSyncPolicyCreateDelete +} + // ApplicationSetSyncPolicy configures how generated Applications will relate to their // ApplicationSet. type ApplicationSetSyncPolicy struct { // PreserveResourcesOnDeletion will preserve resources on deletion. If PreserveResourcesOnDeletion is set to true, these Applications will not be deleted. PreserveResourcesOnDeletion bool `json:"preserveResourcesOnDeletion,omitempty" protobuf:"bytes,1,name=syncPolicy"` + // ApplicationsSync represents the policy applied on the generated applications. Possible values are create-only, create-update, create-delete, sync + // +kubebuilder:validation:Optional + // +kubebuilder:validation:Enum=create-only;create-update;create-delete;sync + ApplicationsSync *ApplicationsSyncPolicy `json:"applicationsSync,omitempty" protobuf:"bytes,2,opt,name=applicationsSync,casttype=ApplicationsSyncPolicy"` +} + +// ApplicationSetIgnoreDifferences configures how the ApplicationSet controller will ignore differences in live +// applications when applying changes from generated applications. +type ApplicationSetIgnoreDifferences []ApplicationSetResourceIgnoreDifferences + +func (a ApplicationSetIgnoreDifferences) ToApplicationIgnoreDifferences() []ResourceIgnoreDifferences { + var result []ResourceIgnoreDifferences + for _, item := range a { + result = append(result, item.ToApplicationResourceIgnoreDifferences()) + } + return result +} + +// ApplicationSetResourceIgnoreDifferences configures how the ApplicationSet controller will ignore differences in live +// applications when applying changes from generated applications. +type ApplicationSetResourceIgnoreDifferences struct { + // Name is the name of the application to ignore differences for. If not specified, the rule applies to all applications. + Name string `json:"name,omitempty" protobuf:"bytes,1,name=name"` + // JSONPointers is a list of JSON pointers to fields to ignore differences for. + JSONPointers []string `json:"jsonPointers,omitempty" protobuf:"bytes,2,name=jsonPointers"` + // JQPathExpressions is a list of JQ path expressions to fields to ignore differences for. + JQPathExpressions []string `json:"jqPathExpressions,omitempty" protobuf:"bytes,3,name=jqExpressions"` +} + +func (a *ApplicationSetResourceIgnoreDifferences) ToApplicationResourceIgnoreDifferences() ResourceIgnoreDifferences { + return ResourceIgnoreDifferences{ + Kind: ApplicationSchemaGroupVersionKind.Kind, + Group: ApplicationSchemaGroupVersionKind.Group, + Name: a.Name, + JSONPointers: a.JSONPointers, + JQPathExpressions: a.JQPathExpressions, + } } // ApplicationSetTemplate represents argocd ApplicationSpec @@ -118,6 +191,8 @@ type ApplicationSetGenerator struct { // Selector allows to post-filter all generator. Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,9,name=selector"` + + Plugin *PluginGenerator `json:"plugin,omitempty" protobuf:"bytes,10,name=plugin"` } // ApplicationSetNestedGenerator represents a generator nested within a combination-type generator (MatrixGenerator or @@ -138,6 +213,8 @@ type ApplicationSetNestedGenerator struct { // Selector allows to post-filter all generator. Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,9,name=selector"` + + Plugin *PluginGenerator `json:"plugin,omitempty" protobuf:"bytes,10,name=plugin"` } type ApplicationSetNestedGenerators []ApplicationSetNestedGenerator @@ -153,6 +230,10 @@ type ApplicationSetTerminalGenerator struct { SCMProvider *SCMProviderGenerator `json:"scmProvider,omitempty" protobuf:"bytes,4,name=scmProvider"` ClusterDecisionResource *DuckTypeGenerator `json:"clusterDecisionResource,omitempty" protobuf:"bytes,5,name=clusterDecisionResource"` PullRequest *PullRequestGenerator `json:"pullRequest,omitempty" protobuf:"bytes,6,name=pullRequest"` + Plugin *PluginGenerator `json:"plugin,omitempty" protobuf:"bytes,7,name=plugin"` + + // Selector allows to post-filter all generator. + Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,8,name=selector"` } type ApplicationSetTerminalGenerators []ApplicationSetTerminalGenerator @@ -170,6 +251,8 @@ func (g ApplicationSetTerminalGenerators) toApplicationSetNestedGenerators() []A SCMProvider: terminalGenerator.SCMProvider, ClusterDecisionResource: terminalGenerator.ClusterDecisionResource, PullRequest: terminalGenerator.PullRequest, + Plugin: terminalGenerator.Plugin, + Selector: terminalGenerator.Selector, } } return nestedGenerators @@ -177,8 +260,10 @@ func (g ApplicationSetTerminalGenerators) toApplicationSetNestedGenerators() []A // ListGenerator include items info type ListGenerator struct { - Elements []apiextensionsv1.JSON `json:"elements" protobuf:"bytes,1,name=elements"` - Template ApplicationSetTemplate `json:"template,omitempty" protobuf:"bytes,2,name=template"` + // +kubebuilder:validation:Optional + Elements []apiextensionsv1.JSON `json:"elements" protobuf:"bytes,1,name=elements"` + Template ApplicationSetTemplate `json:"template,omitempty" protobuf:"bytes,2,name=template"` + ElementsYaml string `json:"elementsYaml,omitempty" protobuf:"bytes,3,opt,name=elementsYaml"` } // MatrixGenerator generates the cartesian product of two sets of parameters. The parameters are defined by two nested @@ -314,6 +399,9 @@ type GitGenerator struct { RequeueAfterSeconds *int64 `json:"requeueAfterSeconds,omitempty" protobuf:"bytes,5,name=requeueAfterSeconds"` Template ApplicationSetTemplate `json:"template,omitempty" protobuf:"bytes,6,name=template"` PathParamPrefix string `json:"pathParamPrefix,omitempty" protobuf:"bytes,7,name=pathParamPrefix"` + + // Values contains key/value pairs which are passed directly as parameters to the template + Values map[string]string `json:"values,omitempty" protobuf:"bytes,8,name=values"` } type GitDirectoryGeneratorItem struct { @@ -342,6 +430,26 @@ type SCMProviderGenerator struct { // Standard parameters. RequeueAfterSeconds *int64 `json:"requeueAfterSeconds,omitempty" protobuf:"varint,9,opt,name=requeueAfterSeconds"` Template ApplicationSetTemplate `json:"template,omitempty" protobuf:"bytes,10,opt,name=template"` + + // Values contains key/value pairs which are passed directly as parameters to the template + Values map[string]string `json:"values,omitempty" protobuf:"bytes,11,name=values"` + AWSCodeCommit *SCMProviderGeneratorAWSCodeCommit `json:"awsCodeCommit,omitempty" protobuf:"bytes,12,opt,name=awsCodeCommit"` + // If you add a new SCM provider, update CustomApiUrl below. +} + +func (g *SCMProviderGenerator) CustomApiUrl() string { + if g.Github != nil { + return g.Github.API + } else if g.Gitlab != nil { + return g.Gitlab.API + } else if g.Gitea != nil { + return g.Gitea.API + } else if g.BitbucketServer != nil { + return g.BitbucketServer.API + } else if g.AzureDevOps != nil { + return g.AzureDevOps.API + } + return "" } // SCMProviderGeneratorGitea defines a connection info specific to Gitea. @@ -384,6 +492,16 @@ type SCMProviderGeneratorGitlab struct { TokenRef *SecretRef `json:"tokenRef,omitempty" protobuf:"bytes,4,opt,name=tokenRef"` // Scan all branches instead of just the default branch. AllBranches bool `json:"allBranches,omitempty" protobuf:"varint,5,opt,name=allBranches"` + // Skips validating the SCM provider's TLS certificate - useful for self-signed certificates.; default: false + Insecure bool `json:"insecure,omitempty" protobuf:"varint,6,opt,name=insecure"` + // When recursing through subgroups, also include shared Projects (true) or scan only the subgroups under same path (false). Defaults to "true" + IncludeSharedProjects *bool `json:"includeSharedProjects,omitempty" protobuf:"varint,7,opt,name=includeSharedProjects"` + // Filter repos list based on Gitlab Topic. + Topic string `json:"topic,omitempty" protobuf:"bytes,8,opt,name=topic"` +} + +func (s *SCMProviderGeneratorGitlab) WillIncludeSharedProjects() bool { + return s.IncludeSharedProjects == nil || *s.IncludeSharedProjects } // SCMProviderGeneratorBitbucket defines connection info specific to Bitbucket Cloud (API version 2). @@ -424,6 +542,25 @@ type SCMProviderGeneratorAzureDevOps struct { AllBranches bool `json:"allBranches,omitempty" protobuf:"varint,9,opt,name=allBranches"` } +type TagFilter struct { + Key string `json:"key" protobuf:"bytes,1,opt,name=key"` + Value string `json:"value,omitempty" protobuf:"bytes,2,opt,name=value"` +} + +// SCMProviderGeneratorAWSCodeCommit defines connection info specific to AWS CodeCommit. +type SCMProviderGeneratorAWSCodeCommit struct { + // TagFilters provides the tag filter(s) for repo discovery + TagFilters []*TagFilter `json:"tagFilters,omitempty" protobuf:"bytes,1,opt,name=tagFilters"` + // Role provides the AWS IAM role to assume, for cross-account repo discovery + // if not provided, AppSet controller will use its pod/node identity to discover. + Role string `json:"role,omitempty" protobuf:"bytes,2,opt,name=role"` + // Region provides the AWS region to discover repos. + // if not provided, AppSet controller will infer the current region from environment. + Region string `json:"region,omitempty" protobuf:"bytes,3,opt,name=region"` + // Scan all branches instead of just the default branch. + AllBranches bool `json:"allBranches,omitempty" protobuf:"varint,4,opt,name=allBranches"` +} + // SCMProviderGeneratorFilter is a single repository filter. // If multiple filter types are set on a single struct, they will be AND'd together. All filters must // pass for a repo to be included. @@ -450,11 +587,37 @@ type PullRequestGenerator struct { // Filters for which pull requests should be considered. Filters []PullRequestGeneratorFilter `json:"filters,omitempty" protobuf:"bytes,5,rep,name=filters"` // Standard parameters. - RequeueAfterSeconds *int64 `json:"requeueAfterSeconds,omitempty" protobuf:"varint,6,opt,name=requeueAfterSeconds"` - Template ApplicationSetTemplate `json:"template,omitempty" protobuf:"bytes,7,opt,name=template"` + RequeueAfterSeconds *int64 `json:"requeueAfterSeconds,omitempty" protobuf:"varint,6,opt,name=requeueAfterSeconds"` + Template ApplicationSetTemplate `json:"template,omitempty" protobuf:"bytes,7,opt,name=template"` + Bitbucket *PullRequestGeneratorBitbucket `json:"bitbucket,omitempty" protobuf:"bytes,8,opt,name=bitbucket"` + // Additional provider to use and config for it. + AzureDevOps *PullRequestGeneratorAzureDevOps `json:"azuredevops,omitempty" protobuf:"bytes,9,opt,name=azuredevops"` + // If you add a new SCM provider, update CustomApiUrl below. } -// PullRequestGenerator defines connection info specific to Gitea. +func (p *PullRequestGenerator) CustomApiUrl() string { + if p.Github != nil { + return p.Github.API + } + if p.GitLab != nil { + return p.GitLab.API + } + if p.Gitea != nil { + return p.Gitea.API + } + if p.BitbucketServer != nil { + return p.BitbucketServer.API + } + if p.Bitbucket != nil { + return p.Bitbucket.API + } + if p.AzureDevOps != nil { + return p.AzureDevOps.API + } + return "" +} + +// PullRequestGeneratorGitea defines connection info specific to Gitea. type PullRequestGeneratorGitea struct { // Gitea org or user to scan. Required. Owner string `json:"owner" protobuf:"bytes,1,opt,name=owner"` @@ -468,6 +631,22 @@ type PullRequestGeneratorGitea struct { Insecure bool `json:"insecure,omitempty" protobuf:"varint,5,opt,name=insecure"` } +// PullRequestGeneratorAzureDevOps defines connection info specific to AzureDevOps. +type PullRequestGeneratorAzureDevOps struct { + // Azure DevOps org to scan. Required. + Organization string `json:"organization" protobuf:"bytes,1,opt,name=organization"` + // Azure DevOps project name to scan. Required. + Project string `json:"project" protobuf:"bytes,2,opt,name=project"` + // Azure DevOps repo name to scan. Required. + Repo string `json:"repo" protobuf:"bytes,3,opt,name=repo"` + // The Azure DevOps API URL to talk to. If blank, use https://dev.azure.com/. + API string `json:"api,omitempty" protobuf:"bytes,4,opt,name=api"` + // Authentication token reference. + TokenRef *SecretRef `json:"tokenRef,omitempty" protobuf:"bytes,5,opt,name=tokenRef"` + // Labels is used to filter the PRs that you want to target + Labels []string `json:"labels,omitempty" protobuf:"bytes,6,rep,name=labels"` +} + // PullRequestGenerator defines connection info specific to GitHub. type PullRequestGeneratorGithub struct { // GitHub org or user to scan. Required. @@ -496,9 +675,11 @@ type PullRequestGeneratorGitLab struct { Labels []string `json:"labels,omitempty" protobuf:"bytes,4,rep,name=labels"` // PullRequestState is an additional MRs filter to get only those with a certain state. Default: "" (all states) PullRequestState string `json:"pullRequestState,omitempty" protobuf:"bytes,5,rep,name=pullRequestState"` + // Skips validating the SCM provider's TLS certificate - useful for self-signed certificates.; default: false + Insecure bool `json:"insecure,omitempty" protobuf:"varint,6,opt,name=insecure"` } -// PullRequestGenerator defines connection info specific to BitbucketServer. +// PullRequestGeneratorBitbucketServer defines connection info specific to BitbucketServer. type PullRequestGeneratorBitbucketServer struct { // Project to scan. Required. Project string `json:"project" protobuf:"bytes,1,opt,name=project"` @@ -510,6 +691,26 @@ type PullRequestGeneratorBitbucketServer struct { BasicAuth *BasicAuthBitbucketServer `json:"basicAuth,omitempty" protobuf:"bytes,4,opt,name=basicAuth"` } +// PullRequestGeneratorBitbucket defines connection info specific to Bitbucket. +type PullRequestGeneratorBitbucket struct { + // Workspace to scan. Required. + Owner string `json:"owner" protobuf:"bytes,1,opt,name=owner"` + // Repo name to scan. Required. + Repo string `json:"repo" protobuf:"bytes,2,opt,name=repo"` + // The Bitbucket REST API URL to talk to. If blank, uses https://api.bitbucket.org/2.0. + API string `json:"api,omitempty" protobuf:"bytes,3,opt,name=api"` + // Credentials for Basic auth + BasicAuth *BasicAuthBitbucketServer `json:"basicAuth,omitempty" protobuf:"bytes,4,opt,name=basicAuth"` + // Credentials for AppToken (Bearer auth) + BearerToken *BearerTokenBitbucketCloud `json:"bearerToken,omitempty" protobuf:"bytes,5,opt,name=bearerToken"` +} + +// BearerTokenBitbucketCloud defines the Bearer token for BitBucket AppToken auth. +type BearerTokenBitbucketCloud struct { + // Password (or personal access token) reference. + TokenRef *SecretRef `json:"tokenRef" protobuf:"bytes,1,opt,name=tokenRef"` +} + // BasicAuthBitbucketServer defines the username/(password or personal access token) for Basic auth. type BasicAuthBitbucketServer struct { // Username for Basic auth @@ -522,7 +723,34 @@ type BasicAuthBitbucketServer struct { // If multiple filter types are set on a single struct, they will be AND'd together. All filters must // pass for a pull request to be included. type PullRequestGeneratorFilter struct { - BranchMatch *string `json:"branchMatch,omitempty" protobuf:"bytes,1,opt,name=branchMatch"` + BranchMatch *string `json:"branchMatch,omitempty" protobuf:"bytes,1,opt,name=branchMatch"` + TargetBranchMatch *string `json:"targetBranchMatch,omitempty" protobuf:"bytes,2,opt,name=targetBranchMatch"` +} + +type PluginConfigMapRef struct { + // Name of the ConfigMap + Name string `json:"name" protobuf:"bytes,1,opt,name=name"` +} + +type PluginParameters map[string]apiextensionsv1.JSON + +type PluginInput struct { + // Parameters contains the information to pass to the plugin. It is a map. The keys must be strings, and the + // values can be any type. + Parameters PluginParameters `json:"parameters,omitempty" protobuf:"bytes,1,name=parameters"` +} + +// PluginGenerator defines connection info specific to Plugin. +type PluginGenerator struct { + ConfigMapRef PluginConfigMapRef `json:"configMapRef" protobuf:"bytes,1,name=configMapRef"` + Input PluginInput `json:"input,omitempty" protobuf:"bytes,2,name=input"` + // RequeueAfterSeconds determines how long the ApplicationSet controller will wait before reconciling the ApplicationSet again. + RequeueAfterSeconds *int64 `json:"requeueAfterSeconds,omitempty" protobuf:"varint,3,opt,name=requeueAfterSeconds"` + Template ApplicationSetTemplate `json:"template,omitempty" protobuf:"bytes,4,name=template"` + + // Values contains key/value pairs which are passed directly as parameters to the template. These values will not be + // sent as parameters to the plugin. + Values map[string]string `json:"values,omitempty" protobuf:"bytes,5,name=values"` } // ApplicationSetStatus defines the observed state of ApplicationSet @@ -602,7 +830,9 @@ type ApplicationSetApplicationStatus struct { // Message contains human-readable message indicating details about the status Message string `json:"message" protobuf:"bytes,3,opt,name=message"` // Status contains the AppSet's perceived status of the managed Application resource: (Waiting, Pending, Progressing, Healthy) - Status string `json:"status" protobuf:"bytes,5,opt,name=status"` + Status string `json:"status" protobuf:"bytes,4,opt,name=status"` + // Step tracks which step this Application should be updated in + Step string `json:"step" protobuf:"bytes,5,opt,name=step"` } // ApplicationSetList contains a list of ApplicationSet @@ -673,3 +903,14 @@ func (status *ApplicationSetStatus) SetApplicationStatus(newStatus ApplicationSe } status.ApplicationStatus = append(status.ApplicationStatus, newStatus) } + +// QualifiedName returns the full qualified name of the applicationset, including +// the name of the namespace it is created in delimited by a forward slash, +// i.e. / +func (a *ApplicationSet) QualifiedName() string { + if a.Namespace == "" { + return a.Name + } else { + return a.Namespace + "/" + a.Name + } +} diff --git a/pkg/apis/application/v1alpha1/applicationset_types_test.go b/pkg/apis/application/v1alpha1/applicationset_types_test.go index 67fc502470a06..282cc1ca9a423 100644 --- a/pkg/apis/application/v1alpha1/applicationset_types_test.go +++ b/pkg/apis/application/v1alpha1/applicationset_types_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" ) func testAppSetCond(t ApplicationSetConditionType, msg string, lastTransitionTime *metav1.Time, status ApplicationSetConditionStatus, reason string) ApplicationSetCondition { @@ -38,6 +39,42 @@ func newTestAppSet(name, namespace, repo string) *ApplicationSet { return a } +func TestApplicationsSyncPolicy(t *testing.T) { + assert.False(t, ApplicationsSyncPolicyCreateOnly.AllowDelete()) + assert.False(t, ApplicationsSyncPolicyCreateOnly.AllowUpdate()) + + assert.False(t, ApplicationsSyncPolicyCreateUpdate.AllowDelete()) + assert.True(t, ApplicationsSyncPolicyCreateUpdate.AllowUpdate()) + + assert.True(t, ApplicationsSyncPolicySync.AllowDelete()) + assert.True(t, ApplicationsSyncPolicySync.AllowUpdate()) +} + +func TestApplicationSetRBACName(t *testing.T) { + testRepo := "https://github.com/org/repo" + + t.Run("Test RBAC name with namespace", func(t *testing.T) { + namespace := "guestbook" + a := newTestAppSet("test-appset", namespace, testRepo) + a.Spec.Template.Spec.Project = "test" + assert.Equal(t, "test/guestbook/test-appset", a.RBACName("argocd")) + }) + + t.Run("Test RBAC name default ns", func(t *testing.T) { + namespace := "argocd" + a := newTestAppSet("test-appset", namespace, testRepo) + a.Spec.Template.Spec.Project = "test" + assert.Equal(t, "test/test-appset", a.RBACName("argocd")) + }) + + t.Run("Test RBAC no ns", func(t *testing.T) { + a := newTestAppSet("test-appset", "", testRepo) + a.Spec.Template.Spec.Project = "test" + assert.Equal(t, "test/test-appset", a.RBACName("argocd")) + }) + +} + func TestApplicationSetSetConditions(t *testing.T) { fiveMinsAgo := &metav1.Time{Time: time.Now().Add(-5 * time.Minute)} tenMinsAgo := &metav1.Time{Time: time.Now().Add(-10 * time.Minute)} @@ -131,3 +168,14 @@ func assertAppSetConditions(t *testing.T, expected []ApplicationSetCondition, ac assert.Equal(t, expected[i].Message, actual[i].Message) } } + +func TestSCMProviderGeneratorGitlab_WillIncludeSharedProjects(t *testing.T) { + settings := SCMProviderGeneratorGitlab{} + assert.True(t, settings.WillIncludeSharedProjects()) + + settings.IncludeSharedProjects = pointer.Bool(false) + assert.False(t, settings.WillIncludeSharedProjects()) + + settings.IncludeSharedProjects = pointer.Bool(true) + assert.True(t, settings.WillIncludeSharedProjects()) +} diff --git a/pkg/apis/application/v1alpha1/generated.pb.go b/pkg/apis/application/v1alpha1/generated.pb.go index 6eb480af30f11..f6a253d23ed7d 100644 --- a/pkg/apis/application/v1alpha1/generated.pb.go +++ b/pkg/apis/application/v1alpha1/generated.pb.go @@ -17,6 +17,7 @@ import ( v12 "k8s.io/api/core/v1" v11 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" math "math" math_bits "math/bits" @@ -318,10 +319,38 @@ func (m *ApplicationMatchExpression) XXX_DiscardUnknown() { var xxx_messageInfo_ApplicationMatchExpression proto.InternalMessageInfo +func (m *ApplicationPreservedFields) Reset() { *m = ApplicationPreservedFields{} } +func (*ApplicationPreservedFields) ProtoMessage() {} +func (*ApplicationPreservedFields) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{10} +} +func (m *ApplicationPreservedFields) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ApplicationPreservedFields) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ApplicationPreservedFields) XXX_Merge(src proto.Message) { + xxx_messageInfo_ApplicationPreservedFields.Merge(m, src) +} +func (m *ApplicationPreservedFields) XXX_Size() int { + return m.Size() +} +func (m *ApplicationPreservedFields) XXX_DiscardUnknown() { + xxx_messageInfo_ApplicationPreservedFields.DiscardUnknown(m) +} + +var xxx_messageInfo_ApplicationPreservedFields proto.InternalMessageInfo + func (m *ApplicationSet) Reset() { *m = ApplicationSet{} } func (*ApplicationSet) ProtoMessage() {} func (*ApplicationSet) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{10} + return fileDescriptor_030104ce3b95bcac, []int{11} } func (m *ApplicationSet) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -349,7 +378,7 @@ var xxx_messageInfo_ApplicationSet proto.InternalMessageInfo func (m *ApplicationSetApplicationStatus) Reset() { *m = ApplicationSetApplicationStatus{} } func (*ApplicationSetApplicationStatus) ProtoMessage() {} func (*ApplicationSetApplicationStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{11} + return fileDescriptor_030104ce3b95bcac, []int{12} } func (m *ApplicationSetApplicationStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -377,7 +406,7 @@ var xxx_messageInfo_ApplicationSetApplicationStatus proto.InternalMessageInfo func (m *ApplicationSetCondition) Reset() { *m = ApplicationSetCondition{} } func (*ApplicationSetCondition) ProtoMessage() {} func (*ApplicationSetCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{12} + return fileDescriptor_030104ce3b95bcac, []int{13} } func (m *ApplicationSetCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -405,7 +434,7 @@ var xxx_messageInfo_ApplicationSetCondition proto.InternalMessageInfo func (m *ApplicationSetGenerator) Reset() { *m = ApplicationSetGenerator{} } func (*ApplicationSetGenerator) ProtoMessage() {} func (*ApplicationSetGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{13} + return fileDescriptor_030104ce3b95bcac, []int{14} } func (m *ApplicationSetGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -433,7 +462,7 @@ var xxx_messageInfo_ApplicationSetGenerator proto.InternalMessageInfo func (m *ApplicationSetList) Reset() { *m = ApplicationSetList{} } func (*ApplicationSetList) ProtoMessage() {} func (*ApplicationSetList) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{14} + return fileDescriptor_030104ce3b95bcac, []int{15} } func (m *ApplicationSetList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -461,7 +490,7 @@ var xxx_messageInfo_ApplicationSetList proto.InternalMessageInfo func (m *ApplicationSetNestedGenerator) Reset() { *m = ApplicationSetNestedGenerator{} } func (*ApplicationSetNestedGenerator) ProtoMessage() {} func (*ApplicationSetNestedGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{15} + return fileDescriptor_030104ce3b95bcac, []int{16} } func (m *ApplicationSetNestedGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -486,10 +515,40 @@ func (m *ApplicationSetNestedGenerator) XXX_DiscardUnknown() { var xxx_messageInfo_ApplicationSetNestedGenerator proto.InternalMessageInfo +func (m *ApplicationSetResourceIgnoreDifferences) Reset() { + *m = ApplicationSetResourceIgnoreDifferences{} +} +func (*ApplicationSetResourceIgnoreDifferences) ProtoMessage() {} +func (*ApplicationSetResourceIgnoreDifferences) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{17} +} +func (m *ApplicationSetResourceIgnoreDifferences) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ApplicationSetResourceIgnoreDifferences) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ApplicationSetResourceIgnoreDifferences) XXX_Merge(src proto.Message) { + xxx_messageInfo_ApplicationSetResourceIgnoreDifferences.Merge(m, src) +} +func (m *ApplicationSetResourceIgnoreDifferences) XXX_Size() int { + return m.Size() +} +func (m *ApplicationSetResourceIgnoreDifferences) XXX_DiscardUnknown() { + xxx_messageInfo_ApplicationSetResourceIgnoreDifferences.DiscardUnknown(m) +} + +var xxx_messageInfo_ApplicationSetResourceIgnoreDifferences proto.InternalMessageInfo + func (m *ApplicationSetRolloutStep) Reset() { *m = ApplicationSetRolloutStep{} } func (*ApplicationSetRolloutStep) ProtoMessage() {} func (*ApplicationSetRolloutStep) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{16} + return fileDescriptor_030104ce3b95bcac, []int{18} } func (m *ApplicationSetRolloutStep) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -517,7 +576,7 @@ var xxx_messageInfo_ApplicationSetRolloutStep proto.InternalMessageInfo func (m *ApplicationSetRolloutStrategy) Reset() { *m = ApplicationSetRolloutStrategy{} } func (*ApplicationSetRolloutStrategy) ProtoMessage() {} func (*ApplicationSetRolloutStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{17} + return fileDescriptor_030104ce3b95bcac, []int{19} } func (m *ApplicationSetRolloutStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -545,7 +604,7 @@ var xxx_messageInfo_ApplicationSetRolloutStrategy proto.InternalMessageInfo func (m *ApplicationSetSpec) Reset() { *m = ApplicationSetSpec{} } func (*ApplicationSetSpec) ProtoMessage() {} func (*ApplicationSetSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{18} + return fileDescriptor_030104ce3b95bcac, []int{20} } func (m *ApplicationSetSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -573,7 +632,7 @@ var xxx_messageInfo_ApplicationSetSpec proto.InternalMessageInfo func (m *ApplicationSetStatus) Reset() { *m = ApplicationSetStatus{} } func (*ApplicationSetStatus) ProtoMessage() {} func (*ApplicationSetStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{19} + return fileDescriptor_030104ce3b95bcac, []int{21} } func (m *ApplicationSetStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -601,7 +660,7 @@ var xxx_messageInfo_ApplicationSetStatus proto.InternalMessageInfo func (m *ApplicationSetStrategy) Reset() { *m = ApplicationSetStrategy{} } func (*ApplicationSetStrategy) ProtoMessage() {} func (*ApplicationSetStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{20} + return fileDescriptor_030104ce3b95bcac, []int{22} } func (m *ApplicationSetStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -629,7 +688,7 @@ var xxx_messageInfo_ApplicationSetStrategy proto.InternalMessageInfo func (m *ApplicationSetSyncPolicy) Reset() { *m = ApplicationSetSyncPolicy{} } func (*ApplicationSetSyncPolicy) ProtoMessage() {} func (*ApplicationSetSyncPolicy) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{21} + return fileDescriptor_030104ce3b95bcac, []int{23} } func (m *ApplicationSetSyncPolicy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -657,7 +716,7 @@ var xxx_messageInfo_ApplicationSetSyncPolicy proto.InternalMessageInfo func (m *ApplicationSetTemplate) Reset() { *m = ApplicationSetTemplate{} } func (*ApplicationSetTemplate) ProtoMessage() {} func (*ApplicationSetTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{22} + return fileDescriptor_030104ce3b95bcac, []int{24} } func (m *ApplicationSetTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -685,7 +744,7 @@ var xxx_messageInfo_ApplicationSetTemplate proto.InternalMessageInfo func (m *ApplicationSetTemplateMeta) Reset() { *m = ApplicationSetTemplateMeta{} } func (*ApplicationSetTemplateMeta) ProtoMessage() {} func (*ApplicationSetTemplateMeta) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{23} + return fileDescriptor_030104ce3b95bcac, []int{25} } func (m *ApplicationSetTemplateMeta) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -713,7 +772,7 @@ var xxx_messageInfo_ApplicationSetTemplateMeta proto.InternalMessageInfo func (m *ApplicationSetTerminalGenerator) Reset() { *m = ApplicationSetTerminalGenerator{} } func (*ApplicationSetTerminalGenerator) ProtoMessage() {} func (*ApplicationSetTerminalGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{24} + return fileDescriptor_030104ce3b95bcac, []int{26} } func (m *ApplicationSetTerminalGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -741,7 +800,7 @@ var xxx_messageInfo_ApplicationSetTerminalGenerator proto.InternalMessageInfo func (m *ApplicationSource) Reset() { *m = ApplicationSource{} } func (*ApplicationSource) ProtoMessage() {} func (*ApplicationSource) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{25} + return fileDescriptor_030104ce3b95bcac, []int{27} } func (m *ApplicationSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -769,7 +828,7 @@ var xxx_messageInfo_ApplicationSource proto.InternalMessageInfo func (m *ApplicationSourceDirectory) Reset() { *m = ApplicationSourceDirectory{} } func (*ApplicationSourceDirectory) ProtoMessage() {} func (*ApplicationSourceDirectory) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{26} + return fileDescriptor_030104ce3b95bcac, []int{28} } func (m *ApplicationSourceDirectory) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -797,7 +856,7 @@ var xxx_messageInfo_ApplicationSourceDirectory proto.InternalMessageInfo func (m *ApplicationSourceHelm) Reset() { *m = ApplicationSourceHelm{} } func (*ApplicationSourceHelm) ProtoMessage() {} func (*ApplicationSourceHelm) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{27} + return fileDescriptor_030104ce3b95bcac, []int{29} } func (m *ApplicationSourceHelm) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -825,7 +884,7 @@ var xxx_messageInfo_ApplicationSourceHelm proto.InternalMessageInfo func (m *ApplicationSourceJsonnet) Reset() { *m = ApplicationSourceJsonnet{} } func (*ApplicationSourceJsonnet) ProtoMessage() {} func (*ApplicationSourceJsonnet) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{28} + return fileDescriptor_030104ce3b95bcac, []int{30} } func (m *ApplicationSourceJsonnet) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -853,7 +912,7 @@ var xxx_messageInfo_ApplicationSourceJsonnet proto.InternalMessageInfo func (m *ApplicationSourceKustomize) Reset() { *m = ApplicationSourceKustomize{} } func (*ApplicationSourceKustomize) ProtoMessage() {} func (*ApplicationSourceKustomize) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{29} + return fileDescriptor_030104ce3b95bcac, []int{31} } func (m *ApplicationSourceKustomize) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -881,7 +940,7 @@ var xxx_messageInfo_ApplicationSourceKustomize proto.InternalMessageInfo func (m *ApplicationSourcePlugin) Reset() { *m = ApplicationSourcePlugin{} } func (*ApplicationSourcePlugin) ProtoMessage() {} func (*ApplicationSourcePlugin) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{30} + return fileDescriptor_030104ce3b95bcac, []int{32} } func (m *ApplicationSourcePlugin) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -909,7 +968,7 @@ var xxx_messageInfo_ApplicationSourcePlugin proto.InternalMessageInfo func (m *ApplicationSourcePluginParameter) Reset() { *m = ApplicationSourcePluginParameter{} } func (*ApplicationSourcePluginParameter) ProtoMessage() {} func (*ApplicationSourcePluginParameter) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{31} + return fileDescriptor_030104ce3b95bcac, []int{33} } func (m *ApplicationSourcePluginParameter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -937,7 +996,7 @@ var xxx_messageInfo_ApplicationSourcePluginParameter proto.InternalMessageInfo func (m *ApplicationSpec) Reset() { *m = ApplicationSpec{} } func (*ApplicationSpec) ProtoMessage() {} func (*ApplicationSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{32} + return fileDescriptor_030104ce3b95bcac, []int{34} } func (m *ApplicationSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -965,7 +1024,7 @@ var xxx_messageInfo_ApplicationSpec proto.InternalMessageInfo func (m *ApplicationStatus) Reset() { *m = ApplicationStatus{} } func (*ApplicationStatus) ProtoMessage() {} func (*ApplicationStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{33} + return fileDescriptor_030104ce3b95bcac, []int{35} } func (m *ApplicationStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -993,7 +1052,7 @@ var xxx_messageInfo_ApplicationStatus proto.InternalMessageInfo func (m *ApplicationSummary) Reset() { *m = ApplicationSummary{} } func (*ApplicationSummary) ProtoMessage() {} func (*ApplicationSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{34} + return fileDescriptor_030104ce3b95bcac, []int{36} } func (m *ApplicationSummary) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1021,7 +1080,7 @@ var xxx_messageInfo_ApplicationSummary proto.InternalMessageInfo func (m *ApplicationTree) Reset() { *m = ApplicationTree{} } func (*ApplicationTree) ProtoMessage() {} func (*ApplicationTree) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{35} + return fileDescriptor_030104ce3b95bcac, []int{37} } func (m *ApplicationTree) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1049,7 +1108,7 @@ var xxx_messageInfo_ApplicationTree proto.InternalMessageInfo func (m *ApplicationWatchEvent) Reset() { *m = ApplicationWatchEvent{} } func (*ApplicationWatchEvent) ProtoMessage() {} func (*ApplicationWatchEvent) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{36} + return fileDescriptor_030104ce3b95bcac, []int{38} } func (m *ApplicationWatchEvent) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1077,7 +1136,7 @@ var xxx_messageInfo_ApplicationWatchEvent proto.InternalMessageInfo func (m *Backoff) Reset() { *m = Backoff{} } func (*Backoff) ProtoMessage() {} func (*Backoff) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{37} + return fileDescriptor_030104ce3b95bcac, []int{39} } func (m *Backoff) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1105,7 +1164,7 @@ var xxx_messageInfo_Backoff proto.InternalMessageInfo func (m *BasicAuthBitbucketServer) Reset() { *m = BasicAuthBitbucketServer{} } func (*BasicAuthBitbucketServer) ProtoMessage() {} func (*BasicAuthBitbucketServer) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{38} + return fileDescriptor_030104ce3b95bcac, []int{40} } func (m *BasicAuthBitbucketServer) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1130,10 +1189,66 @@ func (m *BasicAuthBitbucketServer) XXX_DiscardUnknown() { var xxx_messageInfo_BasicAuthBitbucketServer proto.InternalMessageInfo +func (m *BearerTokenBitbucketCloud) Reset() { *m = BearerTokenBitbucketCloud{} } +func (*BearerTokenBitbucketCloud) ProtoMessage() {} +func (*BearerTokenBitbucketCloud) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{41} +} +func (m *BearerTokenBitbucketCloud) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BearerTokenBitbucketCloud) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *BearerTokenBitbucketCloud) XXX_Merge(src proto.Message) { + xxx_messageInfo_BearerTokenBitbucketCloud.Merge(m, src) +} +func (m *BearerTokenBitbucketCloud) XXX_Size() int { + return m.Size() +} +func (m *BearerTokenBitbucketCloud) XXX_DiscardUnknown() { + xxx_messageInfo_BearerTokenBitbucketCloud.DiscardUnknown(m) +} + +var xxx_messageInfo_BearerTokenBitbucketCloud proto.InternalMessageInfo + +func (m *ChartDetails) Reset() { *m = ChartDetails{} } +func (*ChartDetails) ProtoMessage() {} +func (*ChartDetails) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{42} +} +func (m *ChartDetails) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ChartDetails) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ChartDetails) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChartDetails.Merge(m, src) +} +func (m *ChartDetails) XXX_Size() int { + return m.Size() +} +func (m *ChartDetails) XXX_DiscardUnknown() { + xxx_messageInfo_ChartDetails.DiscardUnknown(m) +} + +var xxx_messageInfo_ChartDetails proto.InternalMessageInfo + func (m *Cluster) Reset() { *m = Cluster{} } func (*Cluster) ProtoMessage() {} func (*Cluster) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{39} + return fileDescriptor_030104ce3b95bcac, []int{43} } func (m *Cluster) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1161,7 +1276,7 @@ var xxx_messageInfo_Cluster proto.InternalMessageInfo func (m *ClusterCacheInfo) Reset() { *m = ClusterCacheInfo{} } func (*ClusterCacheInfo) ProtoMessage() {} func (*ClusterCacheInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{40} + return fileDescriptor_030104ce3b95bcac, []int{44} } func (m *ClusterCacheInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1189,7 +1304,7 @@ var xxx_messageInfo_ClusterCacheInfo proto.InternalMessageInfo func (m *ClusterConfig) Reset() { *m = ClusterConfig{} } func (*ClusterConfig) ProtoMessage() {} func (*ClusterConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{41} + return fileDescriptor_030104ce3b95bcac, []int{45} } func (m *ClusterConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1217,7 +1332,7 @@ var xxx_messageInfo_ClusterConfig proto.InternalMessageInfo func (m *ClusterGenerator) Reset() { *m = ClusterGenerator{} } func (*ClusterGenerator) ProtoMessage() {} func (*ClusterGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{42} + return fileDescriptor_030104ce3b95bcac, []int{46} } func (m *ClusterGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1245,7 +1360,7 @@ var xxx_messageInfo_ClusterGenerator proto.InternalMessageInfo func (m *ClusterInfo) Reset() { *m = ClusterInfo{} } func (*ClusterInfo) ProtoMessage() {} func (*ClusterInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{43} + return fileDescriptor_030104ce3b95bcac, []int{47} } func (m *ClusterInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1273,7 +1388,7 @@ var xxx_messageInfo_ClusterInfo proto.InternalMessageInfo func (m *ClusterList) Reset() { *m = ClusterList{} } func (*ClusterList) ProtoMessage() {} func (*ClusterList) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{44} + return fileDescriptor_030104ce3b95bcac, []int{48} } func (m *ClusterList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1301,7 +1416,7 @@ var xxx_messageInfo_ClusterList proto.InternalMessageInfo func (m *Command) Reset() { *m = Command{} } func (*Command) ProtoMessage() {} func (*Command) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{45} + return fileDescriptor_030104ce3b95bcac, []int{49} } func (m *Command) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1329,7 +1444,7 @@ var xxx_messageInfo_Command proto.InternalMessageInfo func (m *ComparedTo) Reset() { *m = ComparedTo{} } func (*ComparedTo) ProtoMessage() {} func (*ComparedTo) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{46} + return fileDescriptor_030104ce3b95bcac, []int{50} } func (m *ComparedTo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1357,7 +1472,7 @@ var xxx_messageInfo_ComparedTo proto.InternalMessageInfo func (m *ComponentParameter) Reset() { *m = ComponentParameter{} } func (*ComponentParameter) ProtoMessage() {} func (*ComponentParameter) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{47} + return fileDescriptor_030104ce3b95bcac, []int{51} } func (m *ComponentParameter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1385,7 +1500,7 @@ var xxx_messageInfo_ComponentParameter proto.InternalMessageInfo func (m *ConfigManagementPlugin) Reset() { *m = ConfigManagementPlugin{} } func (*ConfigManagementPlugin) ProtoMessage() {} func (*ConfigManagementPlugin) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{48} + return fileDescriptor_030104ce3b95bcac, []int{52} } func (m *ConfigManagementPlugin) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1413,7 +1528,7 @@ var xxx_messageInfo_ConfigManagementPlugin proto.InternalMessageInfo func (m *ConnectionState) Reset() { *m = ConnectionState{} } func (*ConnectionState) ProtoMessage() {} func (*ConnectionState) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{49} + return fileDescriptor_030104ce3b95bcac, []int{53} } func (m *ConnectionState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1441,7 +1556,7 @@ var xxx_messageInfo_ConnectionState proto.InternalMessageInfo func (m *DuckTypeGenerator) Reset() { *m = DuckTypeGenerator{} } func (*DuckTypeGenerator) ProtoMessage() {} func (*DuckTypeGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{50} + return fileDescriptor_030104ce3b95bcac, []int{54} } func (m *DuckTypeGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1469,7 +1584,7 @@ var xxx_messageInfo_DuckTypeGenerator proto.InternalMessageInfo func (m *EnvEntry) Reset() { *m = EnvEntry{} } func (*EnvEntry) ProtoMessage() {} func (*EnvEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{51} + return fileDescriptor_030104ce3b95bcac, []int{55} } func (m *EnvEntry) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1497,7 +1612,7 @@ var xxx_messageInfo_EnvEntry proto.InternalMessageInfo func (m *ExecProviderConfig) Reset() { *m = ExecProviderConfig{} } func (*ExecProviderConfig) ProtoMessage() {} func (*ExecProviderConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{52} + return fileDescriptor_030104ce3b95bcac, []int{56} } func (m *ExecProviderConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1525,7 +1640,7 @@ var xxx_messageInfo_ExecProviderConfig proto.InternalMessageInfo func (m *GitDirectoryGeneratorItem) Reset() { *m = GitDirectoryGeneratorItem{} } func (*GitDirectoryGeneratorItem) ProtoMessage() {} func (*GitDirectoryGeneratorItem) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{53} + return fileDescriptor_030104ce3b95bcac, []int{57} } func (m *GitDirectoryGeneratorItem) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1553,7 +1668,7 @@ var xxx_messageInfo_GitDirectoryGeneratorItem proto.InternalMessageInfo func (m *GitFileGeneratorItem) Reset() { *m = GitFileGeneratorItem{} } func (*GitFileGeneratorItem) ProtoMessage() {} func (*GitFileGeneratorItem) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{54} + return fileDescriptor_030104ce3b95bcac, []int{58} } func (m *GitFileGeneratorItem) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1581,7 +1696,7 @@ var xxx_messageInfo_GitFileGeneratorItem proto.InternalMessageInfo func (m *GitGenerator) Reset() { *m = GitGenerator{} } func (*GitGenerator) ProtoMessage() {} func (*GitGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{55} + return fileDescriptor_030104ce3b95bcac, []int{59} } func (m *GitGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1609,7 +1724,7 @@ var xxx_messageInfo_GitGenerator proto.InternalMessageInfo func (m *GnuPGPublicKey) Reset() { *m = GnuPGPublicKey{} } func (*GnuPGPublicKey) ProtoMessage() {} func (*GnuPGPublicKey) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{56} + return fileDescriptor_030104ce3b95bcac, []int{60} } func (m *GnuPGPublicKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1637,7 +1752,7 @@ var xxx_messageInfo_GnuPGPublicKey proto.InternalMessageInfo func (m *GnuPGPublicKeyList) Reset() { *m = GnuPGPublicKeyList{} } func (*GnuPGPublicKeyList) ProtoMessage() {} func (*GnuPGPublicKeyList) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{57} + return fileDescriptor_030104ce3b95bcac, []int{61} } func (m *GnuPGPublicKeyList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1665,7 +1780,7 @@ var xxx_messageInfo_GnuPGPublicKeyList proto.InternalMessageInfo func (m *HealthStatus) Reset() { *m = HealthStatus{} } func (*HealthStatus) ProtoMessage() {} func (*HealthStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{58} + return fileDescriptor_030104ce3b95bcac, []int{62} } func (m *HealthStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1693,7 +1808,7 @@ var xxx_messageInfo_HealthStatus proto.InternalMessageInfo func (m *HelmFileParameter) Reset() { *m = HelmFileParameter{} } func (*HelmFileParameter) ProtoMessage() {} func (*HelmFileParameter) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{59} + return fileDescriptor_030104ce3b95bcac, []int{63} } func (m *HelmFileParameter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1721,7 +1836,7 @@ var xxx_messageInfo_HelmFileParameter proto.InternalMessageInfo func (m *HelmOptions) Reset() { *m = HelmOptions{} } func (*HelmOptions) ProtoMessage() {} func (*HelmOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{60} + return fileDescriptor_030104ce3b95bcac, []int{64} } func (m *HelmOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1749,7 +1864,7 @@ var xxx_messageInfo_HelmOptions proto.InternalMessageInfo func (m *HelmParameter) Reset() { *m = HelmParameter{} } func (*HelmParameter) ProtoMessage() {} func (*HelmParameter) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{61} + return fileDescriptor_030104ce3b95bcac, []int{65} } func (m *HelmParameter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1777,7 +1892,7 @@ var xxx_messageInfo_HelmParameter proto.InternalMessageInfo func (m *HostInfo) Reset() { *m = HostInfo{} } func (*HostInfo) ProtoMessage() {} func (*HostInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{62} + return fileDescriptor_030104ce3b95bcac, []int{66} } func (m *HostInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1805,7 +1920,7 @@ var xxx_messageInfo_HostInfo proto.InternalMessageInfo func (m *HostResourceInfo) Reset() { *m = HostResourceInfo{} } func (*HostResourceInfo) ProtoMessage() {} func (*HostResourceInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{63} + return fileDescriptor_030104ce3b95bcac, []int{67} } func (m *HostResourceInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1833,7 +1948,7 @@ var xxx_messageInfo_HostResourceInfo proto.InternalMessageInfo func (m *Info) Reset() { *m = Info{} } func (*Info) ProtoMessage() {} func (*Info) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{64} + return fileDescriptor_030104ce3b95bcac, []int{68} } func (m *Info) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1861,7 +1976,7 @@ var xxx_messageInfo_Info proto.InternalMessageInfo func (m *InfoItem) Reset() { *m = InfoItem{} } func (*InfoItem) ProtoMessage() {} func (*InfoItem) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{65} + return fileDescriptor_030104ce3b95bcac, []int{69} } func (m *InfoItem) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1889,7 +2004,7 @@ var xxx_messageInfo_InfoItem proto.InternalMessageInfo func (m *JWTToken) Reset() { *m = JWTToken{} } func (*JWTToken) ProtoMessage() {} func (*JWTToken) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{66} + return fileDescriptor_030104ce3b95bcac, []int{70} } func (m *JWTToken) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1917,7 +2032,7 @@ var xxx_messageInfo_JWTToken proto.InternalMessageInfo func (m *JWTTokens) Reset() { *m = JWTTokens{} } func (*JWTTokens) ProtoMessage() {} func (*JWTTokens) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{67} + return fileDescriptor_030104ce3b95bcac, []int{71} } func (m *JWTTokens) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1945,7 +2060,7 @@ var xxx_messageInfo_JWTTokens proto.InternalMessageInfo func (m *JsonnetVar) Reset() { *m = JsonnetVar{} } func (*JsonnetVar) ProtoMessage() {} func (*JsonnetVar) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{68} + return fileDescriptor_030104ce3b95bcac, []int{72} } func (m *JsonnetVar) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1973,7 +2088,7 @@ var xxx_messageInfo_JsonnetVar proto.InternalMessageInfo func (m *KnownTypeField) Reset() { *m = KnownTypeField{} } func (*KnownTypeField) ProtoMessage() {} func (*KnownTypeField) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{69} + return fileDescriptor_030104ce3b95bcac, []int{73} } func (m *KnownTypeField) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1998,10 +2113,38 @@ func (m *KnownTypeField) XXX_DiscardUnknown() { var xxx_messageInfo_KnownTypeField proto.InternalMessageInfo +func (m *KustomizeGvk) Reset() { *m = KustomizeGvk{} } +func (*KustomizeGvk) ProtoMessage() {} +func (*KustomizeGvk) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{74} +} +func (m *KustomizeGvk) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *KustomizeGvk) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *KustomizeGvk) XXX_Merge(src proto.Message) { + xxx_messageInfo_KustomizeGvk.Merge(m, src) +} +func (m *KustomizeGvk) XXX_Size() int { + return m.Size() +} +func (m *KustomizeGvk) XXX_DiscardUnknown() { + xxx_messageInfo_KustomizeGvk.DiscardUnknown(m) +} + +var xxx_messageInfo_KustomizeGvk proto.InternalMessageInfo + func (m *KustomizeOptions) Reset() { *m = KustomizeOptions{} } func (*KustomizeOptions) ProtoMessage() {} func (*KustomizeOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{70} + return fileDescriptor_030104ce3b95bcac, []int{75} } func (m *KustomizeOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2026,10 +2169,122 @@ func (m *KustomizeOptions) XXX_DiscardUnknown() { var xxx_messageInfo_KustomizeOptions proto.InternalMessageInfo +func (m *KustomizePatch) Reset() { *m = KustomizePatch{} } +func (*KustomizePatch) ProtoMessage() {} +func (*KustomizePatch) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{76} +} +func (m *KustomizePatch) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *KustomizePatch) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *KustomizePatch) XXX_Merge(src proto.Message) { + xxx_messageInfo_KustomizePatch.Merge(m, src) +} +func (m *KustomizePatch) XXX_Size() int { + return m.Size() +} +func (m *KustomizePatch) XXX_DiscardUnknown() { + xxx_messageInfo_KustomizePatch.DiscardUnknown(m) +} + +var xxx_messageInfo_KustomizePatch proto.InternalMessageInfo + +func (m *KustomizeReplica) Reset() { *m = KustomizeReplica{} } +func (*KustomizeReplica) ProtoMessage() {} +func (*KustomizeReplica) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{77} +} +func (m *KustomizeReplica) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *KustomizeReplica) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *KustomizeReplica) XXX_Merge(src proto.Message) { + xxx_messageInfo_KustomizeReplica.Merge(m, src) +} +func (m *KustomizeReplica) XXX_Size() int { + return m.Size() +} +func (m *KustomizeReplica) XXX_DiscardUnknown() { + xxx_messageInfo_KustomizeReplica.DiscardUnknown(m) +} + +var xxx_messageInfo_KustomizeReplica proto.InternalMessageInfo + +func (m *KustomizeResId) Reset() { *m = KustomizeResId{} } +func (*KustomizeResId) ProtoMessage() {} +func (*KustomizeResId) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{78} +} +func (m *KustomizeResId) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *KustomizeResId) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *KustomizeResId) XXX_Merge(src proto.Message) { + xxx_messageInfo_KustomizeResId.Merge(m, src) +} +func (m *KustomizeResId) XXX_Size() int { + return m.Size() +} +func (m *KustomizeResId) XXX_DiscardUnknown() { + xxx_messageInfo_KustomizeResId.DiscardUnknown(m) +} + +var xxx_messageInfo_KustomizeResId proto.InternalMessageInfo + +func (m *KustomizeSelector) Reset() { *m = KustomizeSelector{} } +func (*KustomizeSelector) ProtoMessage() {} +func (*KustomizeSelector) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{79} +} +func (m *KustomizeSelector) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *KustomizeSelector) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *KustomizeSelector) XXX_Merge(src proto.Message) { + xxx_messageInfo_KustomizeSelector.Merge(m, src) +} +func (m *KustomizeSelector) XXX_Size() int { + return m.Size() +} +func (m *KustomizeSelector) XXX_DiscardUnknown() { + xxx_messageInfo_KustomizeSelector.DiscardUnknown(m) +} + +var xxx_messageInfo_KustomizeSelector proto.InternalMessageInfo + func (m *ListGenerator) Reset() { *m = ListGenerator{} } func (*ListGenerator) ProtoMessage() {} func (*ListGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{71} + return fileDescriptor_030104ce3b95bcac, []int{80} } func (m *ListGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2057,7 +2312,7 @@ var xxx_messageInfo_ListGenerator proto.InternalMessageInfo func (m *ManagedNamespaceMetadata) Reset() { *m = ManagedNamespaceMetadata{} } func (*ManagedNamespaceMetadata) ProtoMessage() {} func (*ManagedNamespaceMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{72} + return fileDescriptor_030104ce3b95bcac, []int{81} } func (m *ManagedNamespaceMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2085,7 +2340,7 @@ var xxx_messageInfo_ManagedNamespaceMetadata proto.InternalMessageInfo func (m *MatrixGenerator) Reset() { *m = MatrixGenerator{} } func (*MatrixGenerator) ProtoMessage() {} func (*MatrixGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{73} + return fileDescriptor_030104ce3b95bcac, []int{82} } func (m *MatrixGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2113,7 +2368,7 @@ var xxx_messageInfo_MatrixGenerator proto.InternalMessageInfo func (m *MergeGenerator) Reset() { *m = MergeGenerator{} } func (*MergeGenerator) ProtoMessage() {} func (*MergeGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{74} + return fileDescriptor_030104ce3b95bcac, []int{83} } func (m *MergeGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2141,7 +2396,7 @@ var xxx_messageInfo_MergeGenerator proto.InternalMessageInfo func (m *NestedMatrixGenerator) Reset() { *m = NestedMatrixGenerator{} } func (*NestedMatrixGenerator) ProtoMessage() {} func (*NestedMatrixGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{75} + return fileDescriptor_030104ce3b95bcac, []int{84} } func (m *NestedMatrixGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2169,7 +2424,7 @@ var xxx_messageInfo_NestedMatrixGenerator proto.InternalMessageInfo func (m *NestedMergeGenerator) Reset() { *m = NestedMergeGenerator{} } func (*NestedMergeGenerator) ProtoMessage() {} func (*NestedMergeGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{76} + return fileDescriptor_030104ce3b95bcac, []int{85} } func (m *NestedMergeGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2197,7 +2452,7 @@ var xxx_messageInfo_NestedMergeGenerator proto.InternalMessageInfo func (m *Operation) Reset() { *m = Operation{} } func (*Operation) ProtoMessage() {} func (*Operation) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{77} + return fileDescriptor_030104ce3b95bcac, []int{86} } func (m *Operation) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2225,7 +2480,7 @@ var xxx_messageInfo_Operation proto.InternalMessageInfo func (m *OperationInitiator) Reset() { *m = OperationInitiator{} } func (*OperationInitiator) ProtoMessage() {} func (*OperationInitiator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{78} + return fileDescriptor_030104ce3b95bcac, []int{87} } func (m *OperationInitiator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2253,7 +2508,7 @@ var xxx_messageInfo_OperationInitiator proto.InternalMessageInfo func (m *OperationState) Reset() { *m = OperationState{} } func (*OperationState) ProtoMessage() {} func (*OperationState) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{79} + return fileDescriptor_030104ce3b95bcac, []int{88} } func (m *OperationState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2278,10 +2533,66 @@ func (m *OperationState) XXX_DiscardUnknown() { var xxx_messageInfo_OperationState proto.InternalMessageInfo +func (m *OptionalArray) Reset() { *m = OptionalArray{} } +func (*OptionalArray) ProtoMessage() {} +func (*OptionalArray) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{89} +} +func (m *OptionalArray) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *OptionalArray) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *OptionalArray) XXX_Merge(src proto.Message) { + xxx_messageInfo_OptionalArray.Merge(m, src) +} +func (m *OptionalArray) XXX_Size() int { + return m.Size() +} +func (m *OptionalArray) XXX_DiscardUnknown() { + xxx_messageInfo_OptionalArray.DiscardUnknown(m) +} + +var xxx_messageInfo_OptionalArray proto.InternalMessageInfo + +func (m *OptionalMap) Reset() { *m = OptionalMap{} } +func (*OptionalMap) ProtoMessage() {} +func (*OptionalMap) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{90} +} +func (m *OptionalMap) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *OptionalMap) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *OptionalMap) XXX_Merge(src proto.Message) { + xxx_messageInfo_OptionalMap.Merge(m, src) +} +func (m *OptionalMap) XXX_Size() int { + return m.Size() +} +func (m *OptionalMap) XXX_DiscardUnknown() { + xxx_messageInfo_OptionalMap.DiscardUnknown(m) +} + +var xxx_messageInfo_OptionalMap proto.InternalMessageInfo + func (m *OrphanedResourceKey) Reset() { *m = OrphanedResourceKey{} } func (*OrphanedResourceKey) ProtoMessage() {} func (*OrphanedResourceKey) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{80} + return fileDescriptor_030104ce3b95bcac, []int{91} } func (m *OrphanedResourceKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2309,7 +2620,7 @@ var xxx_messageInfo_OrphanedResourceKey proto.InternalMessageInfo func (m *OrphanedResourcesMonitorSettings) Reset() { *m = OrphanedResourcesMonitorSettings{} } func (*OrphanedResourcesMonitorSettings) ProtoMessage() {} func (*OrphanedResourcesMonitorSettings) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{81} + return fileDescriptor_030104ce3b95bcac, []int{92} } func (m *OrphanedResourcesMonitorSettings) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2337,7 +2648,7 @@ var xxx_messageInfo_OrphanedResourcesMonitorSettings proto.InternalMessageInfo func (m *OverrideIgnoreDiff) Reset() { *m = OverrideIgnoreDiff{} } func (*OverrideIgnoreDiff) ProtoMessage() {} func (*OverrideIgnoreDiff) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{82} + return fileDescriptor_030104ce3b95bcac, []int{93} } func (m *OverrideIgnoreDiff) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2362,10 +2673,94 @@ func (m *OverrideIgnoreDiff) XXX_DiscardUnknown() { var xxx_messageInfo_OverrideIgnoreDiff proto.InternalMessageInfo +func (m *PluginConfigMapRef) Reset() { *m = PluginConfigMapRef{} } +func (*PluginConfigMapRef) ProtoMessage() {} +func (*PluginConfigMapRef) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{94} +} +func (m *PluginConfigMapRef) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PluginConfigMapRef) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *PluginConfigMapRef) XXX_Merge(src proto.Message) { + xxx_messageInfo_PluginConfigMapRef.Merge(m, src) +} +func (m *PluginConfigMapRef) XXX_Size() int { + return m.Size() +} +func (m *PluginConfigMapRef) XXX_DiscardUnknown() { + xxx_messageInfo_PluginConfigMapRef.DiscardUnknown(m) +} + +var xxx_messageInfo_PluginConfigMapRef proto.InternalMessageInfo + +func (m *PluginGenerator) Reset() { *m = PluginGenerator{} } +func (*PluginGenerator) ProtoMessage() {} +func (*PluginGenerator) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{95} +} +func (m *PluginGenerator) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PluginGenerator) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *PluginGenerator) XXX_Merge(src proto.Message) { + xxx_messageInfo_PluginGenerator.Merge(m, src) +} +func (m *PluginGenerator) XXX_Size() int { + return m.Size() +} +func (m *PluginGenerator) XXX_DiscardUnknown() { + xxx_messageInfo_PluginGenerator.DiscardUnknown(m) +} + +var xxx_messageInfo_PluginGenerator proto.InternalMessageInfo + +func (m *PluginInput) Reset() { *m = PluginInput{} } +func (*PluginInput) ProtoMessage() {} +func (*PluginInput) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{96} +} +func (m *PluginInput) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PluginInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *PluginInput) XXX_Merge(src proto.Message) { + xxx_messageInfo_PluginInput.Merge(m, src) +} +func (m *PluginInput) XXX_Size() int { + return m.Size() +} +func (m *PluginInput) XXX_DiscardUnknown() { + xxx_messageInfo_PluginInput.DiscardUnknown(m) +} + +var xxx_messageInfo_PluginInput proto.InternalMessageInfo + func (m *ProjectRole) Reset() { *m = ProjectRole{} } func (*ProjectRole) ProtoMessage() {} func (*ProjectRole) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{83} + return fileDescriptor_030104ce3b95bcac, []int{97} } func (m *ProjectRole) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2393,7 +2788,7 @@ var xxx_messageInfo_ProjectRole proto.InternalMessageInfo func (m *PullRequestGenerator) Reset() { *m = PullRequestGenerator{} } func (*PullRequestGenerator) ProtoMessage() {} func (*PullRequestGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{84} + return fileDescriptor_030104ce3b95bcac, []int{98} } func (m *PullRequestGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2418,10 +2813,66 @@ func (m *PullRequestGenerator) XXX_DiscardUnknown() { var xxx_messageInfo_PullRequestGenerator proto.InternalMessageInfo +func (m *PullRequestGeneratorAzureDevOps) Reset() { *m = PullRequestGeneratorAzureDevOps{} } +func (*PullRequestGeneratorAzureDevOps) ProtoMessage() {} +func (*PullRequestGeneratorAzureDevOps) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{99} +} +func (m *PullRequestGeneratorAzureDevOps) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PullRequestGeneratorAzureDevOps) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *PullRequestGeneratorAzureDevOps) XXX_Merge(src proto.Message) { + xxx_messageInfo_PullRequestGeneratorAzureDevOps.Merge(m, src) +} +func (m *PullRequestGeneratorAzureDevOps) XXX_Size() int { + return m.Size() +} +func (m *PullRequestGeneratorAzureDevOps) XXX_DiscardUnknown() { + xxx_messageInfo_PullRequestGeneratorAzureDevOps.DiscardUnknown(m) +} + +var xxx_messageInfo_PullRequestGeneratorAzureDevOps proto.InternalMessageInfo + +func (m *PullRequestGeneratorBitbucket) Reset() { *m = PullRequestGeneratorBitbucket{} } +func (*PullRequestGeneratorBitbucket) ProtoMessage() {} +func (*PullRequestGeneratorBitbucket) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{100} +} +func (m *PullRequestGeneratorBitbucket) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PullRequestGeneratorBitbucket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *PullRequestGeneratorBitbucket) XXX_Merge(src proto.Message) { + xxx_messageInfo_PullRequestGeneratorBitbucket.Merge(m, src) +} +func (m *PullRequestGeneratorBitbucket) XXX_Size() int { + return m.Size() +} +func (m *PullRequestGeneratorBitbucket) XXX_DiscardUnknown() { + xxx_messageInfo_PullRequestGeneratorBitbucket.DiscardUnknown(m) +} + +var xxx_messageInfo_PullRequestGeneratorBitbucket proto.InternalMessageInfo + func (m *PullRequestGeneratorBitbucketServer) Reset() { *m = PullRequestGeneratorBitbucketServer{} } func (*PullRequestGeneratorBitbucketServer) ProtoMessage() {} func (*PullRequestGeneratorBitbucketServer) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{85} + return fileDescriptor_030104ce3b95bcac, []int{101} } func (m *PullRequestGeneratorBitbucketServer) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2449,7 +2900,7 @@ var xxx_messageInfo_PullRequestGeneratorBitbucketServer proto.InternalMessageInf func (m *PullRequestGeneratorFilter) Reset() { *m = PullRequestGeneratorFilter{} } func (*PullRequestGeneratorFilter) ProtoMessage() {} func (*PullRequestGeneratorFilter) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{86} + return fileDescriptor_030104ce3b95bcac, []int{102} } func (m *PullRequestGeneratorFilter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2477,7 +2928,7 @@ var xxx_messageInfo_PullRequestGeneratorFilter proto.InternalMessageInfo func (m *PullRequestGeneratorGitLab) Reset() { *m = PullRequestGeneratorGitLab{} } func (*PullRequestGeneratorGitLab) ProtoMessage() {} func (*PullRequestGeneratorGitLab) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{87} + return fileDescriptor_030104ce3b95bcac, []int{103} } func (m *PullRequestGeneratorGitLab) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2505,7 +2956,7 @@ var xxx_messageInfo_PullRequestGeneratorGitLab proto.InternalMessageInfo func (m *PullRequestGeneratorGitea) Reset() { *m = PullRequestGeneratorGitea{} } func (*PullRequestGeneratorGitea) ProtoMessage() {} func (*PullRequestGeneratorGitea) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{88} + return fileDescriptor_030104ce3b95bcac, []int{104} } func (m *PullRequestGeneratorGitea) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2533,7 +2984,7 @@ var xxx_messageInfo_PullRequestGeneratorGitea proto.InternalMessageInfo func (m *PullRequestGeneratorGithub) Reset() { *m = PullRequestGeneratorGithub{} } func (*PullRequestGeneratorGithub) ProtoMessage() {} func (*PullRequestGeneratorGithub) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{89} + return fileDescriptor_030104ce3b95bcac, []int{105} } func (m *PullRequestGeneratorGithub) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2561,7 +3012,7 @@ var xxx_messageInfo_PullRequestGeneratorGithub proto.InternalMessageInfo func (m *RefTarget) Reset() { *m = RefTarget{} } func (*RefTarget) ProtoMessage() {} func (*RefTarget) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{90} + return fileDescriptor_030104ce3b95bcac, []int{106} } func (m *RefTarget) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2589,7 +3040,7 @@ var xxx_messageInfo_RefTarget proto.InternalMessageInfo func (m *RepoCreds) Reset() { *m = RepoCreds{} } func (*RepoCreds) ProtoMessage() {} func (*RepoCreds) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{91} + return fileDescriptor_030104ce3b95bcac, []int{107} } func (m *RepoCreds) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2617,7 +3068,7 @@ var xxx_messageInfo_RepoCreds proto.InternalMessageInfo func (m *RepoCredsList) Reset() { *m = RepoCredsList{} } func (*RepoCredsList) ProtoMessage() {} func (*RepoCredsList) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{92} + return fileDescriptor_030104ce3b95bcac, []int{108} } func (m *RepoCredsList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2645,7 +3096,7 @@ var xxx_messageInfo_RepoCredsList proto.InternalMessageInfo func (m *Repository) Reset() { *m = Repository{} } func (*Repository) ProtoMessage() {} func (*Repository) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{93} + return fileDescriptor_030104ce3b95bcac, []int{109} } func (m *Repository) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2673,7 +3124,7 @@ var xxx_messageInfo_Repository proto.InternalMessageInfo func (m *RepositoryCertificate) Reset() { *m = RepositoryCertificate{} } func (*RepositoryCertificate) ProtoMessage() {} func (*RepositoryCertificate) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{94} + return fileDescriptor_030104ce3b95bcac, []int{110} } func (m *RepositoryCertificate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2701,7 +3152,7 @@ var xxx_messageInfo_RepositoryCertificate proto.InternalMessageInfo func (m *RepositoryCertificateList) Reset() { *m = RepositoryCertificateList{} } func (*RepositoryCertificateList) ProtoMessage() {} func (*RepositoryCertificateList) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{95} + return fileDescriptor_030104ce3b95bcac, []int{111} } func (m *RepositoryCertificateList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2729,7 +3180,7 @@ var xxx_messageInfo_RepositoryCertificateList proto.InternalMessageInfo func (m *RepositoryList) Reset() { *m = RepositoryList{} } func (*RepositoryList) ProtoMessage() {} func (*RepositoryList) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{96} + return fileDescriptor_030104ce3b95bcac, []int{112} } func (m *RepositoryList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2757,7 +3208,7 @@ var xxx_messageInfo_RepositoryList proto.InternalMessageInfo func (m *ResourceAction) Reset() { *m = ResourceAction{} } func (*ResourceAction) ProtoMessage() {} func (*ResourceAction) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{97} + return fileDescriptor_030104ce3b95bcac, []int{113} } func (m *ResourceAction) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2785,7 +3236,7 @@ var xxx_messageInfo_ResourceAction proto.InternalMessageInfo func (m *ResourceActionDefinition) Reset() { *m = ResourceActionDefinition{} } func (*ResourceActionDefinition) ProtoMessage() {} func (*ResourceActionDefinition) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{98} + return fileDescriptor_030104ce3b95bcac, []int{114} } func (m *ResourceActionDefinition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2813,7 +3264,7 @@ var xxx_messageInfo_ResourceActionDefinition proto.InternalMessageInfo func (m *ResourceActionParam) Reset() { *m = ResourceActionParam{} } func (*ResourceActionParam) ProtoMessage() {} func (*ResourceActionParam) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{99} + return fileDescriptor_030104ce3b95bcac, []int{115} } func (m *ResourceActionParam) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2841,7 +3292,7 @@ var xxx_messageInfo_ResourceActionParam proto.InternalMessageInfo func (m *ResourceActions) Reset() { *m = ResourceActions{} } func (*ResourceActions) ProtoMessage() {} func (*ResourceActions) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{100} + return fileDescriptor_030104ce3b95bcac, []int{116} } func (m *ResourceActions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2869,7 +3320,7 @@ var xxx_messageInfo_ResourceActions proto.InternalMessageInfo func (m *ResourceDiff) Reset() { *m = ResourceDiff{} } func (*ResourceDiff) ProtoMessage() {} func (*ResourceDiff) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{101} + return fileDescriptor_030104ce3b95bcac, []int{117} } func (m *ResourceDiff) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2897,7 +3348,7 @@ var xxx_messageInfo_ResourceDiff proto.InternalMessageInfo func (m *ResourceIgnoreDifferences) Reset() { *m = ResourceIgnoreDifferences{} } func (*ResourceIgnoreDifferences) ProtoMessage() {} func (*ResourceIgnoreDifferences) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{102} + return fileDescriptor_030104ce3b95bcac, []int{118} } func (m *ResourceIgnoreDifferences) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2925,7 +3376,7 @@ var xxx_messageInfo_ResourceIgnoreDifferences proto.InternalMessageInfo func (m *ResourceNetworkingInfo) Reset() { *m = ResourceNetworkingInfo{} } func (*ResourceNetworkingInfo) ProtoMessage() {} func (*ResourceNetworkingInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{103} + return fileDescriptor_030104ce3b95bcac, []int{119} } func (m *ResourceNetworkingInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2953,7 +3404,7 @@ var xxx_messageInfo_ResourceNetworkingInfo proto.InternalMessageInfo func (m *ResourceNode) Reset() { *m = ResourceNode{} } func (*ResourceNode) ProtoMessage() {} func (*ResourceNode) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{104} + return fileDescriptor_030104ce3b95bcac, []int{120} } func (m *ResourceNode) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2981,7 +3432,7 @@ var xxx_messageInfo_ResourceNode proto.InternalMessageInfo func (m *ResourceOverride) Reset() { *m = ResourceOverride{} } func (*ResourceOverride) ProtoMessage() {} func (*ResourceOverride) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{105} + return fileDescriptor_030104ce3b95bcac, []int{121} } func (m *ResourceOverride) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3009,7 +3460,7 @@ var xxx_messageInfo_ResourceOverride proto.InternalMessageInfo func (m *ResourceRef) Reset() { *m = ResourceRef{} } func (*ResourceRef) ProtoMessage() {} func (*ResourceRef) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{106} + return fileDescriptor_030104ce3b95bcac, []int{122} } func (m *ResourceRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3037,7 +3488,7 @@ var xxx_messageInfo_ResourceRef proto.InternalMessageInfo func (m *ResourceResult) Reset() { *m = ResourceResult{} } func (*ResourceResult) ProtoMessage() {} func (*ResourceResult) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{107} + return fileDescriptor_030104ce3b95bcac, []int{123} } func (m *ResourceResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3065,7 +3516,7 @@ var xxx_messageInfo_ResourceResult proto.InternalMessageInfo func (m *ResourceStatus) Reset() { *m = ResourceStatus{} } func (*ResourceStatus) ProtoMessage() {} func (*ResourceStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{108} + return fileDescriptor_030104ce3b95bcac, []int{124} } func (m *ResourceStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3093,7 +3544,7 @@ var xxx_messageInfo_ResourceStatus proto.InternalMessageInfo func (m *RetryStrategy) Reset() { *m = RetryStrategy{} } func (*RetryStrategy) ProtoMessage() {} func (*RetryStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{109} + return fileDescriptor_030104ce3b95bcac, []int{125} } func (m *RetryStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3121,7 +3572,7 @@ var xxx_messageInfo_RetryStrategy proto.InternalMessageInfo func (m *RevisionHistory) Reset() { *m = RevisionHistory{} } func (*RevisionHistory) ProtoMessage() {} func (*RevisionHistory) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{110} + return fileDescriptor_030104ce3b95bcac, []int{126} } func (m *RevisionHistory) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3149,7 +3600,7 @@ var xxx_messageInfo_RevisionHistory proto.InternalMessageInfo func (m *RevisionMetadata) Reset() { *m = RevisionMetadata{} } func (*RevisionMetadata) ProtoMessage() {} func (*RevisionMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{111} + return fileDescriptor_030104ce3b95bcac, []int{127} } func (m *RevisionMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3177,7 +3628,7 @@ var xxx_messageInfo_RevisionMetadata proto.InternalMessageInfo func (m *SCMProviderGenerator) Reset() { *m = SCMProviderGenerator{} } func (*SCMProviderGenerator) ProtoMessage() {} func (*SCMProviderGenerator) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{112} + return fileDescriptor_030104ce3b95bcac, []int{128} } func (m *SCMProviderGenerator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3202,10 +3653,38 @@ func (m *SCMProviderGenerator) XXX_DiscardUnknown() { var xxx_messageInfo_SCMProviderGenerator proto.InternalMessageInfo +func (m *SCMProviderGeneratorAWSCodeCommit) Reset() { *m = SCMProviderGeneratorAWSCodeCommit{} } +func (*SCMProviderGeneratorAWSCodeCommit) ProtoMessage() {} +func (*SCMProviderGeneratorAWSCodeCommit) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{129} +} +func (m *SCMProviderGeneratorAWSCodeCommit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SCMProviderGeneratorAWSCodeCommit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *SCMProviderGeneratorAWSCodeCommit) XXX_Merge(src proto.Message) { + xxx_messageInfo_SCMProviderGeneratorAWSCodeCommit.Merge(m, src) +} +func (m *SCMProviderGeneratorAWSCodeCommit) XXX_Size() int { + return m.Size() +} +func (m *SCMProviderGeneratorAWSCodeCommit) XXX_DiscardUnknown() { + xxx_messageInfo_SCMProviderGeneratorAWSCodeCommit.DiscardUnknown(m) +} + +var xxx_messageInfo_SCMProviderGeneratorAWSCodeCommit proto.InternalMessageInfo + func (m *SCMProviderGeneratorAzureDevOps) Reset() { *m = SCMProviderGeneratorAzureDevOps{} } func (*SCMProviderGeneratorAzureDevOps) ProtoMessage() {} func (*SCMProviderGeneratorAzureDevOps) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{113} + return fileDescriptor_030104ce3b95bcac, []int{130} } func (m *SCMProviderGeneratorAzureDevOps) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3233,7 +3712,7 @@ var xxx_messageInfo_SCMProviderGeneratorAzureDevOps proto.InternalMessageInfo func (m *SCMProviderGeneratorBitbucket) Reset() { *m = SCMProviderGeneratorBitbucket{} } func (*SCMProviderGeneratorBitbucket) ProtoMessage() {} func (*SCMProviderGeneratorBitbucket) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{114} + return fileDescriptor_030104ce3b95bcac, []int{131} } func (m *SCMProviderGeneratorBitbucket) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3261,7 +3740,7 @@ var xxx_messageInfo_SCMProviderGeneratorBitbucket proto.InternalMessageInfo func (m *SCMProviderGeneratorBitbucketServer) Reset() { *m = SCMProviderGeneratorBitbucketServer{} } func (*SCMProviderGeneratorBitbucketServer) ProtoMessage() {} func (*SCMProviderGeneratorBitbucketServer) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{115} + return fileDescriptor_030104ce3b95bcac, []int{132} } func (m *SCMProviderGeneratorBitbucketServer) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3289,7 +3768,7 @@ var xxx_messageInfo_SCMProviderGeneratorBitbucketServer proto.InternalMessageInf func (m *SCMProviderGeneratorFilter) Reset() { *m = SCMProviderGeneratorFilter{} } func (*SCMProviderGeneratorFilter) ProtoMessage() {} func (*SCMProviderGeneratorFilter) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{116} + return fileDescriptor_030104ce3b95bcac, []int{133} } func (m *SCMProviderGeneratorFilter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3317,7 +3796,7 @@ var xxx_messageInfo_SCMProviderGeneratorFilter proto.InternalMessageInfo func (m *SCMProviderGeneratorGitea) Reset() { *m = SCMProviderGeneratorGitea{} } func (*SCMProviderGeneratorGitea) ProtoMessage() {} func (*SCMProviderGeneratorGitea) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{117} + return fileDescriptor_030104ce3b95bcac, []int{134} } func (m *SCMProviderGeneratorGitea) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3345,7 +3824,7 @@ var xxx_messageInfo_SCMProviderGeneratorGitea proto.InternalMessageInfo func (m *SCMProviderGeneratorGithub) Reset() { *m = SCMProviderGeneratorGithub{} } func (*SCMProviderGeneratorGithub) ProtoMessage() {} func (*SCMProviderGeneratorGithub) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{118} + return fileDescriptor_030104ce3b95bcac, []int{135} } func (m *SCMProviderGeneratorGithub) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3373,7 +3852,7 @@ var xxx_messageInfo_SCMProviderGeneratorGithub proto.InternalMessageInfo func (m *SCMProviderGeneratorGitlab) Reset() { *m = SCMProviderGeneratorGitlab{} } func (*SCMProviderGeneratorGitlab) ProtoMessage() {} func (*SCMProviderGeneratorGitlab) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{119} + return fileDescriptor_030104ce3b95bcac, []int{136} } func (m *SCMProviderGeneratorGitlab) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3401,7 +3880,7 @@ var xxx_messageInfo_SCMProviderGeneratorGitlab proto.InternalMessageInfo func (m *SecretRef) Reset() { *m = SecretRef{} } func (*SecretRef) ProtoMessage() {} func (*SecretRef) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{120} + return fileDescriptor_030104ce3b95bcac, []int{137} } func (m *SecretRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3429,7 +3908,7 @@ var xxx_messageInfo_SecretRef proto.InternalMessageInfo func (m *SignatureKey) Reset() { *m = SignatureKey{} } func (*SignatureKey) ProtoMessage() {} func (*SignatureKey) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{121} + return fileDescriptor_030104ce3b95bcac, []int{138} } func (m *SignatureKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3457,7 +3936,7 @@ var xxx_messageInfo_SignatureKey proto.InternalMessageInfo func (m *SyncOperation) Reset() { *m = SyncOperation{} } func (*SyncOperation) ProtoMessage() {} func (*SyncOperation) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{122} + return fileDescriptor_030104ce3b95bcac, []int{139} } func (m *SyncOperation) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3485,7 +3964,7 @@ var xxx_messageInfo_SyncOperation proto.InternalMessageInfo func (m *SyncOperationResource) Reset() { *m = SyncOperationResource{} } func (*SyncOperationResource) ProtoMessage() {} func (*SyncOperationResource) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{123} + return fileDescriptor_030104ce3b95bcac, []int{140} } func (m *SyncOperationResource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3513,7 +3992,7 @@ var xxx_messageInfo_SyncOperationResource proto.InternalMessageInfo func (m *SyncOperationResult) Reset() { *m = SyncOperationResult{} } func (*SyncOperationResult) ProtoMessage() {} func (*SyncOperationResult) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{124} + return fileDescriptor_030104ce3b95bcac, []int{141} } func (m *SyncOperationResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3541,7 +4020,7 @@ var xxx_messageInfo_SyncOperationResult proto.InternalMessageInfo func (m *SyncPolicy) Reset() { *m = SyncPolicy{} } func (*SyncPolicy) ProtoMessage() {} func (*SyncPolicy) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{125} + return fileDescriptor_030104ce3b95bcac, []int{142} } func (m *SyncPolicy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3569,7 +4048,7 @@ var xxx_messageInfo_SyncPolicy proto.InternalMessageInfo func (m *SyncPolicyAutomated) Reset() { *m = SyncPolicyAutomated{} } func (*SyncPolicyAutomated) ProtoMessage() {} func (*SyncPolicyAutomated) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{126} + return fileDescriptor_030104ce3b95bcac, []int{143} } func (m *SyncPolicyAutomated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3597,7 +4076,7 @@ var xxx_messageInfo_SyncPolicyAutomated proto.InternalMessageInfo func (m *SyncStatus) Reset() { *m = SyncStatus{} } func (*SyncStatus) ProtoMessage() {} func (*SyncStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{127} + return fileDescriptor_030104ce3b95bcac, []int{144} } func (m *SyncStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3625,7 +4104,7 @@ var xxx_messageInfo_SyncStatus proto.InternalMessageInfo func (m *SyncStrategy) Reset() { *m = SyncStrategy{} } func (*SyncStrategy) ProtoMessage() {} func (*SyncStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{128} + return fileDescriptor_030104ce3b95bcac, []int{145} } func (m *SyncStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3653,7 +4132,7 @@ var xxx_messageInfo_SyncStrategy proto.InternalMessageInfo func (m *SyncStrategyApply) Reset() { *m = SyncStrategyApply{} } func (*SyncStrategyApply) ProtoMessage() {} func (*SyncStrategyApply) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{129} + return fileDescriptor_030104ce3b95bcac, []int{146} } func (m *SyncStrategyApply) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3681,7 +4160,7 @@ var xxx_messageInfo_SyncStrategyApply proto.InternalMessageInfo func (m *SyncStrategyHook) Reset() { *m = SyncStrategyHook{} } func (*SyncStrategyHook) ProtoMessage() {} func (*SyncStrategyHook) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{130} + return fileDescriptor_030104ce3b95bcac, []int{147} } func (m *SyncStrategyHook) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3709,7 +4188,7 @@ var xxx_messageInfo_SyncStrategyHook proto.InternalMessageInfo func (m *SyncWindow) Reset() { *m = SyncWindow{} } func (*SyncWindow) ProtoMessage() {} func (*SyncWindow) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{131} + return fileDescriptor_030104ce3b95bcac, []int{148} } func (m *SyncWindow) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3737,7 +4216,7 @@ var xxx_messageInfo_SyncWindow proto.InternalMessageInfo func (m *TLSClientConfig) Reset() { *m = TLSClientConfig{} } func (*TLSClientConfig) ProtoMessage() {} func (*TLSClientConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_030104ce3b95bcac, []int{132} + return fileDescriptor_030104ce3b95bcac, []int{149} } func (m *TLSClientConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3762,6 +4241,34 @@ func (m *TLSClientConfig) XXX_DiscardUnknown() { var xxx_messageInfo_TLSClientConfig proto.InternalMessageInfo +func (m *TagFilter) Reset() { *m = TagFilter{} } +func (*TagFilter) ProtoMessage() {} +func (*TagFilter) Descriptor() ([]byte, []int) { + return fileDescriptor_030104ce3b95bcac, []int{150} +} +func (m *TagFilter) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TagFilter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *TagFilter) XXX_Merge(src proto.Message) { + xxx_messageInfo_TagFilter.Merge(m, src) +} +func (m *TagFilter) XXX_Size() int { + return m.Size() +} +func (m *TagFilter) XXX_DiscardUnknown() { + xxx_messageInfo_TagFilter.DiscardUnknown(m) +} + +var xxx_messageInfo_TagFilter proto.InternalMessageInfo + func init() { proto.RegisterType((*AWSAuthConfig)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.AWSAuthConfig") proto.RegisterType((*AppProject)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.AppProject") @@ -3774,12 +4281,14 @@ func init() { proto.RegisterType((*ApplicationDestination)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationDestination") proto.RegisterType((*ApplicationList)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationList") proto.RegisterType((*ApplicationMatchExpression)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationMatchExpression") + proto.RegisterType((*ApplicationPreservedFields)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationPreservedFields") proto.RegisterType((*ApplicationSet)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSet") proto.RegisterType((*ApplicationSetApplicationStatus)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetApplicationStatus") proto.RegisterType((*ApplicationSetCondition)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetCondition") proto.RegisterType((*ApplicationSetGenerator)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetGenerator") proto.RegisterType((*ApplicationSetList)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetList") proto.RegisterType((*ApplicationSetNestedGenerator)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetNestedGenerator") + proto.RegisterType((*ApplicationSetResourceIgnoreDifferences)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetResourceIgnoreDifferences") proto.RegisterType((*ApplicationSetRolloutStep)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetRolloutStep") proto.RegisterType((*ApplicationSetRolloutStrategy)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetRolloutStrategy") proto.RegisterType((*ApplicationSetSpec)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSetSpec") @@ -3800,7 +4309,6 @@ func init() { proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSourceKustomize.CommonLabelsEntry") proto.RegisterType((*ApplicationSourcePlugin)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSourcePlugin") proto.RegisterType((*ApplicationSourcePluginParameter)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSourcePluginParameter") - proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSourcePluginParameter.MapEntry") proto.RegisterType((*ApplicationSpec)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSpec") proto.RegisterType((*ApplicationStatus)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationStatus") proto.RegisterType((*ApplicationSummary)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSummary") @@ -3808,6 +4316,8 @@ func init() { proto.RegisterType((*ApplicationWatchEvent)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationWatchEvent") proto.RegisterType((*Backoff)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Backoff") proto.RegisterType((*BasicAuthBitbucketServer)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.BasicAuthBitbucketServer") + proto.RegisterType((*BearerTokenBitbucketCloud)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.BearerTokenBitbucketCloud") + proto.RegisterType((*ChartDetails)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ChartDetails") proto.RegisterType((*Cluster)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Cluster") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Cluster.AnnotationsEntry") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Cluster.LabelsEntry") @@ -3830,6 +4340,7 @@ func init() { proto.RegisterType((*GitDirectoryGeneratorItem)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.GitDirectoryGeneratorItem") proto.RegisterType((*GitFileGeneratorItem)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.GitFileGeneratorItem") proto.RegisterType((*GitGenerator)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.GitGenerator") + proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.GitGenerator.ValuesEntry") proto.RegisterType((*GnuPGPublicKey)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.GnuPGPublicKey") proto.RegisterType((*GnuPGPublicKeyList)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.GnuPGPublicKeyList") proto.RegisterType((*HealthStatus)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.HealthStatus") @@ -3844,7 +4355,13 @@ func init() { proto.RegisterType((*JWTTokens)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.JWTTokens") proto.RegisterType((*JsonnetVar)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.JsonnetVar") proto.RegisterType((*KnownTypeField)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.KnownTypeField") + proto.RegisterType((*KustomizeGvk)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.KustomizeGvk") proto.RegisterType((*KustomizeOptions)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.KustomizeOptions") + proto.RegisterType((*KustomizePatch)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.KustomizePatch") + proto.RegisterMapType((map[string]bool)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.KustomizePatch.OptionsEntry") + proto.RegisterType((*KustomizeReplica)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.KustomizeReplica") + proto.RegisterType((*KustomizeResId)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.KustomizeResId") + proto.RegisterType((*KustomizeSelector)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.KustomizeSelector") proto.RegisterType((*ListGenerator)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ListGenerator") proto.RegisterType((*ManagedNamespaceMetadata)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ManagedNamespaceMetadata") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ManagedNamespaceMetadata.AnnotationsEntry") @@ -3856,11 +4373,21 @@ func init() { proto.RegisterType((*Operation)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Operation") proto.RegisterType((*OperationInitiator)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.OperationInitiator") proto.RegisterType((*OperationState)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.OperationState") + proto.RegisterType((*OptionalArray)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.OptionalArray") + proto.RegisterType((*OptionalMap)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.OptionalMap") + proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.OptionalMap.MapEntry") proto.RegisterType((*OrphanedResourceKey)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.OrphanedResourceKey") proto.RegisterType((*OrphanedResourcesMonitorSettings)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.OrphanedResourcesMonitorSettings") proto.RegisterType((*OverrideIgnoreDiff)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.OverrideIgnoreDiff") + proto.RegisterType((*PluginConfigMapRef)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.PluginConfigMapRef") + proto.RegisterType((*PluginGenerator)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.PluginGenerator") + proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.PluginGenerator.ValuesEntry") + proto.RegisterType((*PluginInput)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.PluginInput") + proto.RegisterMapType((PluginParameters)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.PluginInput.ParametersEntry") proto.RegisterType((*ProjectRole)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ProjectRole") proto.RegisterType((*PullRequestGenerator)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.PullRequestGenerator") + proto.RegisterType((*PullRequestGeneratorAzureDevOps)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.PullRequestGeneratorAzureDevOps") + proto.RegisterType((*PullRequestGeneratorBitbucket)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.PullRequestGeneratorBitbucket") proto.RegisterType((*PullRequestGeneratorBitbucketServer)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.PullRequestGeneratorBitbucketServer") proto.RegisterType((*PullRequestGeneratorFilter)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.PullRequestGeneratorFilter") proto.RegisterType((*PullRequestGeneratorGitLab)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.PullRequestGeneratorGitLab") @@ -3891,6 +4418,8 @@ func init() { proto.RegisterType((*RevisionHistory)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.RevisionHistory") proto.RegisterType((*RevisionMetadata)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.RevisionMetadata") proto.RegisterType((*SCMProviderGenerator)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.SCMProviderGenerator") + proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.SCMProviderGenerator.ValuesEntry") + proto.RegisterType((*SCMProviderGeneratorAWSCodeCommit)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.SCMProviderGeneratorAWSCodeCommit") proto.RegisterType((*SCMProviderGeneratorAzureDevOps)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.SCMProviderGeneratorAzureDevOps") proto.RegisterType((*SCMProviderGeneratorBitbucket)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.SCMProviderGeneratorBitbucket") proto.RegisterType((*SCMProviderGeneratorBitbucketServer)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.SCMProviderGeneratorBitbucketServer") @@ -3911,6 +4440,7 @@ func init() { proto.RegisterType((*SyncStrategyHook)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.SyncStrategyHook") proto.RegisterType((*SyncWindow)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.SyncWindow") proto.RegisterType((*TLSClientConfig)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.TLSClientConfig") + proto.RegisterType((*TagFilter)(nil), "github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.TagFilter") } func init() { @@ -3918,609 +4448,697 @@ func init() { } var fileDescriptor_030104ce3b95bcac = []byte{ - // 9618 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6f, 0x70, 0x24, 0xc7, - 0x75, 0x18, 0xae, 0xd9, 0xc5, 0x02, 0xbb, 0x0f, 0x7f, 0xee, 0xd0, 0x77, 0x47, 0x82, 0x47, 0xf2, - 0x70, 0x35, 0x2c, 0x53, 0xd4, 0x4f, 0x24, 0xf0, 0xe3, 0x89, 0x52, 0x18, 0xd3, 0xa6, 0x8c, 0x05, - 0xee, 0x70, 0xb8, 0x03, 0x0e, 0x60, 0x03, 0x77, 0x27, 0x91, 0xa6, 0xa4, 0xc1, 0x6c, 0xef, 0x62, - 0x0e, 0xb3, 0x33, 0xcb, 0x99, 0x59, 0x1c, 0x96, 0x96, 0x65, 0x49, 0x96, 0x6d, 0x25, 0xfa, 0x43, - 0x85, 0xfe, 0x10, 0xb9, 0x92, 0xd8, 0x8a, 0xed, 0x72, 0xc5, 0x95, 0xa8, 0xe2, 0x54, 0x3e, 0x24, - 0x4e, 0x2a, 0x55, 0x89, 0x9d, 0x0f, 0x4c, 0x29, 0x55, 0x51, 0x55, 0x5c, 0x96, 0x13, 0x3b, 0x30, - 0x75, 0xa9, 0x54, 0x52, 0xa9, 0x8a, 0x53, 0xf9, 0xf3, 0x25, 0x57, 0xf9, 0x90, 0xea, 0xff, 0x3d, - 0xb3, 0xbb, 0x87, 0xdd, 0xc3, 0xe0, 0xee, 0xa4, 0xe2, 0xb7, 0xdd, 0x7e, 0x6f, 0xde, 0xeb, 0xe9, - 0xe9, 0x7e, 0xfd, 0x5e, 0xbf, 0x3f, 0x0d, 0xab, 0x0d, 0x2f, 0xd9, 0x69, 0x6f, 0xcf, 0xb9, 0x61, - 0x73, 0xde, 0x89, 0x1a, 0x61, 0x2b, 0x0a, 0x6f, 0xb1, 0x1f, 0x2f, 0xb8, 0xb5, 0xf9, 0xbd, 0x0b, - 0xf3, 0xad, 0xdd, 0xc6, 0xbc, 0xd3, 0xf2, 0xe2, 0x79, 0xa7, 0xd5, 0xf2, 0x3d, 0xd7, 0x49, 0xbc, - 0x30, 0x98, 0xdf, 0x7b, 0xd1, 0xf1, 0x5b, 0x3b, 0xce, 0x8b, 0xf3, 0x0d, 0x12, 0x90, 0xc8, 0x49, - 0x48, 0x6d, 0xae, 0x15, 0x85, 0x49, 0x88, 0x7e, 0x4a, 0x53, 0x9b, 0x93, 0xd4, 0xd8, 0x8f, 0xcf, - 0xba, 0xb5, 0xb9, 0xbd, 0x0b, 0x73, 0xad, 0xdd, 0xc6, 0x1c, 0xa5, 0x36, 0x67, 0x50, 0x9b, 0x93, - 0xd4, 0xce, 0xbe, 0x60, 0xf4, 0xa5, 0x11, 0x36, 0xc2, 0x79, 0x46, 0x74, 0xbb, 0x5d, 0x67, 0xff, - 0xd8, 0x1f, 0xf6, 0x8b, 0x33, 0x3b, 0x6b, 0xef, 0xbe, 0x1c, 0xcf, 0x79, 0x21, 0xed, 0xde, 0xbc, - 0x1b, 0x46, 0x64, 0x7e, 0xaf, 0xab, 0x43, 0x67, 0x2f, 0x6b, 0x1c, 0xb2, 0x9f, 0x90, 0x20, 0xf6, - 0xc2, 0x20, 0x7e, 0x81, 0x76, 0x81, 0x44, 0x7b, 0x24, 0x32, 0x5f, 0xcf, 0x40, 0xe8, 0x45, 0xe9, - 0x25, 0x4d, 0xa9, 0xe9, 0xb8, 0x3b, 0x5e, 0x40, 0xa2, 0x8e, 0x7e, 0xbc, 0x49, 0x12, 0xa7, 0xd7, - 0x53, 0xf3, 0xfd, 0x9e, 0x8a, 0xda, 0x41, 0xe2, 0x35, 0x49, 0xd7, 0x03, 0x9f, 0x38, 0xec, 0x81, - 0xd8, 0xdd, 0x21, 0x4d, 0xa7, 0xeb, 0xb9, 0x8f, 0xf5, 0x7b, 0xae, 0x9d, 0x78, 0xfe, 0xbc, 0x17, - 0x24, 0x71, 0x12, 0x65, 0x1f, 0xb2, 0xdf, 0x82, 0xc9, 0x85, 0x9b, 0x9b, 0x0b, 0xed, 0x64, 0x67, - 0x31, 0x0c, 0xea, 0x5e, 0x03, 0x7d, 0x1c, 0xc6, 0x5d, 0xbf, 0x1d, 0x27, 0x24, 0xba, 0xe6, 0x34, - 0xc9, 0x8c, 0x75, 0xde, 0x7a, 0xae, 0x52, 0x3d, 0xf5, 0xde, 0xc1, 0xec, 0x87, 0xee, 0x1c, 0xcc, - 0x8e, 0x2f, 0x6a, 0x10, 0x36, 0xf1, 0xd0, 0x47, 0x60, 0x2c, 0x0a, 0x7d, 0xb2, 0x80, 0xaf, 0xcd, - 0x14, 0xd8, 0x23, 0x27, 0xc4, 0x23, 0x63, 0x98, 0x37, 0x63, 0x09, 0xb7, 0xff, 0xb8, 0x00, 0xb0, - 0xd0, 0x6a, 0x6d, 0x44, 0xe1, 0x2d, 0xe2, 0x26, 0xe8, 0x73, 0x50, 0xa6, 0x43, 0x57, 0x73, 0x12, - 0x87, 0x71, 0x1b, 0xbf, 0xf0, 0xff, 0xcf, 0xf1, 0x37, 0x99, 0x33, 0xdf, 0x44, 0x4f, 0x1c, 0x8a, - 0x3d, 0xb7, 0xf7, 0xe2, 0xdc, 0xfa, 0x36, 0x7d, 0x7e, 0x8d, 0x24, 0x4e, 0x15, 0x09, 0x66, 0xa0, - 0xdb, 0xb0, 0xa2, 0x8a, 0x02, 0x18, 0x89, 0x5b, 0xc4, 0x65, 0x1d, 0x1b, 0xbf, 0xb0, 0x3a, 0x77, - 0x94, 0x19, 0x3a, 0xa7, 0x7b, 0xbe, 0xd9, 0x22, 0x6e, 0x75, 0x42, 0x70, 0x1e, 0xa1, 0xff, 0x30, - 0xe3, 0x83, 0xf6, 0x60, 0x34, 0x4e, 0x9c, 0xa4, 0x1d, 0xcf, 0x14, 0x19, 0xc7, 0x6b, 0xb9, 0x71, - 0x64, 0x54, 0xab, 0x53, 0x82, 0xe7, 0x28, 0xff, 0x8f, 0x05, 0x37, 0xfb, 0x3f, 0x58, 0x30, 0xa5, - 0x91, 0x57, 0xbd, 0x38, 0x41, 0x3f, 0xdb, 0x35, 0xb8, 0x73, 0x83, 0x0d, 0x2e, 0x7d, 0x9a, 0x0d, - 0xed, 0x49, 0xc1, 0xac, 0x2c, 0x5b, 0x8c, 0x81, 0x6d, 0x42, 0xc9, 0x4b, 0x48, 0x33, 0x9e, 0x29, - 0x9c, 0x2f, 0x3e, 0x37, 0x7e, 0xe1, 0x72, 0x5e, 0xef, 0x59, 0x9d, 0x14, 0x4c, 0x4b, 0x2b, 0x94, - 0x3c, 0xe6, 0x5c, 0xec, 0xdf, 0x9d, 0x30, 0xdf, 0x8f, 0x0e, 0x38, 0x7a, 0x11, 0xc6, 0xe3, 0xb0, - 0x1d, 0xb9, 0x04, 0x93, 0x56, 0x18, 0xcf, 0x58, 0xe7, 0x8b, 0x74, 0xea, 0xd1, 0x99, 0xba, 0xa9, - 0x9b, 0xb1, 0x89, 0x83, 0xbe, 0x69, 0xc1, 0x44, 0x8d, 0xc4, 0x89, 0x17, 0x30, 0xfe, 0xb2, 0xf3, - 0x5b, 0x47, 0xee, 0xbc, 0x6c, 0x5c, 0xd2, 0xc4, 0xab, 0xa7, 0xc5, 0x8b, 0x4c, 0x18, 0x8d, 0x31, - 0x4e, 0xf1, 0xa7, 0x2b, 0xae, 0x46, 0x62, 0x37, 0xf2, 0x5a, 0xf4, 0x3f, 0x9b, 0x33, 0xc6, 0x8a, - 0x5b, 0xd2, 0x20, 0x6c, 0xe2, 0xa1, 0x00, 0x4a, 0x74, 0x45, 0xc5, 0x33, 0x23, 0xac, 0xff, 0x2b, - 0x47, 0xeb, 0xbf, 0x18, 0x54, 0xba, 0x58, 0xf5, 0xe8, 0xd3, 0x7f, 0x31, 0xe6, 0x6c, 0xd0, 0x37, - 0x2c, 0x98, 0x11, 0x2b, 0x1e, 0x13, 0x3e, 0xa0, 0x37, 0x77, 0xbc, 0x84, 0xf8, 0x5e, 0x9c, 0xcc, - 0x94, 0x58, 0x1f, 0xe6, 0x07, 0x9b, 0x5b, 0xcb, 0x51, 0xd8, 0x6e, 0x5d, 0xf5, 0x82, 0x5a, 0xf5, - 0xbc, 0xe0, 0x34, 0xb3, 0xd8, 0x87, 0x30, 0xee, 0xcb, 0x12, 0xfd, 0xaa, 0x05, 0x67, 0x03, 0xa7, - 0x49, 0xe2, 0x96, 0x43, 0x3f, 0x2d, 0x07, 0x57, 0x7d, 0xc7, 0xdd, 0x65, 0x3d, 0x1a, 0xbd, 0xbf, - 0x1e, 0xd9, 0xa2, 0x47, 0x67, 0xaf, 0xf5, 0x25, 0x8d, 0xef, 0xc1, 0x16, 0xfd, 0x96, 0x05, 0xd3, - 0x61, 0xd4, 0xda, 0x71, 0x02, 0x52, 0x93, 0xd0, 0x78, 0x66, 0x8c, 0x2d, 0xbd, 0xcf, 0x1c, 0xed, - 0x13, 0xad, 0x67, 0xc9, 0xae, 0x85, 0x81, 0x97, 0x84, 0xd1, 0x26, 0x49, 0x12, 0x2f, 0x68, 0xc4, - 0xd5, 0x33, 0x77, 0x0e, 0x66, 0xa7, 0xbb, 0xb0, 0x70, 0x77, 0x7f, 0xd0, 0xcf, 0xc1, 0x78, 0xdc, - 0x09, 0xdc, 0x9b, 0x5e, 0x50, 0x0b, 0x6f, 0xc7, 0x33, 0xe5, 0x3c, 0x96, 0xef, 0xa6, 0x22, 0x28, - 0x16, 0xa0, 0x66, 0x80, 0x4d, 0x6e, 0xbd, 0x3f, 0x9c, 0x9e, 0x4a, 0x95, 0xbc, 0x3f, 0x9c, 0x9e, - 0x4c, 0xf7, 0x60, 0x8b, 0x7e, 0xc5, 0x82, 0xc9, 0xd8, 0x6b, 0x04, 0x4e, 0xd2, 0x8e, 0xc8, 0x55, - 0xd2, 0x89, 0x67, 0x80, 0x75, 0xe4, 0xca, 0x11, 0x47, 0xc5, 0x20, 0x59, 0x3d, 0x23, 0xfa, 0x38, - 0x69, 0xb6, 0xc6, 0x38, 0xcd, 0xb7, 0xd7, 0x42, 0xd3, 0xd3, 0x7a, 0x3c, 0xdf, 0x85, 0xa6, 0x27, - 0x75, 0x5f, 0x96, 0xe8, 0x67, 0xe0, 0x24, 0x6f, 0x52, 0x23, 0x1b, 0xcf, 0x4c, 0x30, 0x41, 0x7b, - 0xfa, 0xce, 0xc1, 0xec, 0xc9, 0xcd, 0x0c, 0x0c, 0x77, 0x61, 0xa3, 0xb7, 0x60, 0xb6, 0x45, 0xa2, - 0xa6, 0x97, 0xac, 0x07, 0x7e, 0x47, 0x8a, 0x6f, 0x37, 0x6c, 0x91, 0x9a, 0xe8, 0x4e, 0x3c, 0x33, - 0x79, 0xde, 0x7a, 0xae, 0x5c, 0xfd, 0xb0, 0xe8, 0xe6, 0xec, 0xc6, 0xbd, 0xd1, 0xf1, 0x61, 0xf4, - 0xec, 0x7f, 0x55, 0x80, 0x93, 0xd9, 0x8d, 0x13, 0xfd, 0x8e, 0x05, 0x27, 0x6e, 0xdd, 0x4e, 0xb6, - 0xc2, 0x5d, 0x12, 0xc4, 0xd5, 0x0e, 0x15, 0x6f, 0x6c, 0xcb, 0x18, 0xbf, 0xe0, 0xe6, 0xbb, 0x45, - 0xcf, 0x5d, 0x49, 0x73, 0xb9, 0x18, 0x24, 0x51, 0xa7, 0xfa, 0xb8, 0x78, 0xbb, 0x13, 0x57, 0x6e, - 0x6e, 0x99, 0x50, 0x9c, 0xed, 0xd4, 0xd9, 0xaf, 0x59, 0x70, 0xba, 0x17, 0x09, 0x74, 0x12, 0x8a, - 0xbb, 0xa4, 0xc3, 0xb5, 0x32, 0x4c, 0x7f, 0xa2, 0x37, 0xa1, 0xb4, 0xe7, 0xf8, 0x6d, 0x22, 0xb4, - 0x9b, 0xe5, 0xa3, 0xbd, 0x88, 0xea, 0x19, 0xe6, 0x54, 0x7f, 0xb2, 0xf0, 0xb2, 0x65, 0xff, 0x9b, - 0x22, 0x8c, 0x1b, 0xfb, 0xdb, 0x03, 0xd0, 0xd8, 0xc2, 0x94, 0xc6, 0xb6, 0x96, 0xdb, 0xd6, 0xdc, - 0x57, 0x65, 0xbb, 0x9d, 0x51, 0xd9, 0xd6, 0xf3, 0x63, 0x79, 0x4f, 0x9d, 0x0d, 0x25, 0x50, 0x09, - 0x5b, 0x54, 0x23, 0xa7, 0x5b, 0xff, 0x48, 0x1e, 0x9f, 0x70, 0x5d, 0x92, 0xab, 0x4e, 0xde, 0x39, - 0x98, 0xad, 0xa8, 0xbf, 0x58, 0x33, 0xb2, 0x7f, 0x60, 0xc1, 0x69, 0xa3, 0x8f, 0x8b, 0x61, 0x50, - 0xf3, 0xd8, 0xa7, 0x3d, 0x0f, 0x23, 0x49, 0xa7, 0x25, 0xd5, 0x7e, 0x35, 0x52, 0x5b, 0x9d, 0x16, - 0xc1, 0x0c, 0x42, 0x15, 0xfd, 0x26, 0x89, 0x63, 0xa7, 0x41, 0xb2, 0x8a, 0xfe, 0x1a, 0x6f, 0xc6, - 0x12, 0x8e, 0x22, 0x40, 0xbe, 0x13, 0x27, 0x5b, 0x91, 0x13, 0xc4, 0x8c, 0xfc, 0x96, 0xd7, 0x24, - 0x62, 0x80, 0xff, 0xbf, 0xc1, 0x66, 0x0c, 0x7d, 0xa2, 0xfa, 0xd8, 0x9d, 0x83, 0x59, 0xb4, 0xda, - 0x45, 0x09, 0xf7, 0xa0, 0x6e, 0xff, 0xaa, 0x05, 0x8f, 0xf5, 0xd6, 0xc5, 0xd0, 0xb3, 0x30, 0xca, - 0x4d, 0x3e, 0xf1, 0x76, 0xfa, 0x93, 0xb0, 0x56, 0x2c, 0xa0, 0x68, 0x1e, 0x2a, 0x6a, 0x9f, 0x10, - 0xef, 0x38, 0x2d, 0x50, 0x2b, 0x7a, 0x73, 0xd1, 0x38, 0x74, 0xd0, 0xe8, 0x1f, 0xa1, 0xb9, 0xa9, - 0x41, 0x63, 0x46, 0x12, 0x83, 0xd8, 0x7f, 0x6e, 0xc1, 0x09, 0xa3, 0x57, 0x0f, 0x40, 0x35, 0x0f, - 0xd2, 0xaa, 0xf9, 0x4a, 0x6e, 0xf3, 0xb9, 0x8f, 0x6e, 0xfe, 0x0d, 0x0b, 0xce, 0x1a, 0x58, 0x6b, - 0x4e, 0xe2, 0xee, 0x5c, 0xdc, 0x6f, 0x45, 0x24, 0xa6, 0xe6, 0x34, 0x7a, 0xda, 0x90, 0x5b, 0xd5, - 0x71, 0x41, 0xa1, 0x78, 0x95, 0x74, 0xb8, 0x10, 0x7b, 0x1e, 0xca, 0x7c, 0x72, 0x86, 0x91, 0x18, - 0x71, 0xf5, 0x6e, 0xeb, 0xa2, 0x1d, 0x2b, 0x0c, 0x64, 0xc3, 0x28, 0x13, 0x4e, 0x74, 0xb1, 0xd2, - 0x6d, 0x08, 0xe8, 0x47, 0xbc, 0xc1, 0x5a, 0xb0, 0x80, 0xd8, 0x77, 0x0a, 0xcc, 0x56, 0x50, 0xab, - 0x90, 0x3c, 0x08, 0x43, 0x33, 0x4a, 0x89, 0xad, 0x8d, 0xfc, 0x64, 0x08, 0xe9, 0x6f, 0x6c, 0xbe, - 0x9d, 0x91, 0x5c, 0x38, 0x57, 0xae, 0xf7, 0x36, 0x38, 0x7f, 0xa3, 0x00, 0xb3, 0xe9, 0x07, 0xba, - 0x04, 0x1f, 0xb5, 0x6e, 0x0c, 0x46, 0xd9, 0xf3, 0x04, 0x03, 0x1f, 0x9b, 0x78, 0x7d, 0x64, 0x47, - 0xe1, 0x38, 0x65, 0x87, 0x29, 0xda, 0x8a, 0x87, 0x88, 0xb6, 0x67, 0xd5, 0xa8, 0x97, 0x32, 0xb2, - 0x24, 0x3d, 0x42, 0xff, 0xb5, 0x00, 0x8f, 0xa7, 0x47, 0x48, 0xcb, 0xda, 0x4f, 0xa6, 0x64, 0xed, - 0x47, 0x4d, 0x59, 0x7b, 0xf7, 0x60, 0xf6, 0xc9, 0x3e, 0x8f, 0xfd, 0xc8, 0x88, 0x62, 0xb4, 0xac, - 0xc6, 0x68, 0x84, 0xf5, 0x6e, 0x3e, 0x3d, 0x46, 0x77, 0x0f, 0x66, 0x9f, 0xee, 0xf3, 0x8e, 0x99, - 0x3d, 0xf2, 0x59, 0x18, 0x8d, 0x88, 0x13, 0x87, 0x41, 0x76, 0xb0, 0x31, 0x6b, 0xc5, 0x02, 0x6a, - 0xff, 0x79, 0x39, 0x3b, 0xd8, 0xcb, 0xfc, 0xb4, 0x2b, 0x8c, 0x90, 0x07, 0x23, 0x4c, 0x7f, 0xe6, - 0x0b, 0xff, 0xea, 0xd1, 0x16, 0x09, 0x95, 0xb7, 0x8a, 0x74, 0xb5, 0x4c, 0xbf, 0x1a, 0x6d, 0xc2, - 0x8c, 0x05, 0xda, 0x87, 0xb2, 0x2b, 0xd5, 0xda, 0x42, 0x1e, 0x07, 0x40, 0x42, 0xa9, 0xd5, 0x1c, - 0x27, 0xa8, 0x60, 0x54, 0xba, 0xb0, 0xe2, 0x86, 0x08, 0x14, 0x1b, 0x5e, 0x22, 0x3e, 0xeb, 0x11, - 0x0d, 0x97, 0x65, 0xcf, 0x78, 0xc5, 0x31, 0x2a, 0xad, 0x97, 0xbd, 0x04, 0x53, 0xfa, 0xe8, 0x97, - 0x2c, 0x18, 0x8f, 0xdd, 0xe6, 0x46, 0x14, 0xee, 0x79, 0x35, 0x12, 0x09, 0xb5, 0xe5, 0x88, 0x82, - 0x67, 0x73, 0x71, 0x4d, 0x12, 0xd4, 0x7c, 0xb9, 0x21, 0xa9, 0x21, 0xd8, 0xe4, 0x4b, 0xd5, 0xf9, - 0xc7, 0xc5, 0xbb, 0x2f, 0x11, 0xd7, 0xa3, 0x1b, 0x8d, 0xb4, 0x5e, 0xd8, 0x4c, 0x39, 0xb2, 0x1a, - 0xb7, 0xd4, 0x76, 0x77, 0xe9, 0x7a, 0xd3, 0x1d, 0x7a, 0xf2, 0xce, 0xc1, 0xec, 0xe3, 0x8b, 0xbd, - 0x79, 0xe2, 0x7e, 0x9d, 0x61, 0x03, 0xd6, 0x6a, 0xfb, 0x3e, 0x26, 0x6f, 0xb5, 0x09, 0x3b, 0x9b, - 0xc8, 0x61, 0xc0, 0x36, 0x34, 0xc1, 0xcc, 0x80, 0x19, 0x10, 0x6c, 0xf2, 0x45, 0x6f, 0xc1, 0x68, - 0xd3, 0x49, 0x22, 0x6f, 0x5f, 0x1c, 0x48, 0x1c, 0x51, 0xb1, 0x5e, 0x63, 0xb4, 0x34, 0x73, 0xb6, - 0x0f, 0xf3, 0x46, 0x2c, 0x18, 0xa1, 0x26, 0x94, 0x9a, 0x24, 0x6a, 0x90, 0x99, 0x72, 0x1e, 0x87, - 0xaf, 0x6b, 0x94, 0x94, 0x66, 0x58, 0xa1, 0x6a, 0x08, 0x6b, 0xc3, 0x9c, 0x0b, 0x7a, 0x13, 0xca, - 0x31, 0xf1, 0x89, 0x4b, 0x15, 0x89, 0x0a, 0xe3, 0xf8, 0xb1, 0x01, 0x95, 0x2a, 0x67, 0x9b, 0xf8, - 0x9b, 0xe2, 0x51, 0xbe, 0xc0, 0xe4, 0x3f, 0xac, 0x48, 0xda, 0xff, 0xc9, 0x02, 0x94, 0x96, 0x30, - 0x0f, 0x40, 0x95, 0x7b, 0x2b, 0xad, 0xca, 0xad, 0xe6, 0xb9, 0xc1, 0xf7, 0xd1, 0xe6, 0xde, 0x2b, - 0x43, 0x46, 0x36, 0x5f, 0x23, 0x71, 0x42, 0x6a, 0x1f, 0xc8, 0xd3, 0x0f, 0xe4, 0xe9, 0x07, 0xf2, - 0x54, 0xc9, 0xd3, 0xed, 0x8c, 0x3c, 0x7d, 0xd5, 0x58, 0xf5, 0xda, 0x95, 0xf8, 0x59, 0xe5, 0x6b, - 0x34, 0x7b, 0x60, 0x20, 0x50, 0x49, 0x70, 0x65, 0x73, 0xfd, 0x5a, 0x4f, 0x01, 0xfa, 0xd9, 0xb4, - 0x00, 0x3d, 0x2a, 0x8b, 0x07, 0x2e, 0x32, 0xff, 0x46, 0x01, 0x9e, 0x48, 0x8b, 0x12, 0x1c, 0xfa, - 0x7e, 0xd8, 0x4e, 0x36, 0x13, 0xd2, 0x42, 0xbf, 0x6e, 0xc1, 0xc9, 0x66, 0xda, 0x56, 0x8c, 0xc5, - 0x91, 0xdc, 0xa7, 0x72, 0x93, 0x73, 0x19, 0x63, 0xb4, 0x3a, 0x23, 0x64, 0xde, 0xc9, 0x0c, 0x20, - 0xc6, 0x5d, 0x7d, 0x41, 0x6f, 0x42, 0xa5, 0xe9, 0xec, 0x5f, 0x6f, 0xd5, 0x9c, 0x44, 0x9a, 0x1f, - 0xfd, 0xad, 0xc6, 0x76, 0xe2, 0xf9, 0x73, 0xdc, 0xd1, 0x3a, 0xb7, 0x12, 0x24, 0xeb, 0xd1, 0x66, - 0x12, 0x79, 0x41, 0x83, 0x1f, 0xc4, 0xac, 0x49, 0x32, 0x58, 0x53, 0xb4, 0xff, 0x96, 0x95, 0x15, - 0xb4, 0x6a, 0x74, 0x22, 0x27, 0x21, 0x8d, 0x0e, 0xfa, 0x3c, 0x94, 0xe2, 0x84, 0xb4, 0xe4, 0xa8, - 0xdc, 0xcc, 0x53, 0xfa, 0x1b, 0x5f, 0x42, 0x6f, 0x04, 0xf4, 0x5f, 0x8c, 0x39, 0x53, 0xfb, 0xce, - 0x48, 0x76, 0xc3, 0x63, 0x6e, 0xb7, 0x0b, 0x00, 0x8d, 0x70, 0x8b, 0x34, 0x5b, 0x3e, 0x1d, 0x16, - 0x8b, 0x9d, 0xdd, 0x2a, 0xd3, 0x78, 0x59, 0x41, 0xb0, 0x81, 0x85, 0xfe, 0x8a, 0x05, 0xd0, 0x90, - 0x0b, 0x4b, 0x6e, 0x66, 0xd7, 0xf3, 0x7c, 0x1d, 0xbd, 0x6c, 0x75, 0x5f, 0x14, 0x43, 0x6c, 0x30, - 0x47, 0x5f, 0xb6, 0xa0, 0x9c, 0xc8, 0xee, 0x73, 0xf1, 0xbe, 0x95, 0x67, 0x4f, 0xe4, 0x4b, 0xeb, - 0x7d, 0x5d, 0x0d, 0x89, 0xe2, 0x8b, 0x7e, 0xd9, 0x02, 0x88, 0x3b, 0x81, 0xbb, 0x11, 0xfa, 0x9e, - 0xdb, 0x11, 0x52, 0xff, 0x46, 0xae, 0xe6, 0xbb, 0xa2, 0x5e, 0x9d, 0xa2, 0xa3, 0xa1, 0xff, 0x63, - 0x83, 0x33, 0xfa, 0x02, 0x94, 0x63, 0x31, 0xdd, 0x84, 0x9c, 0xdf, 0xca, 0xf7, 0x10, 0x81, 0xd3, - 0x16, 0x22, 0x42, 0xfc, 0xc3, 0x8a, 0xa7, 0xfd, 0xbd, 0x42, 0xea, 0x34, 0x52, 0x9d, 0x3b, 0xb0, - 0x29, 0xe3, 0x4a, 0xa3, 0x50, 0xae, 0x80, 0x5c, 0xa7, 0x8c, 0x32, 0x39, 0xf5, 0x94, 0x51, 0x4d, - 0x31, 0x36, 0x98, 0xd3, 0xcd, 0x71, 0xda, 0xc9, 0x9e, 0x6e, 0x88, 0x59, 0xfc, 0x66, 0x9e, 0x5d, - 0xea, 0x3e, 0x3b, 0x7e, 0x42, 0x74, 0x6d, 0xba, 0x0b, 0x84, 0xbb, 0xbb, 0x64, 0x7f, 0x2f, 0x7d, - 0x02, 0x6a, 0x7c, 0x80, 0x01, 0x4e, 0x77, 0xbf, 0x69, 0xc1, 0x78, 0x14, 0xfa, 0xbe, 0x17, 0x34, - 0xe8, 0x64, 0x11, 0x12, 0xef, 0x8d, 0x63, 0x11, 0x3a, 0x62, 0x56, 0xb0, 0x2d, 0x16, 0x6b, 0x9e, - 0xd8, 0xec, 0x80, 0xfd, 0x25, 0x0b, 0x66, 0xfa, 0x4d, 0x6a, 0x44, 0xe0, 0x49, 0x2a, 0xa9, 0xe9, - 0xc6, 0xa7, 0x7c, 0x9b, 0xeb, 0xc1, 0x12, 0xf1, 0x89, 0x3a, 0x6b, 0x2a, 0x57, 0x9f, 0x11, 0xaf, - 0xf9, 0xe4, 0x46, 0x7f, 0x54, 0x7c, 0x2f, 0x3a, 0xf6, 0x6f, 0x17, 0xb2, 0x23, 0xaa, 0x84, 0xda, - 0xb7, 0xad, 0x2e, 0xd5, 0xff, 0x53, 0xc7, 0x21, 0x48, 0x98, 0x91, 0xa0, 0x5c, 0x9c, 0xfd, 0x71, - 0x1e, 0xa2, 0x0f, 0xc5, 0xfe, 0xd7, 0x23, 0x70, 0x8f, 0x9e, 0xa9, 0x53, 0x72, 0xab, 0xdf, 0x29, - 0xf9, 0xf0, 0x07, 0xef, 0x5f, 0xb7, 0x60, 0xd4, 0xa7, 0x5a, 0x08, 0x3f, 0x09, 0x1e, 0xbf, 0x50, - 0x3b, 0xae, 0xb1, 0xe7, 0xca, 0x4e, 0xcc, 0xfd, 0x78, 0xea, 0xfc, 0x89, 0x37, 0x62, 0xd1, 0x07, - 0xf4, 0x1d, 0x0b, 0xc6, 0x9d, 0x20, 0x08, 0x13, 0x11, 0x58, 0xc2, 0x03, 0x33, 0xbc, 0x63, 0xeb, - 0xd3, 0x82, 0xe6, 0xc5, 0x3b, 0xa6, 0x8f, 0x55, 0x35, 0x04, 0x9b, 0x5d, 0x42, 0x73, 0x00, 0x75, - 0x2f, 0x70, 0x7c, 0xef, 0x6d, 0x6a, 0x4d, 0x95, 0xd8, 0xf1, 0x39, 0xdb, 0x1a, 0x2e, 0xa9, 0x56, - 0x6c, 0x60, 0x9c, 0xfd, 0xcb, 0x30, 0x6e, 0xbc, 0x79, 0x0f, 0xf7, 0xe3, 0x69, 0xd3, 0xfd, 0x58, - 0x31, 0xbc, 0x86, 0x67, 0x5f, 0x85, 0x93, 0xd9, 0x0e, 0x0e, 0xf3, 0xbc, 0xfd, 0x3b, 0xa3, 0xd9, - 0xc3, 0xe5, 0x2d, 0x12, 0x35, 0x69, 0xd7, 0x3e, 0xb0, 0x42, 0x3f, 0xb0, 0x42, 0x3f, 0xb0, 0x42, - 0xe5, 0x1f, 0xfb, 0x4e, 0x09, 0x52, 0x9a, 0x01, 0xef, 0xdd, 0x47, 0x60, 0x2c, 0x22, 0xad, 0xf0, - 0x3a, 0x5e, 0x15, 0x12, 0x57, 0x07, 0x64, 0xf2, 0x66, 0x2c, 0xe1, 0x54, 0x32, 0xb7, 0x9c, 0x64, - 0x47, 0x88, 0x5c, 0x25, 0x99, 0x37, 0x9c, 0x64, 0x07, 0x33, 0x08, 0x7a, 0x15, 0xa6, 0x12, 0x27, - 0x6a, 0x90, 0x04, 0x93, 0x3d, 0x36, 0x08, 0xe2, 0x48, 0xff, 0x31, 0x81, 0x3b, 0xb5, 0x95, 0x82, - 0xe2, 0x0c, 0x36, 0x7a, 0x0b, 0x46, 0x76, 0x88, 0xdf, 0x14, 0x66, 0xf2, 0x66, 0x7e, 0x12, 0x91, - 0xbd, 0xeb, 0x65, 0xe2, 0x37, 0xf9, 0x7a, 0xa5, 0xbf, 0x30, 0x63, 0x45, 0xbf, 0x4e, 0x65, 0xb7, - 0x1d, 0x27, 0x61, 0xd3, 0x7b, 0x5b, 0x1a, 0xcf, 0x9f, 0xca, 0x99, 0xf1, 0x55, 0x49, 0x9f, 0x5b, - 0x78, 0xea, 0x2f, 0xd6, 0x9c, 0x59, 0x3f, 0x6a, 0x5e, 0xc4, 0x8c, 0xe1, 0xce, 0x0c, 0x1c, 0x4b, - 0x3f, 0x96, 0x24, 0x7d, 0xde, 0x0f, 0xf5, 0x17, 0x6b, 0xce, 0xa8, 0x03, 0xa3, 0x2d, 0xbf, 0xdd, - 0xf0, 0x82, 0x99, 0x71, 0xd6, 0x87, 0xeb, 0x39, 0xf7, 0x61, 0x83, 0x11, 0xe7, 0x47, 0x18, 0xfc, - 0x37, 0x16, 0x0c, 0xd1, 0x33, 0x50, 0x72, 0x77, 0x9c, 0x28, 0x99, 0x99, 0x60, 0x93, 0x46, 0x59, - 0x9a, 0x8b, 0xb4, 0x11, 0x73, 0x18, 0x7a, 0x1a, 0x8a, 0x11, 0xa9, 0xb3, 0x38, 0x20, 0xc3, 0x43, - 0x8c, 0x49, 0x1d, 0xd3, 0x76, 0xfb, 0x6f, 0x17, 0xd2, 0xca, 0x45, 0xfa, 0xbd, 0xf9, 0x6c, 0x77, - 0xdb, 0x51, 0x2c, 0xad, 0x51, 0x63, 0xb6, 0xb3, 0x66, 0x2c, 0xe1, 0xe8, 0x4b, 0x16, 0x8c, 0xdd, - 0x8a, 0xc3, 0x20, 0x20, 0x89, 0x10, 0xe4, 0x37, 0x72, 0x1e, 0x8a, 0x2b, 0x9c, 0xba, 0xee, 0x83, - 0x68, 0xc0, 0x92, 0x2f, 0xed, 0x2e, 0xd9, 0x77, 0xfd, 0x76, 0xad, 0xcb, 0xd3, 0x78, 0x91, 0x37, - 0x63, 0x09, 0xa7, 0xa8, 0x5e, 0xc0, 0x51, 0x47, 0xd2, 0xa8, 0x2b, 0x81, 0x40, 0x15, 0x70, 0xfb, - 0xf7, 0x4a, 0x70, 0xa6, 0xe7, 0xe2, 0xa0, 0xdb, 0x3e, 0xdb, 0x58, 0x2f, 0x79, 0x3e, 0x91, 0x51, - 0xb2, 0x6c, 0xdb, 0xbf, 0xa1, 0x5a, 0xb1, 0x81, 0x81, 0x7e, 0x01, 0xa0, 0xe5, 0x44, 0x4e, 0x93, - 0x88, 0xed, 0xae, 0x78, 0xf4, 0xdd, 0x95, 0xf6, 0x63, 0x43, 0xd2, 0xd4, 0xd6, 0x96, 0x6a, 0x8a, - 0xb1, 0xc1, 0x12, 0x7d, 0x1c, 0xc6, 0x23, 0xe2, 0x13, 0x27, 0x66, 0x61, 0x64, 0xd9, 0x98, 0x58, - 0xac, 0x41, 0xd8, 0xc4, 0x43, 0xcf, 0xaa, 0xc8, 0x80, 0x91, 0xb4, 0xa7, 0x30, 0x1d, 0x1d, 0x80, - 0xde, 0xb1, 0x60, 0xaa, 0xee, 0xf9, 0x44, 0x73, 0x17, 0x11, 0xac, 0xeb, 0x47, 0x7f, 0xc9, 0x4b, - 0x26, 0x5d, 0x2d, 0x21, 0x53, 0xcd, 0x31, 0xce, 0xb0, 0xa7, 0x9f, 0x79, 0x8f, 0x44, 0x4c, 0xb4, - 0x8e, 0xa6, 0x3f, 0xf3, 0x0d, 0xde, 0x8c, 0x25, 0x1c, 0x2d, 0xc0, 0x89, 0x96, 0x13, 0xc7, 0x8b, - 0x11, 0xa9, 0x91, 0x20, 0xf1, 0x1c, 0x9f, 0xc7, 0x97, 0x96, 0x75, 0x7c, 0xd9, 0x46, 0x1a, 0x8c, - 0xb3, 0xf8, 0xe8, 0xd3, 0xf0, 0xb8, 0xd7, 0x08, 0xc2, 0x88, 0xac, 0x79, 0x71, 0xec, 0x05, 0x0d, - 0x3d, 0x0d, 0x98, 0xa4, 0x2c, 0x57, 0x67, 0x05, 0xa9, 0xc7, 0x57, 0x7a, 0xa3, 0xe1, 0x7e, 0xcf, - 0xa3, 0xe7, 0xa1, 0x1c, 0xef, 0x7a, 0xad, 0xc5, 0xa8, 0x16, 0xb3, 0xe3, 0xc4, 0xb2, 0x3e, 0x03, - 0xd9, 0x14, 0xed, 0x58, 0x61, 0xd8, 0xbf, 0x56, 0x48, 0x9b, 0x77, 0xe6, 0xfa, 0x41, 0x31, 0x5d, - 0x25, 0xc9, 0x0d, 0x27, 0x92, 0xa6, 0xff, 0x11, 0x23, 0x54, 0x05, 0xdd, 0x1b, 0x4e, 0x64, 0xae, - 0x37, 0xc6, 0x00, 0x4b, 0x4e, 0xe8, 0x16, 0x8c, 0x24, 0xbe, 0x93, 0x53, 0x48, 0xbb, 0xc1, 0x51, - 0x5b, 0xdb, 0xab, 0x0b, 0x31, 0x66, 0x3c, 0xd0, 0x53, 0x54, 0x7d, 0xdd, 0x96, 0x61, 0x2c, 0x42, - 0xe3, 0xdc, 0x8e, 0x31, 0x6b, 0xb5, 0xff, 0xfb, 0x68, 0x0f, 0x91, 0xa7, 0xf6, 0x18, 0x74, 0x01, - 0x80, 0x5a, 0x42, 0x1b, 0x11, 0xa9, 0x7b, 0xfb, 0x62, 0x8f, 0x57, 0xcb, 0xea, 0x9a, 0x82, 0x60, - 0x03, 0x4b, 0x3e, 0xb3, 0xd9, 0xae, 0xd3, 0x67, 0x0a, 0xdd, 0xcf, 0x70, 0x08, 0x36, 0xb0, 0xd0, - 0x4b, 0x30, 0xea, 0x35, 0x9d, 0x86, 0x8a, 0xb6, 0x79, 0x8a, 0xae, 0xa7, 0x15, 0xd6, 0x72, 0xf7, - 0x60, 0x76, 0x4a, 0x75, 0x88, 0x35, 0x61, 0x81, 0x8b, 0x7e, 0xdb, 0x82, 0x09, 0x37, 0x6c, 0x36, - 0xc3, 0x80, 0xdb, 0x0f, 0xc2, 0x18, 0xba, 0x75, 0x5c, 0x3b, 0xf0, 0xdc, 0xa2, 0xc1, 0x8c, 0x5b, - 0x43, 0x2a, 0xf6, 0xde, 0x04, 0xe1, 0x54, 0xaf, 0xcc, 0x65, 0x57, 0x3a, 0x64, 0xd9, 0xfd, 0x63, - 0x0b, 0xa6, 0xf9, 0xb3, 0x86, 0x59, 0x23, 0xc2, 0xcc, 0xc3, 0x63, 0x7e, 0xad, 0x2e, 0x4b, 0x4f, - 0x1d, 0x09, 0x75, 0xc1, 0x71, 0x77, 0x27, 0xd1, 0x32, 0x4c, 0xd7, 0xc3, 0xc8, 0x25, 0xe6, 0x40, - 0x08, 0x99, 0xa1, 0x08, 0x5d, 0xca, 0x22, 0xe0, 0xee, 0x67, 0xd0, 0x0d, 0x78, 0xcc, 0x68, 0x34, - 0xc7, 0x81, 0x8b, 0x8d, 0x73, 0x82, 0xda, 0x63, 0x97, 0x7a, 0x62, 0xe1, 0x3e, 0x4f, 0x9f, 0xfd, - 0x24, 0x4c, 0x77, 0x7d, 0xbf, 0xa1, 0x8c, 0xcd, 0x25, 0x78, 0xac, 0xf7, 0x48, 0x0d, 0x65, 0x72, - 0xfe, 0xc3, 0x4c, 0xb4, 0x8e, 0xa1, 0xd8, 0x0c, 0x70, 0x7c, 0xe1, 0x40, 0x91, 0x04, 0x7b, 0x42, - 0x70, 0x5c, 0x3a, 0xda, 0x8c, 0xb8, 0x18, 0xec, 0xf1, 0x0f, 0xcd, 0x6c, 0xb4, 0x8b, 0xc1, 0x1e, - 0xa6, 0xb4, 0xd1, 0xbb, 0x56, 0x6a, 0x63, 0xe6, 0x87, 0x1e, 0x9f, 0x39, 0x16, 0x4d, 0x6e, 0xe0, - 0xbd, 0xda, 0xfe, 0x5e, 0x01, 0xce, 0x1f, 0x46, 0x64, 0x80, 0xe1, 0x7b, 0x06, 0x46, 0x63, 0xe6, - 0x2e, 0x11, 0x2b, 0x71, 0x9c, 0xae, 0x42, 0xee, 0x40, 0xf9, 0x2c, 0x16, 0x20, 0xf4, 0xcb, 0x16, - 0x14, 0x9b, 0x4e, 0x4b, 0xbc, 0x79, 0xe3, 0x78, 0xdf, 0x7c, 0x6e, 0xcd, 0x69, 0xf1, 0xaf, 0xa0, - 0xf4, 0xd1, 0x35, 0xa7, 0x85, 0x69, 0x07, 0xd0, 0x2c, 0x94, 0x9c, 0x28, 0x72, 0x3a, 0x4c, 0xae, - 0x55, 0xb8, 0x5b, 0x6d, 0x81, 0x36, 0x60, 0xde, 0x7e, 0xf6, 0x13, 0x50, 0x96, 0x8f, 0x0f, 0x35, - 0x07, 0xbf, 0x3e, 0x96, 0x0a, 0x15, 0x65, 0xee, 0x96, 0x18, 0x46, 0x85, 0x01, 0x6c, 0xe5, 0x1d, - 0x9d, 0xcc, 0x63, 0xfd, 0x99, 0xd6, 0x2e, 0x32, 0xa6, 0x04, 0x2b, 0xf4, 0x35, 0x8b, 0xe5, 0x25, - 0xc9, 0xf0, 0x59, 0xa1, 0x2b, 0x1f, 0x4f, 0x9a, 0x94, 0x99, 0xed, 0x24, 0x1b, 0xb1, 0xc9, 0x9d, - 0x0a, 0xea, 0x16, 0x8f, 0xb0, 0xcf, 0x6a, 0xcc, 0x32, 0x73, 0x49, 0xc2, 0xd1, 0x7e, 0x0f, 0xb7, - 0x4a, 0x0e, 0xb9, 0x2d, 0x03, 0x38, 0x52, 0xbe, 0x63, 0xc1, 0x34, 0xd7, 0x8b, 0x96, 0xbc, 0x7a, - 0x9d, 0x44, 0x24, 0x70, 0x89, 0xd4, 0x2c, 0x8f, 0xe8, 0xb8, 0x93, 0xa7, 0x0e, 0x2b, 0x59, 0xf2, - 0x5a, 0x82, 0x77, 0x81, 0x70, 0x77, 0x67, 0x50, 0x0d, 0x46, 0xbc, 0xa0, 0x1e, 0x8a, 0x7d, 0xab, - 0x7a, 0xb4, 0x4e, 0xad, 0x04, 0xf5, 0x50, 0xaf, 0x65, 0xfa, 0x0f, 0x33, 0xea, 0x68, 0x15, 0x4e, - 0x47, 0xc2, 0xf6, 0xbf, 0xec, 0xc5, 0xd4, 0x42, 0x5b, 0xf5, 0x9a, 0x5e, 0xc2, 0xf6, 0x9c, 0x62, - 0x75, 0xe6, 0xce, 0xc1, 0xec, 0x69, 0xdc, 0x03, 0x8e, 0x7b, 0x3e, 0x85, 0xde, 0x86, 0x31, 0x99, - 0x48, 0x55, 0xce, 0x43, 0x4b, 0xef, 0x9e, 0xff, 0x6a, 0x32, 0x6d, 0x8a, 0x9c, 0x29, 0xc9, 0xd0, - 0xfe, 0xe7, 0x00, 0xdd, 0x6e, 0x17, 0xf4, 0xf3, 0x50, 0x89, 0x54, 0x72, 0x97, 0x95, 0x47, 0x58, - 0x8e, 0xfc, 0xbe, 0xc2, 0xe5, 0xa3, 0xce, 0xbd, 0x75, 0x1a, 0x97, 0xe6, 0x48, 0x75, 0xd4, 0x58, - 0x7b, 0x67, 0x72, 0x98, 0xdb, 0x82, 0xab, 0x3e, 0xd5, 0xef, 0x04, 0x2e, 0x66, 0x3c, 0x50, 0x04, - 0xa3, 0x3b, 0xc4, 0xf1, 0x93, 0x9d, 0x7c, 0x0e, 0x20, 0x2f, 0x33, 0x5a, 0xd9, 0xb8, 0x62, 0xde, - 0x8a, 0x05, 0x27, 0xb4, 0x0f, 0x63, 0x3b, 0x7c, 0x02, 0x08, 0xb5, 0x71, 0xed, 0xa8, 0x83, 0x9b, - 0x9a, 0x55, 0xfa, 0x73, 0x8b, 0x06, 0x2c, 0xd9, 0x31, 0x9f, 0xac, 0xe1, 0x71, 0xe4, 0x4b, 0x37, - 0xbf, 0x90, 0xea, 0xc1, 0xdd, 0x8d, 0x9f, 0x83, 0x89, 0x88, 0xb8, 0x61, 0xe0, 0x7a, 0x3e, 0xa9, - 0x2d, 0xc8, 0xc3, 0xc5, 0x61, 0x42, 0x75, 0x4f, 0x52, 0xd5, 0x17, 0x1b, 0x34, 0x70, 0x8a, 0x22, - 0xfa, 0xaa, 0x05, 0x53, 0x2a, 0x23, 0x84, 0x7e, 0x10, 0x22, 0x8e, 0xe7, 0x56, 0x73, 0xca, 0x3f, - 0x61, 0x34, 0xab, 0x88, 0x1a, 0xbf, 0xe9, 0x36, 0x9c, 0xe1, 0x8b, 0x5e, 0x07, 0x08, 0xb7, 0x99, - 0xfb, 0x8d, 0xbe, 0x6a, 0x79, 0xe8, 0x57, 0x9d, 0xe2, 0x11, 0xf9, 0x92, 0x02, 0x36, 0xa8, 0xa1, - 0xab, 0x00, 0x7c, 0xd9, 0x6c, 0x75, 0x5a, 0x84, 0x59, 0xa4, 0x3a, 0xd6, 0x1a, 0x36, 0x15, 0xe4, - 0xee, 0xc1, 0x6c, 0xf7, 0xd9, 0x09, 0x73, 0x8c, 0x1a, 0x8f, 0xa3, 0x9f, 0x83, 0xb1, 0xb8, 0xdd, - 0x6c, 0x3a, 0xea, 0x24, 0x2f, 0xc7, 0x18, 0x7f, 0x4e, 0xd7, 0x10, 0x45, 0xbc, 0x01, 0x4b, 0x8e, - 0xe8, 0x16, 0x15, 0xaa, 0xb1, 0x38, 0xd4, 0x61, 0xab, 0x88, 0xeb, 0x04, 0xe3, 0xec, 0x9d, 0x3e, - 0x21, 0x9e, 0x3b, 0x8d, 0x7b, 0xe0, 0xdc, 0x3d, 0x98, 0x7d, 0x2c, 0xdd, 0xbe, 0x1a, 0x8a, 0xa8, - 0xfb, 0x9e, 0x34, 0xd1, 0x15, 0x99, 0x57, 0x4d, 0x5f, 0x5b, 0xa6, 0xfb, 0x3d, 0xa7, 0xf3, 0xaa, - 0x59, 0x73, 0xff, 0x31, 0x33, 0x1f, 0xb6, 0x83, 0x74, 0x08, 0x89, 0x78, 0x9b, 0x97, 0x60, 0x82, - 0xec, 0x27, 0x24, 0x0a, 0x1c, 0xff, 0x3a, 0x5e, 0x95, 0x87, 0x52, 0x6c, 0xd2, 0x5e, 0x34, 0xda, - 0x71, 0x0a, 0x0b, 0xd9, 0xca, 0x18, 0x2d, 0xe8, 0xd4, 0x0f, 0x6e, 0x8c, 0x4a, 0xd3, 0xd3, 0xfe, - 0x3f, 0x85, 0x94, 0x06, 0xb5, 0x15, 0x11, 0x82, 0x42, 0x28, 0x05, 0x61, 0x4d, 0x09, 0xeb, 0x2b, - 0xf9, 0x08, 0xeb, 0x6b, 0x61, 0xcd, 0xc8, 0x96, 0xa6, 0xff, 0x62, 0xcc, 0xf9, 0xb0, 0x74, 0x52, - 0x99, 0x77, 0xcb, 0x00, 0xc2, 0x2e, 0xc8, 0x93, 0xb3, 0x4a, 0x27, 0x5d, 0x37, 0x19, 0xe1, 0x34, - 0x5f, 0xb4, 0x0b, 0xa5, 0x9d, 0x30, 0x4e, 0xa4, 0xb5, 0x70, 0x44, 0xc3, 0xe4, 0x72, 0x18, 0x27, - 0x6c, 0xdb, 0x57, 0xaf, 0x4d, 0x5b, 0x62, 0xcc, 0x79, 0xd8, 0xff, 0xd9, 0x4a, 0x1d, 0x41, 0xde, - 0x64, 0xe1, 0x54, 0x7b, 0x24, 0xa0, 0xeb, 0xd0, 0x8c, 0x3d, 0xf8, 0x4b, 0x99, 0x6c, 0x87, 0x0f, - 0xf7, 0xab, 0x5d, 0x71, 0x9b, 0x52, 0x98, 0x63, 0x24, 0x8c, 0x30, 0x85, 0x2f, 0x5a, 0xe9, 0xac, - 0x12, 0xbe, 0x11, 0xe6, 0x98, 0xe4, 0x74, 0x68, 0x82, 0x8a, 0xfd, 0xae, 0x05, 0x63, 0x55, 0xc7, - 0xdd, 0x0d, 0xeb, 0x75, 0xf4, 0x3c, 0x94, 0x6b, 0xed, 0xc8, 0x4c, 0x70, 0x51, 0x67, 0x5e, 0x4b, - 0xa2, 0x1d, 0x2b, 0x0c, 0x3a, 0x87, 0xeb, 0x8e, 0x2b, 0x53, 0x9d, 0x8a, 0x7c, 0x0e, 0x5f, 0x62, - 0x2d, 0x58, 0x40, 0xd0, 0xc7, 0x61, 0xbc, 0xe9, 0xec, 0xcb, 0x87, 0xb3, 0xe7, 0x9f, 0x6b, 0x1a, - 0x84, 0x4d, 0x3c, 0xfb, 0x5f, 0x5a, 0x30, 0x53, 0x75, 0x62, 0xcf, 0x5d, 0x68, 0x27, 0x3b, 0x55, - 0x2f, 0xd9, 0x6e, 0xbb, 0xbb, 0x24, 0xe1, 0xf9, 0x6d, 0xb4, 0x97, 0xed, 0x98, 0x2e, 0x25, 0x65, - 0x86, 0xa9, 0x5e, 0x5e, 0x17, 0xed, 0x58, 0x61, 0xa0, 0xb7, 0x61, 0xbc, 0xe5, 0xc4, 0xf1, 0xed, - 0x30, 0xaa, 0x61, 0x52, 0xcf, 0x27, 0xbb, 0x74, 0x93, 0xb8, 0x11, 0x49, 0x30, 0xa9, 0x0b, 0x8f, - 0x96, 0xa6, 0x8f, 0x4d, 0x66, 0xf6, 0xbf, 0xa8, 0xc0, 0x98, 0x70, 0xc7, 0x0d, 0x9c, 0xb5, 0x27, - 0x0d, 0xcc, 0x42, 0x5f, 0x03, 0x33, 0x86, 0x51, 0x97, 0xd5, 0x38, 0x11, 0x9a, 0xcc, 0xd5, 0x5c, - 0xfc, 0xb7, 0xbc, 0x6c, 0x8a, 0xee, 0x16, 0xff, 0x8f, 0x05, 0x2b, 0xf4, 0x2d, 0x0b, 0x4e, 0xb8, - 0x61, 0x10, 0x10, 0x57, 0x6f, 0xb3, 0x23, 0x79, 0x44, 0x64, 0x2c, 0xa6, 0x89, 0xea, 0xc3, 0xdf, - 0x0c, 0x00, 0x67, 0xd9, 0xa3, 0x57, 0x60, 0x92, 0x8f, 0xd9, 0x8d, 0xd4, 0xc9, 0x97, 0x4e, 0x4e, - 0x37, 0x81, 0x38, 0x8d, 0x8b, 0xe6, 0xf8, 0x09, 0xa2, 0x48, 0x03, 0x1f, 0xd5, 0x9e, 0x04, 0x23, - 0x01, 0xdc, 0xc0, 0x40, 0x11, 0xa0, 0x88, 0xd4, 0x23, 0x12, 0xef, 0x08, 0x77, 0x25, 0xdb, 0xe2, - 0xc7, 0xee, 0x2f, 0xf1, 0x08, 0x77, 0x51, 0xc2, 0x3d, 0xa8, 0xa3, 0x5d, 0x61, 0xe3, 0x94, 0xf3, - 0x90, 0x0a, 0xe2, 0x33, 0xf7, 0x35, 0x75, 0x66, 0xa1, 0x14, 0xef, 0x38, 0x51, 0x8d, 0xa9, 0x16, - 0x45, 0x7e, 0x10, 0xb0, 0x49, 0x1b, 0x30, 0x6f, 0x47, 0x4b, 0x70, 0x32, 0x93, 0x5a, 0x1f, 0x33, - 0xe5, 0xa1, 0xac, 0xe3, 0x50, 0x33, 0x49, 0xf9, 0x31, 0xee, 0x7a, 0xc2, 0xb4, 0x7f, 0xc7, 0x0f, - 0xb1, 0x7f, 0x3b, 0x2a, 0x28, 0x66, 0x82, 0x49, 0xfc, 0xd7, 0x72, 0x19, 0x80, 0x81, 0x22, 0x60, - 0xbe, 0x91, 0x89, 0x80, 0x99, 0x64, 0x1d, 0xb8, 0x91, 0x4f, 0x07, 0x86, 0x0f, 0x77, 0x79, 0x98, - 0xe1, 0x2b, 0xff, 0xdb, 0x02, 0xf9, 0x5d, 0x17, 0x1d, 0x77, 0x87, 0xd0, 0x29, 0x83, 0x5e, 0x85, - 0x29, 0x65, 0xc5, 0x2d, 0x86, 0xed, 0x80, 0x47, 0xae, 0x14, 0xb5, 0x97, 0x08, 0xa7, 0xa0, 0x38, - 0x83, 0x8d, 0xe6, 0xa1, 0x42, 0xc7, 0x89, 0x3f, 0xca, 0x77, 0x0f, 0x65, 0x29, 0x2e, 0x6c, 0xac, - 0x88, 0xa7, 0x34, 0x0e, 0x0a, 0x61, 0xda, 0x77, 0xe2, 0x84, 0xf5, 0x80, 0x1a, 0x75, 0xf7, 0x99, - 0xf6, 0xc7, 0x2a, 0x8b, 0xac, 0x66, 0x09, 0xe1, 0x6e, 0xda, 0xf6, 0x0f, 0x46, 0x60, 0x32, 0x25, - 0x19, 0x87, 0xdc, 0x76, 0x9e, 0x87, 0xb2, 0xdc, 0x09, 0xb2, 0x99, 0xc0, 0x6a, 0xbb, 0x50, 0x18, - 0x74, 0x9b, 0xdc, 0x26, 0x4e, 0x44, 0x22, 0x56, 0xb4, 0x20, 0xbb, 0x4d, 0x56, 0x35, 0x08, 0x9b, - 0x78, 0x4c, 0x28, 0x27, 0x7e, 0xbc, 0xe8, 0x7b, 0x24, 0x48, 0x78, 0x37, 0xf3, 0x11, 0xca, 0x5b, - 0xab, 0x9b, 0x26, 0x51, 0x2d, 0x94, 0x33, 0x00, 0x9c, 0x65, 0x8f, 0xbe, 0x62, 0xc1, 0xa4, 0x73, - 0x3b, 0xd6, 0x85, 0xb8, 0x44, 0xac, 0xcb, 0x11, 0x37, 0xa9, 0x54, 0x6d, 0xaf, 0xea, 0x34, 0x15, - 0xef, 0xa9, 0x26, 0x9c, 0x66, 0x8a, 0xbe, 0x6d, 0x01, 0x22, 0xfb, 0xc4, 0x95, 0xd1, 0x38, 0xa2, - 0x2f, 0xa3, 0x79, 0x18, 0x3b, 0x17, 0xbb, 0xe8, 0x72, 0xa9, 0xde, 0xdd, 0x8e, 0x7b, 0xf4, 0xc1, - 0xfe, 0x27, 0x45, 0xb5, 0xa0, 0x74, 0x00, 0x98, 0x63, 0x24, 0x2f, 0x58, 0xf7, 0x9f, 0xbc, 0xa0, - 0x5d, 0x94, 0x5d, 0x09, 0x0c, 0xe9, 0x58, 0xf1, 0xc2, 0x43, 0x8a, 0x15, 0xff, 0xb2, 0x95, 0xca, - 0x79, 0x1f, 0xbf, 0xf0, 0x7a, 0xbe, 0xc1, 0x67, 0x73, 0xdc, 0x41, 0x9e, 0x91, 0xee, 0x69, 0xaf, - 0x39, 0x95, 0xa6, 0x06, 0xda, 0x50, 0xd2, 0xf0, 0xdf, 0x17, 0x61, 0xdc, 0xd8, 0x49, 0x7b, 0xaa, - 0x45, 0xd6, 0x23, 0xa6, 0x16, 0x15, 0x86, 0x50, 0x8b, 0x7e, 0x01, 0x2a, 0xae, 0x94, 0xf2, 0xf9, - 0x54, 0x7d, 0xcb, 0xee, 0x1d, 0x5a, 0xd0, 0xab, 0x26, 0xac, 0x79, 0xa2, 0xe5, 0x54, 0x74, 0xba, - 0xd8, 0x21, 0x46, 0xd8, 0x0e, 0xd1, 0x2b, 0x7c, 0x5c, 0xec, 0x14, 0xdd, 0xcf, 0xa0, 0x17, 0xa9, - 0x65, 0xe5, 0x89, 0xf7, 0x92, 0x21, 0xa2, 0x4c, 0x5d, 0x5f, 0xd8, 0x58, 0x91, 0xcd, 0xd8, 0xc4, - 0xb1, 0x7f, 0x60, 0xa9, 0x8f, 0xfb, 0x00, 0xd2, 0x21, 0x6f, 0xa5, 0xd3, 0x21, 0x2f, 0xe6, 0x32, - 0xcc, 0x7d, 0xf2, 0x20, 0xaf, 0xc1, 0xd8, 0x62, 0xd8, 0x6c, 0x3a, 0x41, 0x0d, 0xfd, 0x04, 0x8c, - 0xb9, 0xfc, 0xa7, 0x38, 0xaa, 0x60, 0xfe, 0x29, 0x01, 0xc5, 0x12, 0x86, 0x9e, 0x82, 0x11, 0x27, - 0x6a, 0xc8, 0xe3, 0x09, 0xe6, 0xd2, 0x5f, 0x88, 0x1a, 0x31, 0x66, 0xad, 0xf6, 0x3b, 0x45, 0x80, - 0xc5, 0xb0, 0xd9, 0x72, 0x22, 0x52, 0xdb, 0x0a, 0x59, 0xd5, 0x99, 0x63, 0xf5, 0xeb, 0x68, 0x63, - 0xe9, 0x51, 0xf6, 0xed, 0x18, 0xe7, 0xfb, 0xc5, 0x07, 0x7d, 0xbe, 0xff, 0x75, 0x0b, 0x10, 0xfd, - 0x22, 0x61, 0x40, 0x82, 0x44, 0xbb, 0x2b, 0xe7, 0xa1, 0xe2, 0xca, 0x56, 0xa1, 0xb5, 0xe8, 0xf5, - 0x27, 0x01, 0x58, 0xe3, 0x0c, 0x60, 0x7e, 0x3e, 0x23, 0x85, 0x63, 0x31, 0x1d, 0x05, 0xc7, 0x44, - 0xaa, 0x90, 0x95, 0xf6, 0x1f, 0x14, 0xe0, 0x31, 0xbe, 0xdf, 0xad, 0x39, 0x81, 0xd3, 0x20, 0x4d, - 0xda, 0xab, 0x41, 0x1d, 0xd0, 0x2e, 0xb5, 0x7b, 0x3c, 0x19, 0xd5, 0x76, 0xd4, 0x85, 0xc1, 0x27, - 0x34, 0x9f, 0xc2, 0x2b, 0x81, 0x97, 0x60, 0x46, 0x1c, 0xc5, 0x50, 0x96, 0x35, 0x44, 0x85, 0xa0, - 0xcb, 0x89, 0x91, 0x5a, 0xf3, 0x62, 0x53, 0x22, 0x58, 0x31, 0xa2, 0x5a, 0xa1, 0x1f, 0xba, 0xbb, - 0x98, 0xb4, 0x42, 0x26, 0xd4, 0x8c, 0xa0, 0xa2, 0x55, 0xd1, 0x8e, 0x15, 0x86, 0xfd, 0x07, 0x16, - 0x64, 0xc5, 0xbd, 0x51, 0xb0, 0xc3, 0xba, 0x57, 0xc1, 0x8e, 0x61, 0x6a, 0x6a, 0xfc, 0x2c, 0x8c, - 0x3b, 0x09, 0xdd, 0xa1, 0xb9, 0x4d, 0x5b, 0xbc, 0xbf, 0x63, 0xeb, 0xb5, 0xb0, 0xe6, 0xd5, 0x3d, - 0x66, 0xcb, 0x9a, 0xe4, 0xec, 0xff, 0x39, 0x02, 0xd3, 0x5d, 0x91, 0xca, 0xe8, 0x65, 0x98, 0x70, - 0xc5, 0xf4, 0x68, 0x61, 0x52, 0x17, 0x2f, 0x63, 0x44, 0xba, 0x68, 0x18, 0x4e, 0x61, 0x0e, 0x30, - 0x41, 0x57, 0xe0, 0x54, 0x44, 0xad, 0xe8, 0x36, 0x59, 0xa8, 0x27, 0x24, 0xda, 0x24, 0x6e, 0x18, - 0xd4, 0x78, 0x59, 0x99, 0x62, 0xf5, 0xf1, 0x3b, 0x07, 0xb3, 0xa7, 0x70, 0x37, 0x18, 0xf7, 0x7a, - 0x06, 0xb5, 0x60, 0xd2, 0x37, 0x15, 0x2c, 0xa1, 0x5d, 0xdf, 0x97, 0x6e, 0xa6, 0x36, 0xe0, 0x54, - 0x33, 0x4e, 0x33, 0x48, 0x6b, 0x69, 0xa5, 0x87, 0xa4, 0xa5, 0xfd, 0xa2, 0xd6, 0xd2, 0xb8, 0x7f, - 0xf5, 0x8d, 0x9c, 0x23, 0xd5, 0x8f, 0x5b, 0x4d, 0x7b, 0x0d, 0xca, 0x32, 0xf2, 0x64, 0xa0, 0x88, - 0x0d, 0x93, 0x4e, 0x1f, 0x89, 0x76, 0xb7, 0x00, 0x3d, 0x34, 0x7c, 0xba, 0xce, 0xf4, 0x76, 0x9a, - 0x5a, 0x67, 0xc3, 0x6d, 0xa9, 0x68, 0x9f, 0x47, 0xdd, 0xf0, 0x8d, 0xe3, 0xd3, 0x79, 0x5b, 0x28, - 0x3a, 0x10, 0x47, 0x85, 0x80, 0xa8, 0x60, 0x9c, 0x0b, 0x00, 0x5a, 0x0b, 0x12, 0x01, 0xa7, 0xca, - 0xad, 0xa7, 0x95, 0x25, 0x6c, 0x60, 0x51, 0x83, 0xd5, 0x0b, 0xe2, 0xc4, 0xf1, 0xfd, 0xcb, 0x5e, - 0x90, 0x88, 0x93, 0x37, 0xb5, 0x43, 0xae, 0x68, 0x10, 0x36, 0xf1, 0xce, 0x7e, 0xc2, 0xf8, 0x2e, - 0xc3, 0x7c, 0xcf, 0x1d, 0x78, 0x62, 0xd9, 0x4b, 0x54, 0x98, 0xb4, 0x9a, 0x47, 0x54, 0xc9, 0x51, - 0x61, 0xff, 0x56, 0xdf, 0xb0, 0x7f, 0x23, 0x4c, 0xb9, 0x90, 0x8e, 0xaa, 0xce, 0x86, 0x29, 0xdb, - 0x2f, 0xc3, 0xe9, 0x65, 0x2f, 0xb9, 0xe4, 0xf9, 0x64, 0x48, 0x26, 0xf6, 0x57, 0x4a, 0x30, 0x61, - 0xa6, 0xa5, 0x0c, 0x93, 0xb9, 0xf0, 0x4d, 0xaa, 0xc7, 0x88, 0xb7, 0xf3, 0x94, 0x8f, 0xe5, 0xe6, - 0x91, 0x73, 0x64, 0x7a, 0x8f, 0x98, 0xa1, 0xca, 0x68, 0x9e, 0xd8, 0xec, 0x00, 0xba, 0x0d, 0xa5, - 0x3a, 0x0b, 0xa3, 0x2d, 0xe6, 0xe1, 0x39, 0xee, 0x35, 0xa2, 0x7a, 0x99, 0xf1, 0x40, 0x5c, 0xce, - 0x8f, 0xee, 0x90, 0x51, 0x3a, 0x37, 0x43, 0x09, 0x2a, 0x95, 0x95, 0xa1, 0x30, 0xfa, 0x89, 0xfa, - 0xd2, 0x7d, 0x88, 0xfa, 0x94, 0xe0, 0x1d, 0x7d, 0x48, 0x82, 0x97, 0x85, 0x44, 0x27, 0x3b, 0x4c, - 0x7f, 0x13, 0x01, 0xb1, 0x63, 0x6c, 0x10, 0x8c, 0x90, 0xe8, 0x14, 0x18, 0x67, 0xf1, 0xed, 0xaf, - 0x17, 0x60, 0x6a, 0x39, 0x68, 0x6f, 0x2c, 0x6f, 0xb4, 0xb7, 0x7d, 0xcf, 0xbd, 0x4a, 0x3a, 0x54, - 0xbe, 0xed, 0x92, 0xce, 0xca, 0x92, 0x98, 0x86, 0x6a, 0xe0, 0xaf, 0xd2, 0x46, 0xcc, 0x61, 0x74, - 0x45, 0xd7, 0xbd, 0xa0, 0x41, 0xa2, 0x56, 0xe4, 0x89, 0x43, 0x39, 0x63, 0x45, 0x5f, 0xd2, 0x20, - 0x6c, 0xe2, 0x51, 0xda, 0xe1, 0xed, 0x80, 0x44, 0x59, 0x6d, 0x70, 0x9d, 0x36, 0x62, 0x0e, 0xa3, - 0x48, 0x49, 0xd4, 0x8e, 0x13, 0xf1, 0x45, 0x15, 0xd2, 0x16, 0x6d, 0xc4, 0x1c, 0x46, 0x97, 0x4b, - 0xdc, 0xde, 0x66, 0xde, 0xed, 0x4c, 0x08, 0xeb, 0x26, 0x6f, 0xc6, 0x12, 0x4e, 0x51, 0x77, 0x49, - 0x67, 0x89, 0xda, 0x65, 0x99, 0x20, 0xf3, 0xab, 0xbc, 0x19, 0x4b, 0x38, 0xab, 0x74, 0x93, 0x1e, - 0x8e, 0x1f, 0xb9, 0x4a, 0x37, 0xe9, 0xee, 0xf7, 0xb1, 0xf0, 0x7e, 0xd3, 0x82, 0x09, 0x33, 0x26, - 0x05, 0x35, 0x32, 0x8a, 0xe2, 0x7a, 0x57, 0xd5, 0xb2, 0x9f, 0xee, 0x75, 0x5f, 0x42, 0xc3, 0x4b, - 0xc2, 0x56, 0xfc, 0x02, 0x09, 0x1a, 0x5e, 0x40, 0x98, 0xe7, 0x92, 0xc7, 0xb2, 0xa4, 0x02, 0x5e, - 0x16, 0xc3, 0x1a, 0xb9, 0x0f, 0x4d, 0xd3, 0xbe, 0x09, 0xd3, 0x5d, 0x99, 0x05, 0x03, 0xec, 0xcf, - 0x87, 0xe6, 0x75, 0xd9, 0x18, 0xc6, 0x29, 0xe1, 0xf5, 0x16, 0x0f, 0x3a, 0x59, 0x84, 0x69, 0xae, - 0x43, 0x50, 0x4e, 0x9b, 0xee, 0x0e, 0x69, 0xaa, 0x6c, 0x11, 0x76, 0x02, 0x7c, 0x23, 0x0b, 0xc4, - 0xdd, 0xf8, 0xf6, 0x37, 0x2c, 0x98, 0x4c, 0x25, 0x7b, 0xe4, 0xa4, 0x49, 0xb0, 0x95, 0x16, 0xb2, - 0x10, 0x29, 0x16, 0x25, 0x5a, 0x64, 0x3b, 0x92, 0x5e, 0x69, 0x1a, 0x84, 0x4d, 0x3c, 0xfb, 0xdd, - 0x02, 0x94, 0xa5, 0xd7, 0x7a, 0x80, 0xae, 0x7c, 0xcd, 0x82, 0x49, 0x75, 0xea, 0xce, 0x8e, 0x73, - 0xf8, 0x64, 0xbc, 0x76, 0x74, 0xbf, 0xb9, 0x8a, 0xe1, 0x0b, 0xea, 0xa1, 0x56, 0x6b, 0xb1, 0xc9, - 0x0c, 0xa7, 0x79, 0xa3, 0x1b, 0x00, 0x71, 0x27, 0x4e, 0x48, 0xd3, 0x38, 0x58, 0xb2, 0x8d, 0x15, - 0x37, 0xe7, 0x86, 0x11, 0xa1, 0xeb, 0xeb, 0x5a, 0x58, 0x23, 0x9b, 0x0a, 0x53, 0xeb, 0x21, 0xba, - 0x0d, 0x1b, 0x94, 0xec, 0xbf, 0x5f, 0x80, 0x93, 0xd9, 0x2e, 0xa1, 0x37, 0x60, 0x42, 0x72, 0x37, - 0xee, 0x7e, 0x90, 0xae, 0xfa, 0x09, 0x6c, 0xc0, 0xee, 0x1e, 0xcc, 0xce, 0x76, 0xdf, 0xbd, 0x31, - 0x67, 0xa2, 0xe0, 0x14, 0x31, 0xee, 0xfa, 0x10, 0x3e, 0xba, 0x6a, 0x67, 0xa1, 0xd5, 0x12, 0xfe, - 0x0b, 0xc3, 0xf5, 0x61, 0x42, 0x71, 0x06, 0x1b, 0x6d, 0xc0, 0x69, 0xa3, 0xe5, 0x1a, 0xf1, 0x1a, - 0x3b, 0xdb, 0x61, 0x24, 0xcd, 0x93, 0xa7, 0x74, 0xf4, 0x4b, 0x37, 0x0e, 0xee, 0xf9, 0x24, 0xdd, - 0x32, 0x5d, 0xa7, 0xe5, 0xb8, 0x5e, 0xd2, 0x11, 0x27, 0x65, 0x4a, 0x36, 0x2d, 0x8a, 0x76, 0xac, - 0x30, 0xec, 0x35, 0x18, 0x19, 0x70, 0x06, 0x0d, 0xa4, 0x16, 0xbf, 0x06, 0x65, 0x4a, 0x4e, 0xea, - 0x48, 0x79, 0x90, 0x0c, 0xa1, 0x2c, 0xcb, 0x37, 0x23, 0x1b, 0x8a, 0x9e, 0x23, 0xbd, 0x4b, 0xea, - 0xb5, 0x56, 0xe2, 0xb8, 0xcd, 0x2c, 0x4d, 0x0a, 0x44, 0xcf, 0x40, 0x91, 0xec, 0xb7, 0xb2, 0x6e, - 0xa4, 0x8b, 0xfb, 0x2d, 0x2f, 0x22, 0x31, 0x45, 0x22, 0xfb, 0x2d, 0x74, 0x16, 0x0a, 0x5e, 0x4d, - 0x6c, 0x52, 0x20, 0x70, 0x0a, 0x2b, 0x4b, 0xb8, 0xe0, 0xd5, 0xec, 0x7d, 0xa8, 0xa8, 0x7a, 0xd1, - 0x68, 0x57, 0xca, 0x6e, 0x2b, 0x8f, 0x30, 0x13, 0x49, 0xb7, 0x8f, 0xd4, 0x6e, 0x03, 0xe8, 0xd4, - 0x9a, 0xbc, 0xe4, 0xcb, 0x79, 0x18, 0x71, 0x43, 0x91, 0x91, 0x57, 0xd6, 0x64, 0x98, 0xd0, 0x66, - 0x10, 0xfb, 0x26, 0x4c, 0x5d, 0x0d, 0xc2, 0xdb, 0xac, 0x06, 0xe7, 0x25, 0x8f, 0xf8, 0x35, 0x4a, - 0xb8, 0x4e, 0x7f, 0x64, 0x55, 0x04, 0x06, 0xc5, 0x1c, 0xa6, 0xca, 0x6e, 0x14, 0xfa, 0x95, 0xdd, - 0xb0, 0xbf, 0x68, 0xc1, 0x49, 0x95, 0xf3, 0x21, 0xa5, 0xf1, 0xcb, 0x30, 0xb1, 0xdd, 0xf6, 0xfc, - 0x9a, 0xf8, 0x9f, 0xb5, 0xf5, 0xab, 0x06, 0x0c, 0xa7, 0x30, 0xa9, 0x65, 0xb2, 0xed, 0x05, 0x4e, - 0xd4, 0xd9, 0xd0, 0xe2, 0x5f, 0x49, 0x84, 0xaa, 0x82, 0x60, 0x03, 0xcb, 0xfe, 0x72, 0x01, 0x26, - 0x53, 0x19, 0xf0, 0xc8, 0x87, 0x32, 0xf1, 0xd9, 0x09, 0x94, 0xfc, 0xa8, 0x47, 0x2d, 0x3e, 0xa5, - 0x26, 0xe2, 0x45, 0x41, 0x17, 0x2b, 0x0e, 0x8f, 0x84, 0x9b, 0xc5, 0xfe, 0xc3, 0x22, 0xcc, 0xf0, - 0x83, 0xb7, 0x9a, 0x8a, 0x67, 0x58, 0x93, 0xda, 0xc9, 0x5f, 0xd5, 0xd5, 0x26, 0xf8, 0x70, 0x6c, - 0x1f, 0xb5, 0x7c, 0x62, 0x6f, 0x46, 0x03, 0x79, 0xda, 0x7f, 0x3d, 0xe3, 0x69, 0x2f, 0xe4, 0x91, - 0x10, 0xd1, 0xb7, 0x47, 0x3f, 0x5a, 0xae, 0xf7, 0xbf, 0x53, 0x80, 0x13, 0x99, 0xda, 0x94, 0xe8, - 0x9d, 0x74, 0xf5, 0x29, 0x2b, 0x8f, 0xe3, 0x99, 0x7b, 0x56, 0x48, 0x1c, 0xae, 0x06, 0xd5, 0xc3, - 0x9a, 0xf0, 0x7f, 0x54, 0x80, 0xa9, 0x74, 0x51, 0xcd, 0x47, 0x70, 0xa4, 0x3e, 0x0a, 0x15, 0x56, - 0xaa, 0x8e, 0xdd, 0xca, 0xc1, 0x4f, 0x81, 0x78, 0x45, 0x35, 0xd9, 0x88, 0x35, 0xfc, 0x91, 0x28, - 0xed, 0x65, 0xff, 0x5d, 0x0b, 0xce, 0xf0, 0xb7, 0xcc, 0xce, 0xc3, 0xbf, 0xd6, 0x6b, 0x74, 0xdf, - 0xcc, 0xb7, 0x83, 0x99, 0x2a, 0x29, 0x87, 0x8d, 0x2f, 0xbb, 0x0d, 0x40, 0xf4, 0x36, 0x3d, 0x15, - 0x1e, 0xc1, 0xce, 0x0e, 0x35, 0x19, 0xec, 0x3f, 0x2a, 0x82, 0xbe, 0x00, 0x01, 0x79, 0x22, 0x6d, - 0x22, 0x97, 0x6a, 0x31, 0x9b, 0x9d, 0xc0, 0xd5, 0x57, 0x2d, 0x94, 0x33, 0x59, 0x13, 0xbf, 0x62, - 0xc1, 0xb8, 0x17, 0x78, 0x89, 0xe7, 0x30, 0xa5, 0x33, 0x9f, 0x8a, 0xf0, 0x8a, 0xdd, 0x0a, 0xa7, - 0x1c, 0x46, 0xe6, 0xd1, 0xa1, 0x62, 0x86, 0x4d, 0xce, 0xe8, 0x73, 0x22, 0x18, 0xae, 0x98, 0x5b, - 0xc2, 0x4f, 0x39, 0x13, 0x01, 0xd7, 0x82, 0x52, 0x44, 0x92, 0x48, 0xa6, 0x5a, 0x5d, 0x3d, 0x6a, - 0x84, 0x73, 0x12, 0x75, 0x54, 0x71, 0x30, 0x7d, 0x15, 0x15, 0x6d, 0xc6, 0x9c, 0x91, 0x1d, 0x03, - 0xea, 0x1e, 0x8b, 0x21, 0x03, 0x8d, 0xe6, 0xa1, 0xe2, 0xb4, 0x93, 0xb0, 0x49, 0x87, 0x49, 0x9c, - 0x6e, 0xea, 0x50, 0x2a, 0x09, 0xc0, 0x1a, 0xc7, 0x7e, 0xa7, 0x04, 0x99, 0x3c, 0x06, 0xb4, 0x6f, - 0x5e, 0xde, 0x61, 0xe5, 0x7b, 0x79, 0x87, 0xea, 0x4c, 0xaf, 0x0b, 0x3c, 0x50, 0x03, 0x4a, 0xad, - 0x1d, 0x27, 0x96, 0x3a, 0xe5, 0x6b, 0x72, 0x98, 0x36, 0x68, 0xe3, 0xdd, 0x83, 0xd9, 0x9f, 0x19, - 0xec, 0x8c, 0x82, 0xce, 0xd5, 0x79, 0x9e, 0x2f, 0xac, 0x59, 0x33, 0x1a, 0x98, 0xd3, 0x1f, 0xa6, - 0x26, 0xfe, 0x97, 0x44, 0x3d, 0x43, 0x4c, 0xe2, 0xb6, 0x9f, 0x88, 0xd9, 0xf0, 0x5a, 0x8e, 0xab, - 0x8c, 0x13, 0xd6, 0x19, 0x78, 0xfc, 0x3f, 0x36, 0x98, 0xa2, 0x37, 0xa0, 0x12, 0x27, 0x4e, 0x94, - 0xdc, 0x67, 0xce, 0x8c, 0x1a, 0xf4, 0x4d, 0x49, 0x04, 0x6b, 0x7a, 0xe8, 0x75, 0x56, 0x3c, 0xcb, - 0x8b, 0x77, 0xee, 0x33, 0x86, 0x55, 0x16, 0xda, 0x12, 0x14, 0xb0, 0x41, 0x8d, 0xaa, 0xec, 0x6c, - 0x6e, 0xf3, 0xc0, 0x8d, 0x32, 0xb3, 0xc9, 0x94, 0x28, 0xc4, 0x0a, 0x82, 0x0d, 0x2c, 0xfb, 0x0b, - 0x70, 0x2a, 0x7b, 0xdb, 0x97, 0x38, 0xb6, 0x6c, 0x44, 0x61, 0xbb, 0x95, 0xb5, 0x49, 0xd8, 0x6d, - 0x50, 0x98, 0xc3, 0xa8, 0x4d, 0xb2, 0xeb, 0x05, 0xb5, 0xac, 0x4d, 0x72, 0xd5, 0x0b, 0x6a, 0x98, - 0x41, 0x06, 0xb8, 0xd5, 0xe4, 0x9f, 0x5a, 0x70, 0xfe, 0xb0, 0x4b, 0xc9, 0xd0, 0x53, 0x30, 0x72, - 0xdb, 0x89, 0x64, 0x31, 0x3e, 0x26, 0x3b, 0x6e, 0x3a, 0x51, 0x80, 0x59, 0x2b, 0xea, 0xc0, 0x28, - 0xcf, 0x51, 0x14, 0x0a, 0xec, 0x6b, 0xf9, 0x5e, 0x91, 0x76, 0x95, 0x18, 0x1a, 0x34, 0xcf, 0x8f, - 0xc4, 0x82, 0xa1, 0xfd, 0xbe, 0x05, 0x68, 0x7d, 0x8f, 0x44, 0x91, 0x57, 0x33, 0xb2, 0x2a, 0xd1, - 0x4b, 0x30, 0x71, 0x6b, 0x73, 0xfd, 0xda, 0x46, 0xe8, 0x05, 0x2c, 0xc7, 0xda, 0xc8, 0x4b, 0xb9, - 0x62, 0xb4, 0xe3, 0x14, 0x16, 0x5a, 0x84, 0xe9, 0x5b, 0x6f, 0x51, 0x3b, 0xca, 0xac, 0x63, 0x5b, - 0xd0, 0x27, 0x67, 0x57, 0x5e, 0xcb, 0x00, 0x71, 0x37, 0x3e, 0x5a, 0x87, 0x33, 0x4d, 0xae, 0x81, - 0x33, 0xf3, 0x31, 0xe6, 0xea, 0x78, 0x24, 0x0b, 0x2f, 0x3c, 0x71, 0xe7, 0x60, 0xf6, 0xcc, 0x5a, - 0x2f, 0x04, 0xdc, 0xfb, 0x39, 0xfb, 0xbb, 0x05, 0x18, 0x37, 0x2e, 0xf6, 0x1b, 0xc0, 0x50, 0xce, - 0xdc, 0x45, 0x58, 0x18, 0xf0, 0x2e, 0xc2, 0xe7, 0xa0, 0xdc, 0x0a, 0x7d, 0xcf, 0xf5, 0x54, 0x95, - 0x08, 0x56, 0xcc, 0x6c, 0x43, 0xb4, 0x61, 0x05, 0x45, 0xb7, 0xa1, 0xa2, 0x2e, 0xbb, 0x12, 0xc9, - 0x7d, 0x79, 0x1d, 0x15, 0xa8, 0xc5, 0xab, 0x2f, 0xb1, 0xd2, 0xbc, 0x90, 0x0d, 0xa3, 0x6c, 0xe6, - 0xcb, 0x90, 0x26, 0x96, 0x75, 0xc1, 0x96, 0x44, 0x8c, 0x05, 0xc4, 0xfe, 0xa5, 0x31, 0x38, 0xdd, - 0xab, 0x00, 0x17, 0xfa, 0x3c, 0x8c, 0xf2, 0x3e, 0xe6, 0x53, 0xe3, 0xb1, 0x17, 0x8f, 0x65, 0x46, - 0x50, 0x74, 0x8b, 0xfd, 0xc6, 0x82, 0xa7, 0xe0, 0xee, 0x3b, 0xdb, 0x42, 0x8d, 0x38, 0x1e, 0xee, - 0xab, 0x8e, 0xe6, 0xbe, 0xea, 0x70, 0xee, 0xbe, 0xb3, 0x8d, 0xf6, 0xa1, 0xd4, 0xf0, 0x12, 0xe2, - 0x08, 0x65, 0xfa, 0xe6, 0xb1, 0x30, 0x27, 0x0e, 0x8f, 0x9c, 0x67, 0x3f, 0x31, 0x67, 0x88, 0xbe, - 0x63, 0xc1, 0x89, 0xed, 0x74, 0x12, 0x8b, 0xd8, 0x55, 0x9c, 0x63, 0x28, 0xb2, 0x96, 0x66, 0x54, - 0x3d, 0x75, 0xe7, 0x60, 0xf6, 0x44, 0xa6, 0x11, 0x67, 0xbb, 0x83, 0x7e, 0xd1, 0x82, 0xb1, 0xba, - 0xe7, 0x1b, 0x15, 0x84, 0x8e, 0xe1, 0xe3, 0x5c, 0x62, 0x0c, 0xf4, 0xce, 0xcb, 0xff, 0xc7, 0x58, - 0x72, 0xee, 0xe7, 0xce, 0x1b, 0x3d, 0xaa, 0x3b, 0x6f, 0xec, 0x21, 0x99, 0x4f, 0x7f, 0xbd, 0x00, - 0xcf, 0x0c, 0xf0, 0x8d, 0xcc, 0xa4, 0x08, 0xeb, 0x90, 0xa4, 0x88, 0xf3, 0x30, 0x12, 0x91, 0x56, - 0x98, 0xdd, 0xef, 0x58, 0xe4, 0x10, 0x83, 0xa0, 0xa7, 0xa1, 0xe8, 0xb4, 0x3c, 0xb1, 0xdd, 0x29, - 0x6f, 0xff, 0xc2, 0xc6, 0x0a, 0xa6, 0xed, 0xf4, 0x4b, 0x57, 0xb6, 0x65, 0x6a, 0x55, 0x3e, 0xc5, - 0x9a, 0xfb, 0x65, 0x6a, 0x71, 0x83, 0x46, 0x41, 0xb1, 0xe6, 0x6b, 0xaf, 0xc3, 0xd9, 0xfe, 0x33, - 0x04, 0xbd, 0x08, 0xe3, 0xdb, 0x91, 0x13, 0xb8, 0x3b, 0xac, 0xb0, 0xb9, 0x1c, 0x13, 0x16, 0x0a, - 0xaf, 0x9b, 0xb1, 0x89, 0x63, 0xff, 0x61, 0xa1, 0x37, 0x45, 0x2e, 0x04, 0x86, 0x19, 0x61, 0x31, - 0x7e, 0x85, 0x3e, 0xe3, 0xf7, 0x16, 0x94, 0x13, 0x16, 0x89, 0x4f, 0xea, 0x42, 0x92, 0xe4, 0x96, - 0x4c, 0xc6, 0xf6, 0x9a, 0x2d, 0x41, 0x1c, 0x2b, 0x36, 0x54, 0xe4, 0xfb, 0xba, 0xf8, 0x90, 0x10, - 0xf9, 0x99, 0x73, 0xb4, 0x25, 0x38, 0x69, 0xd4, 0x52, 0xe4, 0x81, 0xc8, 0xdc, 0x8d, 0xaa, 0xb2, - 0x73, 0x36, 0x32, 0x70, 0xdc, 0xf5, 0x84, 0xfd, 0x9b, 0x05, 0x78, 0xa2, 0xaf, 0x64, 0xd3, 0xbe, - 0x5e, 0xeb, 0x1e, 0xbe, 0xde, 0x23, 0x4f, 0x50, 0x73, 0x80, 0x47, 0x1e, 0xcc, 0x00, 0x3f, 0x0f, - 0x65, 0x2f, 0x88, 0x89, 0xdb, 0x8e, 0xf8, 0xa0, 0x19, 0x61, 0x79, 0x2b, 0xa2, 0x1d, 0x2b, 0x0c, - 0xfb, 0x8f, 0xfb, 0x4f, 0x35, 0xba, 0xcb, 0xfd, 0xd8, 0x8e, 0xd2, 0x2b, 0x30, 0xe9, 0xb4, 0x5a, - 0x1c, 0x8f, 0xf9, 0xd5, 0x32, 0xf9, 0x76, 0x0b, 0x26, 0x10, 0xa7, 0x71, 0x8d, 0x39, 0x3c, 0xda, - 0x6f, 0x0e, 0xdb, 0x7f, 0x66, 0x41, 0x05, 0x93, 0x3a, 0xaf, 0xc1, 0x89, 0x6e, 0x89, 0x21, 0xb2, - 0xf2, 0x28, 0x0e, 0xc1, 0xae, 0xcc, 0xf6, 0x58, 0xd1, 0x84, 0x5e, 0x83, 0xdd, 0x5d, 0x17, 0xb4, - 0x30, 0x54, 0x5d, 0x50, 0x55, 0x19, 0xb2, 0xd8, 0xbf, 0x32, 0xa4, 0xfd, 0xc3, 0x51, 0xfa, 0x7a, - 0xad, 0x70, 0x31, 0x22, 0xb5, 0x98, 0x7e, 0xdf, 0x76, 0xe4, 0x67, 0x6f, 0x12, 0xbc, 0x8e, 0x57, - 0x31, 0x6d, 0x4f, 0x1d, 0x02, 0x14, 0x86, 0xca, 0x36, 0x2a, 0x1e, 0x9a, 0x6d, 0xf4, 0x0a, 0x4c, - 0xc6, 0xf1, 0xce, 0x46, 0xe4, 0xed, 0x39, 0x09, 0x35, 0x2d, 0x44, 0x58, 0x86, 0xce, 0x10, 0xd8, - 0xbc, 0xac, 0x81, 0x38, 0x8d, 0x8b, 0x96, 0x61, 0x5a, 0xe7, 0xfc, 0x90, 0x28, 0x61, 0x51, 0x18, - 0x7c, 0x26, 0xa8, 0x00, 0x7d, 0x9d, 0x25, 0x24, 0x10, 0x70, 0xf7, 0x33, 0x54, 0x62, 0xa5, 0x1a, - 0x69, 0x47, 0x46, 0xd3, 0x12, 0x2b, 0x45, 0x87, 0xf6, 0xa5, 0xeb, 0x09, 0xb4, 0x06, 0xa7, 0xf8, - 0xc4, 0x60, 0x57, 0xd7, 0xaa, 0x37, 0xe2, 0x51, 0x33, 0x4f, 0x0a, 0x42, 0xa7, 0x96, 0xbb, 0x51, - 0x70, 0xaf, 0xe7, 0xa8, 0xdd, 0xa0, 0x9a, 0x57, 0x96, 0x84, 0xfd, 0xaa, 0xec, 0x06, 0x45, 0x66, - 0xa5, 0x86, 0x4d, 0x3c, 0xf4, 0x69, 0x78, 0x5c, 0xff, 0xe5, 0xf1, 0x6e, 0xfc, 0x50, 0x67, 0x49, - 0xa4, 0x53, 0xaa, 0x3a, 0x84, 0xcb, 0x3d, 0xd1, 0x6a, 0xb8, 0xdf, 0xf3, 0x68, 0x1b, 0xce, 0x2a, - 0xd0, 0x45, 0x6a, 0xa4, 0xb5, 0x22, 0x2f, 0x26, 0x55, 0x27, 0x26, 0xd7, 0x23, 0x9f, 0x25, 0x60, - 0x56, 0x74, 0x41, 0xf5, 0x65, 0x2f, 0xb9, 0xdc, 0x0b, 0x13, 0xaf, 0xe2, 0x7b, 0x50, 0x41, 0xf3, - 0x50, 0x21, 0x81, 0xb3, 0xed, 0x93, 0xf5, 0xc5, 0x15, 0x96, 0x96, 0x69, 0x9c, 0x21, 0x5d, 0x94, - 0x00, 0xac, 0x71, 0x94, 0x27, 0x70, 0xa2, 0x6f, 0x01, 0xfe, 0x0d, 0x38, 0xdd, 0x70, 0x5b, 0x54, - 0x0f, 0xf0, 0x5c, 0xb2, 0xe0, 0xba, 0xd4, 0xd0, 0xa7, 0x1f, 0x86, 0xd7, 0x45, 0x55, 0x6e, 0xee, - 0xe5, 0xc5, 0x8d, 0x2e, 0x1c, 0xdc, 0xf3, 0x49, 0xba, 0xc6, 0x5a, 0x51, 0xb8, 0xdf, 0x99, 0x39, - 0x95, 0x5e, 0x63, 0x1b, 0xb4, 0x11, 0x73, 0x98, 0xfd, 0xa7, 0x16, 0x4c, 0xaa, 0x35, 0xf6, 0x00, - 0x22, 0x7d, 0xfc, 0x74, 0xa4, 0xcf, 0xf2, 0xd1, 0xa5, 0x14, 0xeb, 0x79, 0x1f, 0x77, 0xf1, 0xef, - 0x03, 0x80, 0x96, 0x64, 0x6a, 0x13, 0xb1, 0xfa, 0x6e, 0x22, 0x8f, 0xac, 0x14, 0xe9, 0x95, 0x37, - 0x55, 0x7a, 0xb8, 0x79, 0x53, 0x9b, 0x70, 0x46, 0x6e, 0xf1, 0xfc, 0x18, 0xe5, 0x72, 0x18, 0x2b, - 0xa1, 0x54, 0xae, 0x3e, 0x2d, 0x08, 0x9d, 0x59, 0xe9, 0x85, 0x84, 0x7b, 0x3f, 0x9b, 0xd2, 0x2c, - 0xc6, 0x0e, 0xd3, 0x2c, 0xf4, 0x3a, 0x5c, 0xad, 0xcb, 0x4a, 0x84, 0x99, 0x75, 0xb8, 0x7a, 0x69, - 0x13, 0x6b, 0x9c, 0xde, 0xc2, 0xb8, 0x92, 0x93, 0x30, 0x86, 0xa1, 0x85, 0xb1, 0x14, 0x0b, 0xe3, - 0x7d, 0xc5, 0x82, 0x3c, 0xb9, 0x99, 0xe8, 0x7b, 0x72, 0xf3, 0x2a, 0x4c, 0x79, 0xc1, 0x0e, 0x89, - 0xbc, 0x84, 0xd4, 0xd8, 0x5a, 0x10, 0x57, 0xaa, 0xab, 0xad, 0x78, 0x25, 0x05, 0xc5, 0x19, 0xec, - 0xb4, 0x2c, 0x9b, 0x1a, 0x40, 0x96, 0xf5, 0xd9, 0x41, 0x4e, 0xe4, 0xb3, 0x83, 0x9c, 0x3c, 0xfa, - 0x0e, 0x32, 0x7d, 0xac, 0x3b, 0x08, 0xca, 0x65, 0x07, 0x19, 0x44, 0x38, 0x9b, 0x46, 0xd8, 0xe9, - 0x43, 0x8c, 0xb0, 0x7e, 0xdb, 0xc7, 0x99, 0xfb, 0xdd, 0x3e, 0xec, 0xaf, 0x16, 0xe0, 0x8c, 0x96, - 0x9d, 0x74, 0xc6, 0x7a, 0x75, 0x2a, 0x3d, 0x58, 0x01, 0x5a, 0x1e, 0xf6, 0x61, 0x04, 0x8b, 0xe9, - 0xb8, 0x33, 0x05, 0xc1, 0x06, 0x16, 0x8b, 0xb9, 0x22, 0x11, 0x2b, 0xe5, 0x92, 0x15, 0xac, 0x8b, - 0xa2, 0x1d, 0x2b, 0x0c, 0x3a, 0x27, 0xe8, 0x6f, 0x11, 0xc7, 0x9a, 0x4d, 0xef, 0x5e, 0xd4, 0x20, - 0x6c, 0xe2, 0xa1, 0xe7, 0x38, 0x13, 0xb6, 0xa8, 0xa9, 0x70, 0x9d, 0x10, 0x57, 0x2b, 0xc8, 0x75, - 0xac, 0xa0, 0xb2, 0x3b, 0x2c, 0xb8, 0xae, 0xd4, 0xdd, 0x1d, 0xe6, 0xe4, 0x52, 0x18, 0xf6, 0xff, - 0xb2, 0xe0, 0x89, 0x9e, 0x43, 0xf1, 0x00, 0x36, 0xcc, 0xfd, 0xf4, 0x86, 0xb9, 0x99, 0x97, 0x5a, - 0x6f, 0xbc, 0x45, 0x9f, 0xcd, 0xf3, 0xdf, 0x59, 0x30, 0xa5, 0xf1, 0x1f, 0xc0, 0xab, 0x7a, 0xe9, - 0x57, 0xcd, 0xcf, 0x82, 0xa9, 0x74, 0xbd, 0xdb, 0x9f, 0xb2, 0x77, 0xe3, 0xbe, 0x82, 0x05, 0x57, - 0xde, 0x80, 0x7f, 0xc8, 0x19, 0x79, 0x07, 0x46, 0x59, 0xf5, 0xd3, 0x38, 0x1f, 0x9f, 0x45, 0x9a, - 0x3f, 0x8b, 0x9a, 0xd5, 0x3e, 0x0b, 0xf6, 0x37, 0xc6, 0x82, 0x21, 0x2b, 0x34, 0xe4, 0xc5, 0x54, - 0x02, 0xd7, 0x44, 0x98, 0x9a, 0x2e, 0x34, 0x24, 0xda, 0xb1, 0xc2, 0xb0, 0x9b, 0x30, 0x93, 0x26, - 0xbe, 0x44, 0xea, 0xcc, 0x35, 0x3c, 0xd0, 0x6b, 0xce, 0x43, 0xc5, 0x61, 0x4f, 0xad, 0xb6, 0x9d, - 0xec, 0x6d, 0x3c, 0x0b, 0x12, 0x80, 0x35, 0x8e, 0xfd, 0xbb, 0x16, 0x9c, 0xea, 0xf1, 0x32, 0x39, - 0x86, 0xe7, 0x25, 0x5a, 0x0a, 0xf4, 0xda, 0x24, 0x3f, 0x02, 0x63, 0x35, 0x52, 0x77, 0xa4, 0xf3, - 0xd1, 0x90, 0x93, 0x4b, 0xbc, 0x19, 0x4b, 0xb8, 0xfd, 0xdf, 0x2c, 0x38, 0x91, 0xee, 0x6b, 0x8c, - 0xae, 0x00, 0xe2, 0x2f, 0xb3, 0xe4, 0xc5, 0x6e, 0xb8, 0x47, 0xa2, 0x0e, 0x7d, 0x73, 0xde, 0xeb, - 0xb3, 0x82, 0x12, 0x5a, 0xe8, 0xc2, 0xc0, 0x3d, 0x9e, 0x62, 0x85, 0x50, 0x6a, 0x6a, 0xb4, 0xe5, - 0x4c, 0xb9, 0x91, 0xe7, 0x4c, 0xd1, 0x1f, 0xd3, 0x74, 0xd0, 0x28, 0x96, 0xd8, 0xe4, 0x6f, 0xbf, - 0x3f, 0x02, 0x2a, 0x7e, 0x97, 0xb9, 0xb9, 0x72, 0x72, 0x12, 0xa6, 0xae, 0x6c, 0x2a, 0x0e, 0x70, - 0x65, 0x93, 0x9c, 0x0c, 0x23, 0xf7, 0x72, 0x41, 0xf1, 0x53, 0x02, 0xf3, 0x30, 0x4e, 0xbd, 0xe1, - 0x96, 0x06, 0x61, 0x13, 0x8f, 0xf6, 0xc4, 0xf7, 0xf6, 0x08, 0x7f, 0x68, 0x34, 0xdd, 0x93, 0x55, - 0x09, 0xc0, 0x1a, 0x87, 0xf6, 0xa4, 0xe6, 0xd5, 0xeb, 0xc2, 0xe4, 0x55, 0x3d, 0xa1, 0xa3, 0x83, - 0x19, 0x84, 0x62, 0xec, 0x84, 0xe1, 0xae, 0xd0, 0x28, 0x15, 0xc6, 0xe5, 0x30, 0xdc, 0xc5, 0x0c, - 0x42, 0x75, 0xa0, 0x20, 0x8c, 0x9a, 0xec, 0xb6, 0xa4, 0x9a, 0xe2, 0x22, 0x34, 0x49, 0xa5, 0x03, - 0x5d, 0xeb, 0x46, 0xc1, 0xbd, 0x9e, 0xa3, 0x33, 0xb0, 0x15, 0x91, 0x9a, 0xe7, 0x26, 0x26, 0x35, - 0x48, 0xcf, 0xc0, 0x8d, 0x2e, 0x0c, 0xdc, 0xe3, 0x29, 0xb4, 0x00, 0x27, 0x64, 0xfc, 0xb5, 0x4c, - 0x51, 0x1b, 0x4f, 0xa7, 0xc4, 0xe0, 0x34, 0x18, 0x67, 0xf1, 0xa9, 0xb4, 0x69, 0x8a, 0xec, 0x54, - 0xa6, 0x78, 0x1a, 0xd2, 0x46, 0x66, 0xad, 0x62, 0x85, 0x61, 0x7f, 0xa9, 0x48, 0x77, 0xc7, 0x3e, - 0x55, 0x6a, 0x1f, 0x98, 0x53, 0x3a, 0x3d, 0x23, 0x47, 0x06, 0x98, 0x91, 0x2f, 0xc1, 0xc4, 0xad, - 0x38, 0x0c, 0x94, 0xc3, 0xb7, 0xd4, 0xd7, 0xe1, 0x6b, 0x60, 0xf5, 0x76, 0xf8, 0x8e, 0xe6, 0xe5, - 0xf0, 0x1d, 0xbb, 0x4f, 0x87, 0xef, 0xf7, 0x4a, 0xa0, 0xea, 0x3c, 0x5e, 0x23, 0xc9, 0xed, 0x30, - 0xda, 0xf5, 0x82, 0x06, 0x8b, 0x5b, 0xff, 0x8e, 0x05, 0x13, 0x7c, 0xbd, 0xac, 0x9a, 0x31, 0xac, - 0xf5, 0x9c, 0xea, 0x11, 0xa6, 0x98, 0xcd, 0x6d, 0x19, 0x8c, 0x32, 0xc5, 0xf8, 0x4d, 0x10, 0x4e, - 0xf5, 0x08, 0xfd, 0x3c, 0x80, 0x3c, 0x1f, 0xac, 0x4b, 0x91, 0xb9, 0x92, 0x4f, 0xff, 0x30, 0xa9, - 0x6b, 0xdd, 0x74, 0x4b, 0x31, 0xc1, 0x06, 0x43, 0xf4, 0xd5, 0xec, 0x6d, 0x72, 0x9f, 0x3b, 0x96, - 0xb1, 0x19, 0x24, 0xba, 0x17, 0xc3, 0x98, 0x17, 0x34, 0xe8, 0x3c, 0x11, 0x3e, 0xf2, 0x0f, 0xf7, - 0xca, 0xf9, 0x58, 0x0d, 0x9d, 0x5a, 0xd5, 0xf1, 0x9d, 0xc0, 0x25, 0xd1, 0x0a, 0x47, 0x37, 0x6f, - 0x87, 0x61, 0x0d, 0x58, 0x12, 0xea, 0x2a, 0xb8, 0x59, 0x1a, 0xa4, 0xe0, 0xe6, 0xd9, 0x4f, 0xc2, - 0x74, 0xd7, 0xc7, 0x1c, 0x2a, 0x98, 0xf7, 0xfe, 0xe3, 0x80, 0xed, 0x7f, 0x36, 0xaa, 0x37, 0xad, - 0x6b, 0x61, 0x8d, 0x97, 0x7d, 0x8c, 0xf4, 0x17, 0x15, 0xba, 0x67, 0x8e, 0x53, 0xc4, 0xb8, 0x61, - 0x46, 0x35, 0x62, 0x93, 0x25, 0x9d, 0xa3, 0x2d, 0x27, 0x22, 0xc1, 0x71, 0xcf, 0xd1, 0x0d, 0xc5, - 0x04, 0x1b, 0x0c, 0xd1, 0x4e, 0x2a, 0x9a, 0xef, 0xd2, 0xd1, 0xa3, 0xf9, 0x58, 0x4a, 0x69, 0xaf, - 0xba, 0x76, 0xdf, 0xb2, 0x60, 0x2a, 0x48, 0xcd, 0x5c, 0xe1, 0x2f, 0xd9, 0x3a, 0x8e, 0x55, 0xc1, - 0xcb, 0x04, 0xa7, 0xdb, 0x70, 0x86, 0x7f, 0xaf, 0x2d, 0xad, 0x34, 0xe4, 0x96, 0xa6, 0xeb, 0xc7, - 0x8e, 0xf6, 0xab, 0x1f, 0x8b, 0x02, 0x55, 0xf1, 0x7a, 0x2c, 0xf7, 0x8a, 0xd7, 0xd0, 0xa3, 0xda, - 0xf5, 0x4d, 0xa8, 0xb8, 0x11, 0x71, 0x92, 0xfb, 0x2c, 0x7e, 0xcc, 0x9c, 0xc5, 0x8b, 0x92, 0x00, - 0xd6, 0xb4, 0xec, 0x7f, 0x5b, 0x84, 0x93, 0x72, 0x44, 0x64, 0xa4, 0x13, 0xdd, 0x1f, 0x39, 0x5f, - 0xad, 0xdc, 0xaa, 0xfd, 0xf1, 0xb2, 0x04, 0x60, 0x8d, 0x43, 0xf5, 0xb1, 0x76, 0x4c, 0xd6, 0x5b, - 0x24, 0x58, 0xf5, 0xb6, 0x63, 0xe1, 0xe7, 0x53, 0x0b, 0xe5, 0xba, 0x06, 0x61, 0x13, 0x8f, 0x2a, - 0xe3, 0x5c, 0x2f, 0x8e, 0xb3, 0x81, 0x83, 0x42, 0xdf, 0xc6, 0x12, 0x8e, 0x7e, 0xad, 0x67, 0xd9, - 0xfc, 0x7c, 0x42, 0x66, 0xbb, 0x02, 0xbc, 0x86, 0xac, 0x97, 0xff, 0x8e, 0x05, 0x27, 0x76, 0x53, - 0x39, 0x3f, 0x52, 0x24, 0x1f, 0x31, 0x3b, 0x35, 0x9d, 0x48, 0xa4, 0xa7, 0x70, 0xba, 0x3d, 0xc6, - 0x59, 0xee, 0xf6, 0xff, 0xb0, 0xc0, 0x14, 0x4f, 0x83, 0x69, 0x56, 0xc6, 0x3d, 0x37, 0x85, 0x43, - 0xee, 0xb9, 0x91, 0x4a, 0x58, 0x71, 0x30, 0xa5, 0x7f, 0x64, 0x08, 0xa5, 0xbf, 0xd4, 0x57, 0x6b, - 0x7b, 0x1a, 0x8a, 0x6d, 0xaf, 0x26, 0xf4, 0x76, 0xed, 0xd5, 0x5b, 0x59, 0xc2, 0xb4, 0xdd, 0xfe, - 0xfd, 0x92, 0xb6, 0xd3, 0x45, 0xa4, 0xe7, 0x8f, 0xc5, 0x6b, 0xd7, 0x55, 0xb2, 0x31, 0x7f, 0xf3, - 0x6b, 0x5d, 0xc9, 0xc6, 0x3f, 0x35, 0x7c, 0x20, 0x2f, 0x1f, 0xa0, 0x7e, 0xb9, 0xc6, 0x63, 0x87, - 0x44, 0xf1, 0xde, 0x82, 0x32, 0x35, 0x6d, 0xd8, 0x81, 0x5b, 0x39, 0xd5, 0xa9, 0xf2, 0x65, 0xd1, - 0x7e, 0xf7, 0x60, 0xf6, 0x27, 0x87, 0xef, 0x96, 0x7c, 0x1a, 0x2b, 0xfa, 0x28, 0x86, 0x0a, 0xfd, - 0xcd, 0x02, 0x8e, 0x85, 0xd1, 0x74, 0x5d, 0xc9, 0x22, 0x09, 0xc8, 0x25, 0x9a, 0x59, 0xf3, 0x41, - 0x01, 0x54, 0xd8, 0x95, 0x1d, 0x8c, 0x29, 0xb7, 0xad, 0x36, 0x54, 0xd8, 0xaf, 0x04, 0xdc, 0x3d, - 0x98, 0x7d, 0x65, 0x78, 0xa6, 0xea, 0x71, 0xac, 0x59, 0xd8, 0xef, 0x8e, 0xe8, 0xb9, 0x2b, 0x72, - 0xcc, 0x7f, 0x2c, 0xe6, 0xee, 0xcb, 0x99, 0xb9, 0x7b, 0xbe, 0x6b, 0xee, 0x4e, 0xe9, 0xab, 0x25, - 0x52, 0xb3, 0xf1, 0x41, 0x6f, 0xb0, 0x87, 0xdb, 0xf1, 0x4c, 0xb3, 0x78, 0xab, 0xed, 0x45, 0x24, - 0xde, 0x88, 0xda, 0x81, 0x17, 0x34, 0xc4, 0xdd, 0x75, 0x86, 0x66, 0x91, 0x02, 0xe3, 0x2c, 0x3e, - 0xbb, 0xf7, 0xae, 0x13, 0xb8, 0x37, 0x9d, 0x3d, 0x3e, 0xab, 0x8c, 0xb4, 0xdb, 0x4d, 0xd1, 0x8e, - 0x15, 0x86, 0xfd, 0x5d, 0xe6, 0x6f, 0x35, 0x32, 0x1d, 0xe8, 0x9c, 0xf0, 0xd9, 0x1d, 0x29, 0x3c, - 0x67, 0x57, 0xcd, 0x09, 0x7e, 0x31, 0x0a, 0x87, 0xa1, 0xdb, 0x30, 0xb6, 0xcd, 0x6b, 0x8e, 0xe7, - 0x53, 0xe4, 0x4b, 0x14, 0x30, 0x67, 0x75, 0x38, 0x65, 0x35, 0xf3, 0xbb, 0xfa, 0x27, 0x96, 0xdc, - 0xec, 0xf7, 0x46, 0xe0, 0x44, 0xe6, 0x16, 0x8d, 0x54, 0xc9, 0x91, 0xc2, 0xa1, 0x25, 0x47, 0x3e, - 0x03, 0x50, 0x23, 0x2d, 0x3f, 0xec, 0x30, 0x35, 0x67, 0x64, 0x68, 0x35, 0x47, 0x69, 0xc6, 0x4b, - 0x8a, 0x0a, 0x36, 0x28, 0x8a, 0x44, 0x65, 0x5e, 0xc1, 0x24, 0x93, 0xa8, 0x6c, 0xd4, 0xd9, 0x1b, - 0x7d, 0xb0, 0x75, 0xf6, 0x3c, 0x38, 0xc1, 0xbb, 0xa8, 0xf2, 0x09, 0xee, 0x23, 0x6d, 0x80, 0x45, - 0xa2, 0x2e, 0xa5, 0xc9, 0xe0, 0x2c, 0xdd, 0x87, 0x79, 0x49, 0x0e, 0xfa, 0x28, 0x54, 0xe4, 0x77, - 0x8e, 0x67, 0x2a, 0x3a, 0x27, 0x4b, 0x4e, 0x03, 0x76, 0x79, 0x8d, 0xf8, 0x69, 0x7f, 0xb3, 0x40, - 0xb5, 0x52, 0xfe, 0x4f, 0xe5, 0xd6, 0x3e, 0x0b, 0xa3, 0x4e, 0x3b, 0xd9, 0x09, 0xbb, 0xaa, 0xbc, - 0x2f, 0xb0, 0x56, 0x2c, 0xa0, 0x68, 0x15, 0x46, 0x6a, 0x3a, 0x5f, 0x72, 0x98, 0x51, 0xd4, 0x07, - 0x7c, 0x4e, 0x42, 0x30, 0xa3, 0x82, 0x9e, 0x82, 0x91, 0xc4, 0x69, 0xa4, 0xee, 0x5f, 0xdc, 0x72, - 0x1a, 0x31, 0x66, 0xad, 0xe6, 0xa6, 0x39, 0x72, 0xc8, 0xa6, 0xf9, 0x0a, 0x4c, 0xc6, 0x5e, 0x23, - 0x70, 0x92, 0x76, 0x44, 0x0c, 0x67, 0x92, 0xf6, 0xe9, 0x9b, 0x40, 0x9c, 0xc6, 0xb5, 0xdf, 0xaf, - 0xc0, 0xe9, 0x5e, 0xf7, 0x64, 0xe7, 0x1d, 0x75, 0xde, 0x8b, 0xc7, 0x83, 0x8b, 0x3a, 0xef, 0xc3, - 0xdd, 0x37, 0xa2, 0xce, 0x7d, 0x23, 0xea, 0xfc, 0xab, 0x16, 0x54, 0x54, 0xb0, 0xb5, 0x08, 0x18, - 0x7d, 0xe3, 0x18, 0xee, 0x22, 0x97, 0x2c, 0x44, 0xcc, 0xad, 0xfc, 0x8b, 0x35, 0xf3, 0xe3, 0x0b, - 0x43, 0xbf, 0x67, 0x87, 0x86, 0x0a, 0x43, 0x57, 0x31, 0xfa, 0xa5, 0x3c, 0x62, 0xf4, 0xfb, 0x7c, - 0xaa, 0x9e, 0x31, 0xfa, 0xdf, 0xb2, 0x60, 0xdc, 0x79, 0xbb, 0x1d, 0x91, 0x25, 0xb2, 0xb7, 0xde, - 0x8a, 0x85, 0x80, 0x7d, 0x33, 0xff, 0x0e, 0x2c, 0x68, 0x26, 0xa2, 0x1c, 0xad, 0x6e, 0xc0, 0x66, - 0x17, 0x52, 0x31, 0xf9, 0x63, 0x79, 0xc4, 0xe4, 0xf7, 0xea, 0xce, 0xa1, 0x31, 0xf9, 0xaf, 0xc0, - 0xa4, 0xeb, 0x87, 0x01, 0xd9, 0x88, 0xc2, 0x24, 0x74, 0x43, 0x5f, 0x28, 0xd3, 0x4a, 0x24, 0x2c, - 0x9a, 0x40, 0x9c, 0xc6, 0xed, 0x17, 0xd0, 0x5f, 0x39, 0x6a, 0x40, 0x3f, 0x3c, 0xa4, 0x80, 0xfe, - 0xbf, 0x28, 0xc0, 0xec, 0x21, 0x1f, 0x15, 0xbd, 0x0c, 0x13, 0x61, 0xd4, 0x70, 0x02, 0xef, 0x6d, - 0x9e, 0x4f, 0x59, 0x4a, 0x97, 0xbb, 0x58, 0x37, 0x60, 0x38, 0x85, 0x29, 0x43, 0x7e, 0x47, 0xfb, - 0x84, 0xfc, 0x7e, 0x1c, 0xc6, 0x13, 0xe2, 0x34, 0x45, 0xac, 0x84, 0x30, 0x80, 0xb4, 0x43, 0x49, - 0x83, 0xb0, 0x89, 0x47, 0xa7, 0xd1, 0x94, 0xe3, 0xba, 0x24, 0x8e, 0x65, 0x4c, 0xaf, 0x38, 0x9c, - 0xc9, 0x2d, 0x60, 0x98, 0x9d, 0x79, 0x2d, 0xa4, 0x58, 0xe0, 0x0c, 0x4b, 0xda, 0x79, 0xc7, 0xf7, - 0x79, 0xf8, 0x3e, 0x91, 0x37, 0x2a, 0xeb, 0xea, 0x0b, 0x1a, 0x84, 0x4d, 0x3c, 0xfb, 0xb7, 0x0a, - 0xf0, 0xf4, 0x3d, 0xc5, 0xcb, 0xc0, 0xe1, 0xd6, 0xed, 0x98, 0x44, 0x59, 0x87, 0xcc, 0xf5, 0x98, - 0x44, 0x98, 0x41, 0xf8, 0x28, 0xb5, 0x5a, 0xc6, 0x55, 0x2e, 0x79, 0x47, 0xf7, 0xf3, 0x51, 0x4a, - 0xb1, 0xc0, 0x19, 0x96, 0xd9, 0x51, 0x1a, 0x19, 0x70, 0x94, 0xfe, 0x5e, 0x01, 0x9e, 0x19, 0x40, - 0x08, 0xe7, 0x98, 0x05, 0x91, 0xce, 0x22, 0x29, 0x3e, 0x9c, 0x2c, 0x92, 0xfb, 0x1d, 0xae, 0xef, - 0x16, 0xe0, 0x6c, 0x7f, 0x59, 0x88, 0x7e, 0x9a, 0x1a, 0x51, 0x32, 0xd8, 0xc2, 0xcc, 0x40, 0x39, - 0xc5, 0x0d, 0xa8, 0x14, 0x08, 0x67, 0x71, 0xd1, 0x1c, 0x40, 0xcb, 0x49, 0x76, 0xe2, 0x8b, 0xfb, - 0x5e, 0x9c, 0x88, 0xdc, 0xc9, 0x29, 0x7e, 0x14, 0x2e, 0x5b, 0xb1, 0x81, 0x41, 0xd9, 0xb1, 0x7f, - 0x4b, 0xe1, 0xb5, 0x30, 0xe1, 0x0f, 0x71, 0x3d, 0xee, 0x94, 0xac, 0xf7, 0x67, 0x80, 0x70, 0x16, - 0x97, 0xb2, 0x63, 0xce, 0x16, 0xde, 0x51, 0x71, 0x5d, 0x3c, 0x65, 0xb7, 0xaa, 0x5a, 0xb1, 0x81, - 0x91, 0xcd, 0xad, 0x29, 0x0d, 0x90, 0x5b, 0xf3, 0x8f, 0x0a, 0xf0, 0x44, 0xdf, 0xbd, 0x74, 0xb0, - 0x05, 0xf8, 0xe8, 0x25, 0xd5, 0xdc, 0xdf, 0xdc, 0x19, 0x32, 0x55, 0xe4, 0xcf, 0xfa, 0xcc, 0x34, - 0x91, 0x2a, 0x92, 0xdd, 0x2a, 0xac, 0x61, 0xb7, 0x8a, 0x47, 0x68, 0x3c, 0xbb, 0xb2, 0x43, 0x46, - 0x86, 0xc8, 0x0e, 0xc9, 0x7c, 0x8c, 0xd2, 0x80, 0x0b, 0xf9, 0xfb, 0xfd, 0x87, 0x97, 0xea, 0xde, - 0x03, 0x1d, 0x4f, 0x2d, 0xc1, 0x49, 0x2f, 0x60, 0xb5, 0x5f, 0x37, 0xdb, 0xdb, 0x22, 0xb3, 0xb6, - 0x90, 0xbe, 0xd6, 0x68, 0x25, 0x03, 0xc7, 0x5d, 0x4f, 0x3c, 0x82, 0xd9, 0x3a, 0xf7, 0x39, 0xa4, - 0x9f, 0x81, 0x8a, 0xa2, 0xcd, 0x23, 0x23, 0xd5, 0x07, 0xed, 0x8a, 0x8c, 0x54, 0x5f, 0xd3, 0xc0, - 0xa2, 0x23, 0xb1, 0x4b, 0x3a, 0xd9, 0x99, 0x79, 0x95, 0x74, 0x98, 0x97, 0xd4, 0xfe, 0x18, 0x4c, - 0x28, 0x23, 0x72, 0xd0, 0xda, 0xa4, 0xf6, 0xbb, 0xa3, 0x30, 0x99, 0xaa, 0xa0, 0x90, 0x3a, 0xb3, - 0xb1, 0x0e, 0x3d, 0xb3, 0x61, 0xd1, 0xa9, 0xed, 0x40, 0x56, 0xff, 0x35, 0xa2, 0x53, 0xdb, 0x01, - 0xc1, 0x1c, 0x46, 0x4d, 0xf7, 0x5a, 0xd4, 0xc1, 0xed, 0x40, 0x44, 0xa4, 0x29, 0xd3, 0x7d, 0x89, - 0xb5, 0x62, 0x01, 0x45, 0x5f, 0xb4, 0x60, 0x22, 0x66, 0x07, 0x82, 0xfc, 0xc4, 0x4b, 0x7c, 0xd0, - 0x2b, 0x79, 0xdc, 0x5e, 0x2b, 0xaa, 0x85, 0x30, 0x67, 0xb6, 0xd9, 0x82, 0x53, 0x1c, 0xd1, 0x57, - 0x2c, 0xf3, 0xde, 0xde, 0xd1, 0x3c, 0x22, 0x29, 0xb3, 0x05, 0x2a, 0xf8, 0x51, 0xc9, 0xbd, 0xaf, - 0xef, 0xd5, 0xd7, 0x79, 0x8f, 0x3d, 0xb8, 0xeb, 0xbc, 0x3f, 0x0a, 0x95, 0xa6, 0x13, 0x78, 0x75, - 0x12, 0x27, 0xfc, 0x84, 0x48, 0xd6, 0xcd, 0x91, 0x8d, 0x58, 0xc3, 0xe9, 0x66, 0x17, 0xb3, 0x17, - 0x4b, 0x8c, 0x23, 0x1d, 0xb6, 0xd9, 0x6d, 0xea, 0x66, 0x6c, 0xe2, 0x98, 0xe7, 0x4f, 0xf0, 0x50, - 0xcf, 0x9f, 0xc6, 0x0f, 0x39, 0x7f, 0xfa, 0x07, 0x16, 0x9c, 0xe9, 0xf9, 0xd5, 0x1e, 0xdd, 0x18, - 0x25, 0xfb, 0xfd, 0x22, 0x9c, 0xea, 0x51, 0x0a, 0x05, 0x75, 0x8e, 0xed, 0x1e, 0x6a, 0x51, 0x6b, - 0x65, 0xb2, 0xef, 0x24, 0x1e, 0xee, 0xf4, 0x57, 0x9f, 0xc0, 0x16, 0x1f, 0xec, 0x09, 0xac, 0x31, - 0x2d, 0x47, 0x1e, 0xea, 0xb4, 0x2c, 0x1d, 0x32, 0x2d, 0xdf, 0x2f, 0x82, 0x71, 0xad, 0x3c, 0xfa, - 0x82, 0x59, 0x9e, 0xc8, 0xca, 0xab, 0x94, 0x0e, 0x27, 0xae, 0xca, 0x1b, 0xf1, 0xee, 0xf4, 0xaa, - 0x76, 0x94, 0x95, 0x00, 0x85, 0x01, 0x24, 0x80, 0x2f, 0xeb, 0x40, 0x15, 0xf3, 0xaf, 0x03, 0x55, - 0xc9, 0xd6, 0x80, 0x42, 0xbf, 0x67, 0xc1, 0x4c, 0xb3, 0x4f, 0xbd, 0xc2, 0x7c, 0xd2, 0xf3, 0xfb, - 0x55, 0x43, 0xac, 0x3e, 0x75, 0xe7, 0x60, 0xb6, 0x6f, 0x99, 0x48, 0xdc, 0xb7, 0x57, 0xf6, 0xdf, - 0xb4, 0xf8, 0x2a, 0xce, 0x7c, 0x05, 0xbd, 0xcd, 0x5a, 0xf7, 0xd8, 0x66, 0x9f, 0x67, 0x37, 0x9d, - 0xd5, 0x2f, 0x13, 0xc7, 0x17, 0xdb, 0xb1, 0x79, 0x69, 0x19, 0x6b, 0xc7, 0x0a, 0x83, 0xdd, 0x4d, - 0xe0, 0xfb, 0xe1, 0xed, 0x8b, 0xcd, 0x56, 0xd2, 0x11, 0x1b, 0xb3, 0xbe, 0x9b, 0x40, 0x41, 0xb0, - 0x81, 0x65, 0xff, 0x46, 0x81, 0xcf, 0x40, 0xe1, 0xa4, 0x7c, 0x39, 0x53, 0x08, 0x7b, 0x70, 0xff, - 0xde, 0xe7, 0x01, 0x5c, 0x75, 0xc9, 0x51, 0x3e, 0x97, 0xd4, 0xeb, 0x4b, 0x93, 0xcc, 0x9b, 0xd3, - 0x65, 0x1b, 0x36, 0xf8, 0xa5, 0x04, 0x53, 0xf1, 0x50, 0xc1, 0x94, 0x5a, 0xa3, 0x23, 0x87, 0xac, - 0xd1, 0xbf, 0xb0, 0x20, 0xa5, 0x5e, 0xa0, 0x16, 0x94, 0x68, 0x77, 0x3b, 0xf9, 0xdc, 0xdf, 0x64, - 0x92, 0xa6, 0x72, 0x46, 0x4c, 0x7b, 0xf6, 0x13, 0x73, 0x46, 0xc8, 0x17, 0xbe, 0xcc, 0x42, 0x1e, - 0x77, 0x8c, 0x99, 0x0c, 0x2f, 0x87, 0xe1, 0x2e, 0x77, 0x81, 0x68, 0xbf, 0xa8, 0xfd, 0x32, 0x4c, - 0x77, 0x75, 0x8a, 0xd5, 0xbc, 0x0d, 0xe5, 0xa5, 0x55, 0xc6, 0x74, 0x65, 0x15, 0xb8, 0x31, 0x87, - 0xd9, 0xdf, 0xb5, 0xe0, 0x64, 0x96, 0x3c, 0xfa, 0xb6, 0x05, 0xd3, 0x71, 0x96, 0xde, 0x71, 0x8d, - 0x9d, 0x8a, 0xf3, 0xe9, 0x02, 0xe1, 0xee, 0x4e, 0xd8, 0xff, 0x57, 0x4c, 0xfe, 0x9b, 0x5e, 0x50, - 0x0b, 0x6f, 0xab, 0x5d, 0xde, 0xea, 0xbb, 0xcb, 0xd3, 0xf5, 0xe8, 0xee, 0x90, 0x5a, 0xdb, 0xef, - 0xca, 0x64, 0xda, 0x14, 0xed, 0x58, 0x61, 0xa4, 0x6e, 0x88, 0x2e, 0x1e, 0x7a, 0x43, 0xf4, 0x4b, - 0x30, 0x61, 0x5e, 0xcc, 0x26, 0xe6, 0x25, 0xd3, 0x6e, 0xcd, 0x3b, 0xdc, 0x70, 0x0a, 0x2b, 0x73, - 0x35, 0x6f, 0xe9, 0xd0, 0xab, 0x79, 0x9f, 0x83, 0xb2, 0xb8, 0x66, 0x56, 0x46, 0xc3, 0xf1, 0x34, - 0x29, 0xd1, 0x86, 0x15, 0x94, 0x4a, 0x93, 0xa6, 0x13, 0xb4, 0x1d, 0x9f, 0x8e, 0x90, 0xc8, 0xc7, - 0x54, 0xcb, 0x70, 0x4d, 0x41, 0xb0, 0x81, 0x45, 0xdf, 0x38, 0xf1, 0x9a, 0xe4, 0xf5, 0x30, 0x90, - 0x71, 0x24, 0xfa, 0x80, 0x58, 0xb4, 0x63, 0x85, 0x61, 0xff, 0x17, 0x0b, 0xb2, 0x77, 0x64, 0xa6, - 0x8e, 0x0c, 0xac, 0x43, 0x73, 0x40, 0xd3, 0xd9, 0x68, 0x85, 0x81, 0xb2, 0xd1, 0xcc, 0x44, 0xb1, - 0xe2, 0x3d, 0x13, 0xc5, 0x7e, 0x42, 0xdf, 0x9c, 0xc0, 0x33, 0xca, 0xc6, 0x7b, 0xdd, 0x9a, 0x80, - 0x6c, 0x18, 0x75, 0x1d, 0x95, 0xd9, 0x3f, 0xc1, 0x15, 0xf1, 0xc5, 0x05, 0x86, 0x24, 0x20, 0xd5, - 0xed, 0xf7, 0x7e, 0x78, 0xee, 0x43, 0xdf, 0xff, 0xe1, 0xb9, 0x0f, 0xfd, 0xc9, 0x0f, 0xcf, 0x7d, - 0xe8, 0x8b, 0x77, 0xce, 0x59, 0xef, 0xdd, 0x39, 0x67, 0x7d, 0xff, 0xce, 0x39, 0xeb, 0x4f, 0xee, - 0x9c, 0xb3, 0xde, 0xbf, 0x73, 0xce, 0xfa, 0xd6, 0x7f, 0x3c, 0xf7, 0xa1, 0xd7, 0x7b, 0xc6, 0xfd, - 0xd0, 0x1f, 0x2f, 0xb8, 0xb5, 0xf9, 0xbd, 0x0b, 0x2c, 0xf4, 0x84, 0xae, 0x86, 0x79, 0x63, 0x0a, - 0xcc, 0xcb, 0xd5, 0xf0, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x38, 0xc0, 0xfa, 0xc5, 0xd3, 0xc1, - 0x00, 0x00, + // 11030 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x6d, 0x70, 0x1c, 0xc9, + 0x75, 0x98, 0x66, 0x17, 0x0b, 0xec, 0x3e, 0x7c, 0x90, 0x6c, 0x92, 0x77, 0x20, 0x75, 0x77, 0xa0, + 0xe7, 0xe2, 0xd3, 0x39, 0xba, 0x03, 0x7c, 0xf4, 0x9d, 0x7c, 0xf1, 0xd9, 0x92, 0xb1, 0x00, 0x09, + 0x82, 0x04, 0x08, 0x5c, 0x03, 0x24, 0xa5, 0x93, 0x4f, 0xa7, 0xc1, 0x6e, 0x63, 0x31, 0xc4, 0xec, + 0xcc, 0xdc, 0xcc, 0x2c, 0x08, 0x9c, 0x25, 0x59, 0xb2, 0x64, 0x5b, 0x89, 0x3e, 0x4e, 0x91, 0x92, + 0xf2, 0x39, 0xb1, 0x14, 0xd9, 0x72, 0x52, 0x71, 0x25, 0xaa, 0x38, 0xc9, 0x8f, 0x38, 0x71, 0x52, + 0x2e, 0xdb, 0xa9, 0x94, 0x52, 0x4a, 0xca, 0x2e, 0x97, 0xcb, 0x72, 0x12, 0x1b, 0x91, 0x98, 0x4a, + 0x25, 0x95, 0xaa, 0xb8, 0xca, 0x89, 0x7f, 0x24, 0x4c, 0x7e, 0xa4, 0xfa, 0xbb, 0x67, 0x76, 0x16, + 0x58, 0x00, 0x03, 0x92, 0x52, 0xee, 0xdf, 0x6e, 0xbf, 0x37, 0xef, 0xf5, 0xf4, 0x74, 0xbf, 0xf7, + 0xfa, 0xf5, 0x7b, 0xaf, 0x61, 0xa1, 0xe5, 0x26, 0x1b, 0x9d, 0xb5, 0xc9, 0x46, 0xd0, 0x9e, 0x72, + 0xa2, 0x56, 0x10, 0x46, 0xc1, 0x6d, 0xf6, 0xe3, 0xd9, 0x46, 0x73, 0x6a, 0xeb, 0xe2, 0x54, 0xb8, + 0xd9, 0x9a, 0x72, 0x42, 0x37, 0x9e, 0x72, 0xc2, 0xd0, 0x73, 0x1b, 0x4e, 0xe2, 0x06, 0xfe, 0xd4, + 0xd6, 0x73, 0x8e, 0x17, 0x6e, 0x38, 0xcf, 0x4d, 0xb5, 0x88, 0x4f, 0x22, 0x27, 0x21, 0xcd, 0xc9, + 0x30, 0x0a, 0x92, 0x00, 0xfd, 0xa8, 0xa6, 0x36, 0x29, 0xa9, 0xb1, 0x1f, 0xaf, 0x35, 0x9a, 0x93, + 0x5b, 0x17, 0x27, 0xc3, 0xcd, 0xd6, 0x24, 0xa5, 0x36, 0x69, 0x50, 0x9b, 0x94, 0xd4, 0xce, 0x3f, + 0x6b, 0xf4, 0xa5, 0x15, 0xb4, 0x82, 0x29, 0x46, 0x74, 0xad, 0xb3, 0xce, 0xfe, 0xb1, 0x3f, 0xec, + 0x17, 0x67, 0x76, 0xde, 0xde, 0x7c, 0x31, 0x9e, 0x74, 0x03, 0xda, 0xbd, 0xa9, 0x46, 0x10, 0x91, + 0xa9, 0xad, 0xae, 0x0e, 0x9d, 0xbf, 0xa2, 0x71, 0xc8, 0x76, 0x42, 0xfc, 0xd8, 0x0d, 0xfc, 0xf8, + 0x59, 0xda, 0x05, 0x12, 0x6d, 0x91, 0xc8, 0x7c, 0x3d, 0x03, 0x21, 0x8f, 0xd2, 0xf3, 0x9a, 0x52, + 0xdb, 0x69, 0x6c, 0xb8, 0x3e, 0x89, 0x76, 0xf4, 0xe3, 0x6d, 0x92, 0x38, 0x79, 0x4f, 0x4d, 0xf5, + 0x7a, 0x2a, 0xea, 0xf8, 0x89, 0xdb, 0x26, 0x5d, 0x0f, 0xbc, 0x67, 0xbf, 0x07, 0xe2, 0xc6, 0x06, + 0x69, 0x3b, 0x5d, 0xcf, 0xfd, 0x50, 0xaf, 0xe7, 0x3a, 0x89, 0xeb, 0x4d, 0xb9, 0x7e, 0x12, 0x27, + 0x51, 0xf6, 0x21, 0xfb, 0x17, 0x2d, 0x18, 0x9d, 0xbe, 0xb5, 0x32, 0xdd, 0x49, 0x36, 0x66, 0x02, + 0x7f, 0xdd, 0x6d, 0xa1, 0x17, 0x60, 0xb8, 0xe1, 0x75, 0xe2, 0x84, 0x44, 0xd7, 0x9d, 0x36, 0x19, + 0xb7, 0x2e, 0x58, 0x4f, 0xd7, 0xea, 0xa7, 0xbf, 0xb1, 0x3b, 0xf1, 0x8e, 0xbb, 0xbb, 0x13, 0xc3, + 0x33, 0x1a, 0x84, 0x4d, 0x3c, 0xf4, 0x03, 0x30, 0x14, 0x05, 0x1e, 0x99, 0xc6, 0xd7, 0xc7, 0x4b, + 0xec, 0x91, 0x13, 0xe2, 0x91, 0x21, 0xcc, 0x9b, 0xb1, 0x84, 0x53, 0xd4, 0x30, 0x0a, 0xd6, 0x5d, + 0x8f, 0x8c, 0x97, 0xd3, 0xa8, 0xcb, 0xbc, 0x19, 0x4b, 0xb8, 0xfd, 0x87, 0x25, 0x80, 0xe9, 0x30, + 0x5c, 0x8e, 0x82, 0xdb, 0xa4, 0x91, 0xa0, 0x0f, 0x43, 0x95, 0x0e, 0x73, 0xd3, 0x49, 0x1c, 0xd6, + 0xb1, 0xe1, 0x8b, 0x3f, 0x38, 0xc9, 0xdf, 0x7a, 0xd2, 0x7c, 0x6b, 0x3d, 0xc9, 0x28, 0xf6, 0xe4, + 0xd6, 0x73, 0x93, 0x4b, 0x6b, 0xf4, 0xf9, 0x45, 0x92, 0x38, 0x75, 0x24, 0x98, 0x81, 0x6e, 0xc3, + 0x8a, 0x2a, 0xf2, 0x61, 0x20, 0x0e, 0x49, 0x83, 0xbd, 0xc3, 0xf0, 0xc5, 0x85, 0xc9, 0xa3, 0xcc, + 0xe6, 0x49, 0xdd, 0xf3, 0x95, 0x90, 0x34, 0xea, 0x23, 0x82, 0xf3, 0x00, 0xfd, 0x87, 0x19, 0x1f, + 0xb4, 0x05, 0x83, 0x71, 0xe2, 0x24, 0x9d, 0x98, 0x0d, 0xc5, 0xf0, 0xc5, 0xeb, 0x85, 0x71, 0x64, + 0x54, 0xeb, 0x63, 0x82, 0xe7, 0x20, 0xff, 0x8f, 0x05, 0x37, 0xfb, 0x4f, 0x2c, 0x18, 0xd3, 0xc8, + 0x0b, 0x6e, 0x9c, 0xa0, 0x9f, 0xe8, 0x1a, 0xdc, 0xc9, 0xfe, 0x06, 0x97, 0x3e, 0xcd, 0x86, 0xf6, + 0xa4, 0x60, 0x56, 0x95, 0x2d, 0xc6, 0xc0, 0xb6, 0xa1, 0xe2, 0x26, 0xa4, 0x1d, 0x8f, 0x97, 0x2e, + 0x94, 0x9f, 0x1e, 0xbe, 0x78, 0xa5, 0xa8, 0xf7, 0xac, 0x8f, 0x0a, 0xa6, 0x95, 0x79, 0x4a, 0x1e, + 0x73, 0x2e, 0xf6, 0xaf, 0x8e, 0x98, 0xef, 0x47, 0x07, 0x1c, 0x3d, 0x07, 0xc3, 0x71, 0xd0, 0x89, + 0x1a, 0x04, 0x93, 0x30, 0x88, 0xc7, 0xad, 0x0b, 0x65, 0x3a, 0xf5, 0xe8, 0xa4, 0x5e, 0xd1, 0xcd, + 0xd8, 0xc4, 0x41, 0x9f, 0xb7, 0x60, 0xa4, 0x49, 0xe2, 0xc4, 0xf5, 0x19, 0x7f, 0xd9, 0xf9, 0xd5, + 0x23, 0x77, 0x5e, 0x36, 0xce, 0x6a, 0xe2, 0xf5, 0x33, 0xe2, 0x45, 0x46, 0x8c, 0xc6, 0x18, 0xa7, + 0xf8, 0xd3, 0xc5, 0xd9, 0x24, 0x71, 0x23, 0x72, 0x43, 0xfa, 0x5f, 0x2c, 0x1f, 0xb5, 0x38, 0x67, + 0x35, 0x08, 0x9b, 0x78, 0xc8, 0x87, 0x0a, 0x5d, 0x7c, 0xf1, 0xf8, 0x00, 0xeb, 0xff, 0xfc, 0xd1, + 0xfa, 0x2f, 0x06, 0x95, 0xae, 0x6b, 0x3d, 0xfa, 0xf4, 0x5f, 0x8c, 0x39, 0x1b, 0xf4, 0x39, 0x0b, + 0xc6, 0x85, 0x70, 0xc0, 0x84, 0x0f, 0xe8, 0xad, 0x0d, 0x37, 0x21, 0x9e, 0x1b, 0x27, 0xe3, 0x15, + 0xd6, 0x87, 0xa9, 0xfe, 0xe6, 0xd6, 0x5c, 0x14, 0x74, 0xc2, 0x6b, 0xae, 0xdf, 0xac, 0x5f, 0x10, + 0x9c, 0xc6, 0x67, 0x7a, 0x10, 0xc6, 0x3d, 0x59, 0xa2, 0x2f, 0x59, 0x70, 0xde, 0x77, 0xda, 0x24, + 0x0e, 0x1d, 0xfa, 0x69, 0x39, 0xb8, 0xee, 0x39, 0x8d, 0x4d, 0xd6, 0xa3, 0xc1, 0xc3, 0xf5, 0xc8, + 0x16, 0x3d, 0x3a, 0x7f, 0xbd, 0x27, 0x69, 0xbc, 0x07, 0x5b, 0xf4, 0x35, 0x0b, 0x4e, 0x05, 0x51, + 0xb8, 0xe1, 0xf8, 0xa4, 0x29, 0xa1, 0xf1, 0xf8, 0x10, 0x5b, 0x7a, 0x1f, 0x3a, 0xda, 0x27, 0x5a, + 0xca, 0x92, 0x5d, 0x0c, 0x7c, 0x37, 0x09, 0xa2, 0x15, 0x92, 0x24, 0xae, 0xdf, 0x8a, 0xeb, 0x67, + 0xef, 0xee, 0x4e, 0x9c, 0xea, 0xc2, 0xc2, 0xdd, 0xfd, 0x41, 0x3f, 0x09, 0xc3, 0xf1, 0x8e, 0xdf, + 0xb8, 0xe5, 0xfa, 0xcd, 0xe0, 0x4e, 0x3c, 0x5e, 0x2d, 0x62, 0xf9, 0xae, 0x28, 0x82, 0x62, 0x01, + 0x6a, 0x06, 0xd8, 0xe4, 0x96, 0xff, 0xe1, 0xf4, 0x54, 0xaa, 0x15, 0xfd, 0xe1, 0xf4, 0x64, 0xda, + 0x83, 0x2d, 0xfa, 0x39, 0x0b, 0x46, 0x63, 0xb7, 0xe5, 0x3b, 0x49, 0x27, 0x22, 0xd7, 0xc8, 0x4e, + 0x3c, 0x0e, 0xac, 0x23, 0x57, 0x8f, 0x38, 0x2a, 0x06, 0xc9, 0xfa, 0x59, 0xd1, 0xc7, 0x51, 0xb3, + 0x35, 0xc6, 0x69, 0xbe, 0x79, 0x0b, 0x4d, 0x4f, 0xeb, 0xe1, 0x62, 0x17, 0x9a, 0x9e, 0xd4, 0x3d, + 0x59, 0xa2, 0x1f, 0x87, 0x93, 0xbc, 0x49, 0x8d, 0x6c, 0x3c, 0x3e, 0xc2, 0x04, 0xed, 0x99, 0xbb, + 0xbb, 0x13, 0x27, 0x57, 0x32, 0x30, 0xdc, 0x85, 0x8d, 0x5e, 0x87, 0x89, 0x90, 0x44, 0x6d, 0x37, + 0x59, 0xf2, 0xbd, 0x1d, 0x29, 0xbe, 0x1b, 0x41, 0x48, 0x9a, 0xa2, 0x3b, 0xf1, 0xf8, 0xe8, 0x05, + 0xeb, 0xe9, 0x6a, 0xfd, 0x5d, 0xa2, 0x9b, 0x13, 0xcb, 0x7b, 0xa3, 0xe3, 0xfd, 0xe8, 0xd9, 0xff, + 0xba, 0x04, 0x27, 0xb3, 0x8a, 0x13, 0xfd, 0x1d, 0x0b, 0x4e, 0xdc, 0xbe, 0x93, 0xac, 0x06, 0x9b, + 0xc4, 0x8f, 0xeb, 0x3b, 0x54, 0xbc, 0x31, 0x95, 0x31, 0x7c, 0xb1, 0x51, 0xac, 0x8a, 0x9e, 0xbc, + 0x9a, 0xe6, 0x72, 0xc9, 0x4f, 0xa2, 0x9d, 0xfa, 0xa3, 0xe2, 0xed, 0x4e, 0x5c, 0xbd, 0xb5, 0x6a, + 0x42, 0x71, 0xb6, 0x53, 0xe7, 0x3f, 0x63, 0xc1, 0x99, 0x3c, 0x12, 0xe8, 0x24, 0x94, 0x37, 0xc9, + 0x0e, 0x37, 0xe0, 0x30, 0xfd, 0x89, 0x5e, 0x85, 0xca, 0x96, 0xe3, 0x75, 0x88, 0xb0, 0x6e, 0xe6, + 0x8e, 0xf6, 0x22, 0xaa, 0x67, 0x98, 0x53, 0xfd, 0x91, 0xd2, 0x8b, 0x96, 0xfd, 0xbb, 0x65, 0x18, + 0x36, 0xf4, 0xdb, 0x7d, 0xb0, 0xd8, 0x82, 0x94, 0xc5, 0xb6, 0x58, 0x98, 0x6a, 0xee, 0x69, 0xb2, + 0xdd, 0xc9, 0x98, 0x6c, 0x4b, 0xc5, 0xb1, 0xdc, 0xd3, 0x66, 0x43, 0x09, 0xd4, 0x82, 0x90, 0x5a, + 0xef, 0x54, 0xf5, 0x0f, 0x14, 0xf1, 0x09, 0x97, 0x24, 0xb9, 0xfa, 0xe8, 0xdd, 0xdd, 0x89, 0x9a, + 0xfa, 0x8b, 0x35, 0x23, 0xfb, 0x5b, 0x16, 0x9c, 0x31, 0xfa, 0x38, 0x13, 0xf8, 0x4d, 0x97, 0x7d, + 0xda, 0x0b, 0x30, 0x90, 0xec, 0x84, 0x72, 0x87, 0xa0, 0x46, 0x6a, 0x75, 0x27, 0x24, 0x98, 0x41, + 0xa8, 0xa1, 0xdf, 0x26, 0x71, 0xec, 0xb4, 0x48, 0x76, 0x4f, 0xb0, 0xc8, 0x9b, 0xb1, 0x84, 0xa3, + 0x08, 0x90, 0xe7, 0xc4, 0xc9, 0x6a, 0xe4, 0xf8, 0x31, 0x23, 0xbf, 0xea, 0xb6, 0x89, 0x18, 0xe0, + 0xbf, 0xd8, 0xdf, 0x8c, 0xa1, 0x4f, 0xd4, 0x1f, 0xb9, 0xbb, 0x3b, 0x81, 0x16, 0xba, 0x28, 0xe1, + 0x1c, 0xea, 0xf6, 0x97, 0x2c, 0x78, 0x24, 0xdf, 0x16, 0x43, 0x4f, 0xc1, 0x20, 0xdf, 0x1e, 0x8a, + 0xb7, 0xd3, 0x9f, 0x84, 0xb5, 0x62, 0x01, 0x45, 0x53, 0x50, 0x53, 0x7a, 0x42, 0xbc, 0xe3, 0x29, + 0x81, 0x5a, 0xd3, 0xca, 0x45, 0xe3, 0xd0, 0x41, 0xa3, 0x7f, 0x84, 0xe5, 0xa6, 0x06, 0x8d, 0xed, + 0xa7, 0x18, 0xc4, 0xfe, 0x8f, 0x16, 0x9c, 0x30, 0x7a, 0x75, 0x1f, 0x4c, 0x73, 0x3f, 0x6d, 0x9a, + 0xcf, 0x17, 0x36, 0x9f, 0x7b, 0xd8, 0xe6, 0x9f, 0xb3, 0xe0, 0xbc, 0x81, 0xb5, 0xe8, 0x24, 0x8d, + 0x8d, 0x4b, 0xdb, 0x61, 0x44, 0x62, 0xba, 0xf5, 0x46, 0x8f, 0x1b, 0x72, 0xab, 0x3e, 0x2c, 0x28, + 0x94, 0xaf, 0x91, 0x1d, 0x2e, 0xc4, 0x9e, 0x81, 0x2a, 0x9f, 0x9c, 0x41, 0x24, 0x46, 0x5c, 0xbd, + 0xdb, 0x92, 0x68, 0xc7, 0x0a, 0x03, 0xd9, 0x30, 0xc8, 0x84, 0x13, 0x5d, 0xac, 0x54, 0x0d, 0x01, + 0xfd, 0x88, 0x37, 0x59, 0x0b, 0x16, 0x10, 0x3b, 0x4e, 0x75, 0x67, 0x39, 0x22, 0xec, 0xe3, 0x36, + 0x2f, 0xbb, 0xc4, 0x6b, 0xc6, 0x74, 0xdb, 0xe0, 0xf8, 0x7e, 0x90, 0x88, 0x1d, 0x80, 0xb1, 0x6d, + 0x98, 0xd6, 0xcd, 0xd8, 0xc4, 0xa1, 0x4c, 0x3d, 0x67, 0x8d, 0x78, 0x7c, 0x44, 0x05, 0xd3, 0x05, + 0xd6, 0x82, 0x05, 0xc4, 0xbe, 0x5b, 0x62, 0x1b, 0x14, 0xb5, 0xf4, 0xc9, 0xfd, 0xd8, 0xdd, 0x46, + 0x29, 0x59, 0xb9, 0x5c, 0x9c, 0xe0, 0x22, 0xbd, 0x77, 0xb8, 0x6f, 0x64, 0xc4, 0x25, 0x2e, 0x94, + 0xeb, 0xde, 0xbb, 0xdc, 0xdf, 0x2a, 0xc1, 0x44, 0xfa, 0x81, 0x2e, 0x69, 0x4b, 0xb7, 0x54, 0x06, + 0xa3, 0xac, 0xbf, 0xc3, 0xc0, 0xc7, 0x26, 0x5e, 0x0f, 0x81, 0x55, 0x3a, 0x4e, 0x81, 0x65, 0xca, + 0xd3, 0xf2, 0x3e, 0xf2, 0xf4, 0x29, 0x35, 0xea, 0x03, 0x19, 0x01, 0x96, 0xd6, 0x29, 0x17, 0x60, + 0x20, 0x4e, 0x48, 0x38, 0x5e, 0x49, 0xcb, 0xa3, 0x95, 0x84, 0x84, 0x98, 0x41, 0xec, 0xff, 0x56, + 0x82, 0x47, 0xd3, 0x63, 0xa8, 0x55, 0xc0, 0xfb, 0x52, 0x2a, 0xe0, 0xdd, 0xa6, 0x0a, 0xb8, 0xb7, + 0x3b, 0xf1, 0xce, 0x1e, 0x8f, 0x7d, 0xd7, 0x68, 0x08, 0x34, 0x97, 0x19, 0xc5, 0xa9, 0xf4, 0x28, + 0xde, 0xdb, 0x9d, 0x78, 0xbc, 0xc7, 0x3b, 0x66, 0x86, 0xf9, 0x29, 0x18, 0x8c, 0x88, 0x13, 0x07, + 0xbe, 0x18, 0x68, 0xf5, 0x39, 0x30, 0x6b, 0xc5, 0x02, 0x6a, 0xff, 0x7e, 0x2d, 0x3b, 0xd8, 0x73, + 0xdc, 0x61, 0x17, 0x44, 0xc8, 0x85, 0x01, 0x66, 0xd6, 0x73, 0xd1, 0x70, 0xed, 0x68, 0xcb, 0x88, + 0xaa, 0x01, 0x45, 0xba, 0x5e, 0xa5, 0x5f, 0x8d, 0x36, 0x61, 0xc6, 0x02, 0x6d, 0x43, 0xb5, 0x21, + 0xad, 0xed, 0x52, 0x11, 0x7e, 0x29, 0x61, 0x6b, 0x6b, 0x8e, 0x23, 0x54, 0x5e, 0x2b, 0x13, 0x5d, + 0x71, 0x43, 0x04, 0xca, 0x2d, 0x37, 0x11, 0x9f, 0xf5, 0x88, 0xfb, 0xa9, 0x39, 0xd7, 0x78, 0xc5, + 0x21, 0xaa, 0x44, 0xe6, 0xdc, 0x04, 0x53, 0xfa, 0xe8, 0x67, 0x2c, 0x18, 0x8e, 0x1b, 0xed, 0xe5, + 0x28, 0xd8, 0x72, 0x9b, 0x24, 0x12, 0xd6, 0xd4, 0x11, 0x45, 0xd3, 0xca, 0xcc, 0xa2, 0x24, 0xa8, + 0xf9, 0xf2, 0xfd, 0xad, 0x86, 0x60, 0x93, 0x2f, 0xdd, 0x65, 0x3c, 0x2a, 0xde, 0x7d, 0x96, 0x34, + 0x5c, 0xaa, 0xff, 0xe4, 0xa6, 0x8a, 0xcd, 0x94, 0x23, 0x5b, 0x97, 0xb3, 0x9d, 0xc6, 0x26, 0x5d, + 0x6f, 0xba, 0x43, 0xef, 0xbc, 0xbb, 0x3b, 0xf1, 0xe8, 0x4c, 0x3e, 0x4f, 0xdc, 0xab, 0x33, 0x6c, + 0xc0, 0xc2, 0x8e, 0xe7, 0x61, 0xf2, 0x7a, 0x87, 0x30, 0x97, 0x49, 0x01, 0x03, 0xb6, 0xac, 0x09, + 0x66, 0x06, 0xcc, 0x80, 0x60, 0x93, 0x2f, 0x7a, 0x1d, 0x06, 0xdb, 0x4e, 0x12, 0xb9, 0xdb, 0xc2, + 0x4f, 0x72, 0x44, 0x7b, 0x7f, 0x91, 0xd1, 0xd2, 0xcc, 0x99, 0xa6, 0xe6, 0x8d, 0x58, 0x30, 0x42, + 0x6d, 0xa8, 0xb4, 0x49, 0xd4, 0x22, 0xe3, 0xd5, 0x22, 0x7c, 0xc2, 0x8b, 0x94, 0x94, 0x66, 0x58, + 0xa3, 0xd6, 0x11, 0x6b, 0xc3, 0x9c, 0x0b, 0x7a, 0x15, 0xaa, 0x31, 0xf1, 0x48, 0x83, 0xda, 0x37, + 0x35, 0xc6, 0xf1, 0x87, 0xfa, 0xb4, 0xf5, 0xa8, 0x61, 0xb1, 0x22, 0x1e, 0xe5, 0x0b, 0x4c, 0xfe, + 0xc3, 0x8a, 0x24, 0x1d, 0xc0, 0xd0, 0xeb, 0xb4, 0x5c, 0x7f, 0x1c, 0x8a, 0x18, 0xc0, 0x65, 0x46, + 0x2b, 0x33, 0x80, 0xbc, 0x11, 0x0b, 0x46, 0xf6, 0x7f, 0xb6, 0x00, 0xa5, 0x85, 0xda, 0x7d, 0x30, + 0x6a, 0x5f, 0x4f, 0x1b, 0xb5, 0x0b, 0x45, 0x5a, 0x1d, 0x3d, 0xec, 0xda, 0xdf, 0xa8, 0x41, 0x46, + 0x1d, 0x5c, 0x27, 0x71, 0x42, 0x9a, 0x6f, 0x8b, 0xf0, 0xb7, 0x45, 0xf8, 0xdb, 0x22, 0x5c, 0x89, + 0xf0, 0xb5, 0x8c, 0x08, 0x7f, 0xaf, 0xb1, 0xea, 0xf5, 0x01, 0xec, 0x6b, 0xea, 0x84, 0xd6, 0xec, + 0x81, 0x81, 0x40, 0x25, 0xc1, 0xd5, 0x95, 0xa5, 0xeb, 0xb9, 0x32, 0xfb, 0xb5, 0xb4, 0xcc, 0x3e, + 0x2a, 0x8b, 0xff, 0x1f, 0xa4, 0xf4, 0xbf, 0xb2, 0xe0, 0x5d, 0x69, 0xe9, 0x25, 0x67, 0xce, 0x7c, + 0xcb, 0x0f, 0x22, 0x32, 0xeb, 0xae, 0xaf, 0x93, 0x88, 0xf8, 0x0d, 0x12, 0x2b, 0x2f, 0x86, 0xd5, + 0xcb, 0x8b, 0x81, 0x9e, 0x87, 0x91, 0xdb, 0x71, 0xe0, 0x2f, 0x07, 0xae, 0x2f, 0x44, 0x10, 0xdd, + 0x08, 0x9f, 0xbc, 0xbb, 0x3b, 0x31, 0x42, 0x47, 0x54, 0xb6, 0xe3, 0x14, 0x16, 0x9a, 0x81, 0x53, + 0xb7, 0x5f, 0x5f, 0x76, 0x12, 0xc3, 0x1d, 0x20, 0x37, 0xee, 0xec, 0xc0, 0xe2, 0xea, 0xcb, 0x19, + 0x20, 0xee, 0xc6, 0xb7, 0xff, 0x66, 0x09, 0xce, 0x65, 0x5e, 0x24, 0xf0, 0xbc, 0xa0, 0x93, 0xd0, + 0x4d, 0x0d, 0xfa, 0x8a, 0x05, 0x27, 0xdb, 0x69, 0x8f, 0x43, 0x2c, 0x1c, 0xbb, 0xef, 0x2f, 0x4c, + 0x47, 0x64, 0x5c, 0x1a, 0xf5, 0x71, 0x31, 0x42, 0x27, 0x33, 0x80, 0x18, 0x77, 0xf5, 0x05, 0xbd, + 0x0a, 0xb5, 0xb6, 0xb3, 0x7d, 0x23, 0x6c, 0x3a, 0x89, 0xdc, 0x4f, 0xf6, 0x76, 0x03, 0x74, 0x12, + 0xd7, 0x9b, 0xe4, 0x47, 0xfb, 0x93, 0xf3, 0x7e, 0xb2, 0x14, 0xad, 0x24, 0x91, 0xeb, 0xb7, 0xb8, + 0x3b, 0x6f, 0x51, 0x92, 0xc1, 0x9a, 0xa2, 0xfd, 0x65, 0x2b, 0xab, 0xa4, 0xd4, 0xe8, 0x44, 0x4e, + 0x42, 0x5a, 0x3b, 0xe8, 0x23, 0x50, 0xa1, 0x1b, 0x3f, 0x39, 0x2a, 0xb7, 0x8a, 0xd4, 0x9c, 0xc6, + 0x97, 0xd0, 0x4a, 0x94, 0xfe, 0x8b, 0x31, 0x67, 0x6a, 0x7f, 0xa5, 0x96, 0x35, 0x16, 0xd8, 0xe1, + 0xed, 0x45, 0x80, 0x56, 0xb0, 0x4a, 0xda, 0xa1, 0x47, 0x87, 0xc5, 0x62, 0x27, 0x00, 0xca, 0xd7, + 0x31, 0xa7, 0x20, 0xd8, 0xc0, 0x42, 0x7f, 0xd9, 0x02, 0x68, 0xc9, 0x39, 0x2f, 0x0d, 0x81, 0x1b, + 0x45, 0xbe, 0x8e, 0x5e, 0x51, 0xba, 0x2f, 0x8a, 0x21, 0x36, 0x98, 0xa3, 0x9f, 0xb6, 0xa0, 0x9a, + 0xc8, 0xee, 0x73, 0xd5, 0xb8, 0x5a, 0x64, 0x4f, 0xe4, 0x4b, 0x6b, 0x9b, 0x48, 0x0d, 0x89, 0xe2, + 0x8b, 0x7e, 0xd6, 0x02, 0x88, 0x77, 0xfc, 0xc6, 0x72, 0xe0, 0xb9, 0x8d, 0x1d, 0xa1, 0x31, 0x6f, + 0x16, 0xea, 0x8f, 0x51, 0xd4, 0xeb, 0x63, 0x74, 0x34, 0xf4, 0x7f, 0x6c, 0x70, 0x46, 0x1f, 0x83, + 0x6a, 0x2c, 0xa6, 0x9b, 0xd0, 0x91, 0xab, 0xc5, 0x7a, 0x85, 0x38, 0x6d, 0x21, 0x5e, 0xc5, 0x3f, + 0xac, 0x78, 0xa2, 0x9f, 0xb7, 0xe0, 0x44, 0x98, 0xf6, 0xf3, 0x09, 0x75, 0x58, 0x9c, 0x0c, 0xc8, + 0xf8, 0x11, 0xeb, 0xa7, 0xef, 0xee, 0x4e, 0x9c, 0xc8, 0x34, 0xe2, 0x6c, 0x2f, 0xa8, 0x04, 0xd4, + 0x33, 0x78, 0x29, 0xe4, 0x3e, 0xc7, 0x21, 0x2d, 0x01, 0xe7, 0xb2, 0x40, 0xdc, 0x8d, 0x8f, 0x96, + 0xe1, 0x0c, 0xed, 0xdd, 0x0e, 0x37, 0x3f, 0xa5, 0x7a, 0x89, 0x99, 0x32, 0xac, 0xd6, 0x1f, 0x13, + 0x33, 0x84, 0x79, 0xf5, 0xb3, 0x38, 0x38, 0xf7, 0x49, 0xf4, 0xbb, 0x16, 0x3c, 0xe6, 0x32, 0x35, + 0x60, 0x3a, 0xcc, 0xb5, 0x46, 0x10, 0x27, 0xb1, 0xa4, 0x50, 0x59, 0xd1, 0x4b, 0xfd, 0xd4, 0xff, + 0x82, 0x78, 0x83, 0xc7, 0xe6, 0xf7, 0xe8, 0x12, 0xde, 0xb3, 0xc3, 0xe8, 0x87, 0x61, 0x54, 0xae, + 0x8b, 0x65, 0x2a, 0x82, 0x99, 0xa2, 0xad, 0xd5, 0x4f, 0xdd, 0xdd, 0x9d, 0x18, 0x5d, 0x35, 0x01, + 0x38, 0x8d, 0x67, 0x7f, 0xb3, 0x94, 0x3a, 0x0f, 0x51, 0x4e, 0x48, 0x26, 0x6e, 0x1a, 0xd2, 0xff, + 0x23, 0xa5, 0x67, 0xa1, 0xe2, 0x46, 0x79, 0x97, 0xb4, 0xb8, 0x51, 0x4d, 0x31, 0x36, 0x98, 0x53, + 0xa3, 0xf4, 0x94, 0x93, 0x75, 0x75, 0x0a, 0x09, 0xf8, 0x6a, 0x91, 0x5d, 0xea, 0x3e, 0xbd, 0x3a, + 0x27, 0xba, 0x76, 0xaa, 0x0b, 0x84, 0xbb, 0xbb, 0x64, 0x7f, 0x33, 0x7d, 0x06, 0x63, 0x2c, 0xde, + 0x3e, 0xce, 0x97, 0x3e, 0x6f, 0xc1, 0x70, 0x14, 0x78, 0x9e, 0xeb, 0xb7, 0xa8, 0xa0, 0x11, 0xda, + 0xf2, 0x83, 0xc7, 0xa2, 0xb0, 0x84, 0x44, 0x61, 0xa6, 0x2d, 0xd6, 0x3c, 0xb1, 0xd9, 0x01, 0xfb, + 0x4f, 0x2c, 0x18, 0xef, 0x25, 0x10, 0x11, 0x81, 0x77, 0xca, 0xd5, 0xae, 0xa2, 0x2b, 0x96, 0xfc, + 0x59, 0xe2, 0x11, 0xe5, 0x78, 0xae, 0xd6, 0x9f, 0x14, 0xaf, 0xf9, 0xce, 0xe5, 0xde, 0xa8, 0x78, + 0x2f, 0x3a, 0xe8, 0x15, 0x38, 0x69, 0xbc, 0x57, 0xac, 0x06, 0xa6, 0x56, 0x9f, 0xa4, 0x16, 0xc8, + 0x74, 0x06, 0x76, 0x6f, 0x77, 0xe2, 0x91, 0x6c, 0x9b, 0x90, 0xd8, 0x5d, 0x74, 0xec, 0x5f, 0x29, + 0x65, 0xbf, 0x96, 0x52, 0xb6, 0x6f, 0x59, 0x5d, 0xdb, 0xf9, 0xf7, 0x1f, 0x87, 0x82, 0x63, 0x1b, + 0x7f, 0x15, 0xc0, 0xd1, 0x1b, 0xe7, 0x01, 0x9e, 0x10, 0xdb, 0xff, 0x66, 0x00, 0xf6, 0xe8, 0x59, + 0x1f, 0xd6, 0xf3, 0x81, 0x8f, 0x15, 0x3f, 0x6b, 0xa9, 0x23, 0xa7, 0x32, 0x5b, 0xe4, 0xcd, 0xe3, + 0x1a, 0x7b, 0xbe, 0x81, 0x89, 0x79, 0x94, 0x82, 0x72, 0x63, 0xa7, 0x0f, 0xb7, 0xd0, 0x57, 0xad, + 0xf4, 0xa1, 0x19, 0x0f, 0x3b, 0x73, 0x8f, 0xad, 0x4f, 0xc6, 0x49, 0x1c, 0xef, 0x98, 0x3e, 0xbf, + 0xe9, 0x75, 0x46, 0x37, 0x09, 0xb0, 0xee, 0xfa, 0x8e, 0xe7, 0xbe, 0x41, 0xb7, 0x27, 0x15, 0xa6, + 0x61, 0x99, 0xc9, 0x72, 0x59, 0xb5, 0x62, 0x03, 0xe3, 0xfc, 0x5f, 0x82, 0x61, 0xe3, 0xcd, 0x73, + 0x82, 0x2b, 0xce, 0x98, 0xc1, 0x15, 0x35, 0x23, 0x26, 0xe2, 0xfc, 0x7b, 0xe1, 0x64, 0xb6, 0x83, + 0x07, 0x79, 0xde, 0xfe, 0x5f, 0x43, 0xd9, 0x53, 0xac, 0x55, 0x12, 0xb5, 0x69, 0xd7, 0xde, 0xf6, + 0x2c, 0xbd, 0xed, 0x59, 0x7a, 0xdb, 0xb3, 0x64, 0x1e, 0x0e, 0x08, 0xaf, 0xc9, 0xd0, 0x7d, 0xf2, + 0x9a, 0xa4, 0xfc, 0x40, 0xd5, 0xc2, 0xfd, 0x40, 0xf6, 0xdd, 0x0a, 0xa4, 0xec, 0x28, 0x3e, 0xde, + 0x3f, 0x00, 0x43, 0x11, 0x09, 0x83, 0x1b, 0x78, 0x41, 0xe8, 0x10, 0x1d, 0x6b, 0xcf, 0x9b, 0xb1, + 0x84, 0x53, 0x5d, 0x13, 0x3a, 0xc9, 0x86, 0x50, 0x22, 0x4a, 0xd7, 0x2c, 0x3b, 0xc9, 0x06, 0x66, + 0x10, 0xf4, 0x5e, 0x18, 0x4b, 0x9c, 0xa8, 0x45, 0xed, 0xed, 0x2d, 0xf6, 0x59, 0xc5, 0x59, 0xe7, + 0x23, 0x02, 0x77, 0x6c, 0x35, 0x05, 0xc5, 0x19, 0x6c, 0xf4, 0x3a, 0x0c, 0x6c, 0x10, 0xaf, 0x2d, + 0x86, 0x7c, 0xa5, 0x38, 0x19, 0xcf, 0xde, 0xf5, 0x0a, 0xf1, 0xda, 0x5c, 0x02, 0xd1, 0x5f, 0x98, + 0xb1, 0xa2, 0xf3, 0xad, 0xb6, 0xd9, 0x89, 0x93, 0xa0, 0xed, 0xbe, 0x21, 0x5d, 0x7c, 0xef, 0x2f, + 0x98, 0xf1, 0x35, 0x49, 0x9f, 0xfb, 0x52, 0xd4, 0x5f, 0xac, 0x39, 0xb3, 0x7e, 0x34, 0xdd, 0x88, + 0x7d, 0xaa, 0x1d, 0xe1, 0xa9, 0x2b, 0xba, 0x1f, 0xb3, 0x92, 0x3e, 0xef, 0x87, 0xfa, 0x8b, 0x35, + 0x67, 0xb4, 0xa3, 0xe6, 0xfd, 0x30, 0xeb, 0xc3, 0x8d, 0x82, 0xfb, 0xc0, 0xe7, 0x7c, 0xee, 0xfc, + 0x7f, 0x12, 0x2a, 0x8d, 0x0d, 0x27, 0x4a, 0xc6, 0x47, 0xd8, 0xa4, 0x51, 0x3e, 0x9d, 0x19, 0xda, + 0x88, 0x39, 0x0c, 0x3d, 0x0e, 0xe5, 0x88, 0xac, 0xb3, 0xb8, 0x4d, 0x23, 0xa2, 0x07, 0x93, 0x75, + 0x4c, 0xdb, 0xed, 0x5f, 0x2a, 0xa5, 0xcd, 0xa5, 0xf4, 0x7b, 0xf3, 0xd9, 0xde, 0xe8, 0x44, 0xb1, + 0xf4, 0xfb, 0x18, 0xb3, 0x9d, 0x35, 0x63, 0x09, 0x47, 0x9f, 0xb0, 0x60, 0xe8, 0x76, 0x1c, 0xf8, + 0x3e, 0x49, 0x84, 0x6a, 0xba, 0x59, 0xf0, 0x50, 0x5c, 0xe5, 0xd4, 0x75, 0x1f, 0x44, 0x03, 0x96, + 0x7c, 0x69, 0x77, 0xc9, 0x76, 0xc3, 0xeb, 0x34, 0xbb, 0x82, 0x34, 0x2e, 0xf1, 0x66, 0x2c, 0xe1, + 0x14, 0xd5, 0xf5, 0x39, 0xea, 0x40, 0x1a, 0x75, 0xde, 0x17, 0xa8, 0x02, 0x6e, 0xff, 0xf5, 0x41, + 0x38, 0x9b, 0xbb, 0x38, 0xa8, 0x21, 0xc3, 0x4c, 0x85, 0xcb, 0xae, 0x47, 0x64, 0x78, 0x12, 0x33, + 0x64, 0x6e, 0xaa, 0x56, 0x6c, 0x60, 0xa0, 0x9f, 0x02, 0x08, 0x9d, 0xc8, 0x69, 0x13, 0xe5, 0x97, + 0x3d, 0xb2, 0xbd, 0x40, 0xfb, 0xb1, 0x2c, 0x69, 0xea, 0xbd, 0xa9, 0x6a, 0x8a, 0xb1, 0xc1, 0x12, + 0xbd, 0x00, 0xc3, 0x11, 0xf1, 0x88, 0x13, 0xb3, 0xb0, 0xdf, 0x6c, 0x0e, 0x03, 0xd6, 0x20, 0x6c, + 0xe2, 0xa1, 0xa7, 0x54, 0x24, 0x57, 0x26, 0xa2, 0x25, 0x1d, 0xcd, 0x85, 0xde, 0xb4, 0x60, 0x6c, + 0xdd, 0xf5, 0x88, 0xe6, 0x2e, 0x32, 0x0e, 0x96, 0x8e, 0xfe, 0x92, 0x97, 0x4d, 0xba, 0x5a, 0x42, + 0xa6, 0x9a, 0x63, 0x9c, 0x61, 0x4f, 0x3f, 0xf3, 0x16, 0x89, 0x98, 0x68, 0x1d, 0x4c, 0x7f, 0xe6, + 0x9b, 0xbc, 0x19, 0x4b, 0x38, 0x9a, 0x86, 0x13, 0xa1, 0x13, 0xc7, 0x33, 0x11, 0x69, 0x12, 0x3f, + 0x71, 0x1d, 0x8f, 0xe7, 0x03, 0x54, 0x75, 0x3c, 0xf0, 0x72, 0x1a, 0x8c, 0xb3, 0xf8, 0xe8, 0x03, + 0xf0, 0x28, 0x77, 0x7c, 0x2c, 0xba, 0x71, 0xec, 0xfa, 0x2d, 0x3d, 0x0d, 0x84, 0xff, 0x67, 0x42, + 0x90, 0x7a, 0x74, 0x3e, 0x1f, 0x0d, 0xf7, 0x7a, 0x1e, 0x3d, 0x03, 0xd5, 0x78, 0xd3, 0x0d, 0x67, + 0xa2, 0x66, 0xcc, 0x0e, 0x3d, 0xaa, 0xda, 0xdb, 0xb8, 0x22, 0xda, 0xb1, 0xc2, 0x40, 0x0d, 0x18, + 0xe1, 0x9f, 0x84, 0x87, 0xa2, 0x09, 0xf9, 0xf8, 0x6c, 0x4f, 0xf5, 0x28, 0xd2, 0xdb, 0x26, 0xb1, + 0x73, 0xe7, 0x92, 0x3c, 0x82, 0xe1, 0x27, 0x06, 0x37, 0x0d, 0x32, 0x38, 0x45, 0xd4, 0xfe, 0x85, + 0x52, 0x7a, 0xc7, 0x6d, 0x2e, 0x52, 0x14, 0xd3, 0xa5, 0x98, 0xdc, 0x74, 0x22, 0xe9, 0x8d, 0x39, + 0x62, 0xda, 0x82, 0xa0, 0x7b, 0xd3, 0x89, 0xcc, 0x45, 0xcd, 0x18, 0x60, 0xc9, 0x09, 0xdd, 0x86, + 0x81, 0xc4, 0x73, 0x0a, 0xca, 0x73, 0x32, 0x38, 0x6a, 0x07, 0xc8, 0xc2, 0x74, 0x8c, 0x19, 0x0f, + 0xf4, 0x18, 0xb5, 0xfa, 0xd7, 0xe4, 0x11, 0x89, 0x30, 0xd4, 0xd7, 0x62, 0xcc, 0x5a, 0xed, 0x7b, + 0x90, 0x23, 0x57, 0x95, 0x22, 0x43, 0x17, 0x01, 0xe8, 0x06, 0x72, 0x39, 0x22, 0xeb, 0xee, 0xb6, + 0x30, 0x24, 0xd4, 0xda, 0xbd, 0xae, 0x20, 0xd8, 0xc0, 0x92, 0xcf, 0xac, 0x74, 0xd6, 0xe9, 0x33, + 0xa5, 0xee, 0x67, 0x38, 0x04, 0x1b, 0x58, 0xe8, 0x79, 0x18, 0x74, 0xdb, 0x4e, 0x4b, 0x85, 0x60, + 0x3e, 0x46, 0x17, 0xed, 0x3c, 0x6b, 0xb9, 0xb7, 0x3b, 0x31, 0xa6, 0x3a, 0xc4, 0x9a, 0xb0, 0xc0, + 0x45, 0xbf, 0x62, 0xc1, 0x48, 0x23, 0x68, 0xb7, 0x03, 0x9f, 0x6f, 0xbb, 0xc4, 0x1e, 0xf2, 0xf6, + 0x71, 0xa9, 0xf9, 0xc9, 0x19, 0x83, 0x19, 0xdf, 0x44, 0xaa, 0x84, 0x2c, 0x13, 0x84, 0x53, 0xbd, + 0x32, 0xd7, 0x76, 0x65, 0x9f, 0xb5, 0xfd, 0xeb, 0x16, 0x9c, 0xe2, 0xcf, 0x1a, 0xbb, 0x41, 0x91, + 0x7b, 0x14, 0x1c, 0xf3, 0x6b, 0x75, 0x6d, 0x90, 0x95, 0x97, 0xae, 0x0b, 0x8e, 0xbb, 0x3b, 0x89, + 0xe6, 0xe0, 0xd4, 0x7a, 0x10, 0x35, 0x88, 0x39, 0x10, 0x42, 0x30, 0x29, 0x42, 0x97, 0xb3, 0x08, + 0xb8, 0xfb, 0x19, 0x74, 0x13, 0x1e, 0x31, 0x1a, 0xcd, 0x71, 0xe0, 0xb2, 0xe9, 0x09, 0x41, 0xed, + 0x91, 0xcb, 0xb9, 0x58, 0xb8, 0xc7, 0xd3, 0x69, 0x87, 0x49, 0xad, 0x0f, 0x87, 0xc9, 0x6b, 0x70, + 0xae, 0xd1, 0x3d, 0x32, 0x5b, 0x71, 0x67, 0x2d, 0xe6, 0x92, 0xaa, 0x5a, 0xff, 0x3e, 0x41, 0xe0, + 0xdc, 0x4c, 0x2f, 0x44, 0xdc, 0x9b, 0x06, 0xfa, 0x08, 0x54, 0x23, 0xc2, 0xbe, 0x4a, 0x2c, 0x12, + 0x71, 0x8e, 0xb8, 0x4b, 0xd6, 0x16, 0x28, 0x27, 0xab, 0x65, 0xaf, 0x68, 0x88, 0xb1, 0xe2, 0x88, + 0xee, 0xc0, 0x50, 0xe8, 0x24, 0x8d, 0x0d, 0x91, 0x7e, 0x73, 0xe4, 0xf8, 0x17, 0xc5, 0x9c, 0xf9, + 0xc0, 0x8d, 0x84, 0x5d, 0xce, 0x04, 0x4b, 0x6e, 0xd4, 0x1a, 0x69, 0x04, 0xed, 0x30, 0xf0, 0x89, + 0x9f, 0xc4, 0xe3, 0xa3, 0xda, 0x1a, 0x99, 0x51, 0xad, 0xd8, 0xc0, 0x40, 0xcb, 0x70, 0x86, 0xf9, + 0x8c, 0x6e, 0xb9, 0xc9, 0x46, 0xd0, 0x49, 0xe4, 0x16, 0x68, 0x7c, 0x2c, 0x7d, 0x54, 0xb1, 0x90, + 0x83, 0x83, 0x73, 0x9f, 0x3c, 0xff, 0x3e, 0x38, 0xd5, 0xb5, 0x94, 0x0f, 0xe4, 0xae, 0x99, 0x85, + 0x47, 0xf2, 0x17, 0xcd, 0x81, 0x9c, 0x36, 0xff, 0x38, 0x13, 0x36, 0x6b, 0x18, 0xd2, 0x7d, 0x38, + 0x00, 0x1d, 0x28, 0x13, 0x7f, 0x4b, 0xe8, 0x90, 0xcb, 0x47, 0xfb, 0x76, 0x97, 0xfc, 0x2d, 0xbe, + 0xe6, 0x99, 0x97, 0xe3, 0x92, 0xbf, 0x85, 0x29, 0x6d, 0xf4, 0x45, 0x2b, 0x65, 0x08, 0x72, 0xb7, + 0xe1, 0x87, 0x8e, 0x65, 0xe7, 0xd0, 0xb7, 0x6d, 0x68, 0xff, 0xdb, 0x12, 0x5c, 0xd8, 0x8f, 0x48, + 0x1f, 0xc3, 0xf7, 0x24, 0x0c, 0xc6, 0xec, 0x20, 0x5c, 0x08, 0xe5, 0x61, 0x3a, 0x57, 0xf9, 0xd1, + 0xf8, 0x6b, 0x58, 0x80, 0x90, 0x07, 0xe5, 0xb6, 0x13, 0x0a, 0x6f, 0xd2, 0xfc, 0x51, 0x13, 0x69, + 0xe8, 0x7f, 0xc7, 0x5b, 0x74, 0x42, 0xee, 0xa3, 0x30, 0x1a, 0x30, 0x65, 0x83, 0x12, 0xa8, 0x38, + 0x51, 0xe4, 0xc8, 0x53, 0xd7, 0x6b, 0xc5, 0xf0, 0x9b, 0xa6, 0x24, 0xf9, 0xa1, 0x55, 0xaa, 0x09, + 0x73, 0x66, 0xf6, 0x67, 0x87, 0x52, 0xc9, 0x24, 0xec, 0x28, 0x3d, 0x86, 0x41, 0xe1, 0x44, 0xb2, + 0x8a, 0xce, 0x5f, 0xe2, 0xd9, 0x80, 0x6c, 0x9f, 0x28, 0x72, 0xaa, 0x05, 0x2b, 0xf4, 0x19, 0x8b, + 0x65, 0x2e, 0xcb, 0x04, 0x1b, 0xb1, 0x3b, 0x3b, 0x9e, 0x44, 0x6a, 0x33, 0x1f, 0x5a, 0x36, 0x62, + 0x93, 0xbb, 0xa8, 0x40, 0xc0, 0xac, 0xd2, 0xee, 0x0a, 0x04, 0xcc, 0xca, 0x94, 0x70, 0xb4, 0x9d, + 0x73, 0x64, 0x5e, 0x40, 0xf6, 0x6b, 0x1f, 0x87, 0xe4, 0x5f, 0xb5, 0xe0, 0x94, 0x9b, 0x3d, 0xfb, + 0x14, 0x7b, 0x99, 0x23, 0x06, 0x65, 0xf4, 0x3e, 0x5a, 0x55, 0xea, 0xbc, 0x0b, 0x84, 0xbb, 0x3b, + 0x83, 0x9a, 0x30, 0xe0, 0xfa, 0xeb, 0x81, 0x30, 0x62, 0xea, 0x47, 0xeb, 0xd4, 0xbc, 0xbf, 0x1e, + 0xe8, 0xd5, 0x4c, 0xff, 0x61, 0x46, 0x1d, 0x2d, 0xc0, 0x99, 0x48, 0x78, 0x9b, 0xae, 0xb8, 0x71, + 0x12, 0x44, 0x3b, 0x0b, 0x6e, 0xdb, 0x4d, 0x98, 0x01, 0x52, 0xae, 0x8f, 0x53, 0xfd, 0x80, 0x73, + 0xe0, 0x38, 0xf7, 0x29, 0xf4, 0x06, 0x0c, 0xc9, 0x54, 0xeb, 0x6a, 0x11, 0xfb, 0xc2, 0xee, 0xf9, + 0xaf, 0x26, 0xd3, 0x8a, 0xc8, 0xaa, 0x96, 0x0c, 0xed, 0x37, 0x87, 0xa1, 0xfb, 0x58, 0x14, 0x7d, + 0x14, 0x6a, 0x91, 0x4a, 0xff, 0xb6, 0x8a, 0x50, 0xd7, 0xf2, 0xfb, 0x8a, 0x23, 0x59, 0x65, 0x0a, + 0xe9, 0x44, 0x6f, 0xcd, 0x91, 0x6e, 0x58, 0x62, 0x7d, 0x7a, 0x5a, 0xc0, 0xdc, 0x16, 0x5c, 0xf5, + 0xc9, 0xd8, 0x8e, 0xdf, 0xc0, 0x8c, 0x07, 0x8a, 0x60, 0x70, 0x83, 0x38, 0x5e, 0xb2, 0x51, 0x8c, + 0x13, 0xff, 0x0a, 0xa3, 0x95, 0x4d, 0x02, 0xe2, 0xad, 0x58, 0x70, 0x42, 0xdb, 0x30, 0xb4, 0xc1, + 0x27, 0x80, 0xd8, 0x43, 0x2c, 0x1e, 0x75, 0x70, 0x53, 0xb3, 0x4a, 0x7f, 0x6e, 0xd1, 0x80, 0x25, + 0x3b, 0x16, 0x6f, 0x63, 0x44, 0x04, 0xf0, 0xa5, 0x5b, 0x5c, 0xfe, 0x53, 0xff, 0xe1, 0x00, 0x1f, + 0x86, 0x91, 0x88, 0x34, 0x02, 0xbf, 0xe1, 0x7a, 0xa4, 0x39, 0x2d, 0x1d, 0xf4, 0x07, 0xc9, 0x9a, + 0x61, 0xfb, 0x70, 0x6c, 0xd0, 0xc0, 0x29, 0x8a, 0xe8, 0xd3, 0x16, 0x8c, 0xa9, 0x9c, 0x51, 0xfa, + 0x41, 0x88, 0x70, 0x08, 0x2f, 0x14, 0x94, 0xa1, 0xca, 0x68, 0xd6, 0xd1, 0xdd, 0xdd, 0x89, 0xb1, + 0x74, 0x1b, 0xce, 0xf0, 0x45, 0xaf, 0x00, 0x04, 0x6b, 0x3c, 0xa8, 0x66, 0x3a, 0x11, 0xde, 0xe1, + 0x83, 0xbc, 0xea, 0x18, 0x4f, 0x9f, 0x93, 0x14, 0xb0, 0x41, 0x0d, 0x5d, 0x03, 0xe0, 0xcb, 0x66, + 0x75, 0x27, 0x94, 0x1b, 0x0d, 0x99, 0xf6, 0x04, 0x2b, 0x0a, 0x72, 0x6f, 0x77, 0xa2, 0xdb, 0x5b, + 0xc7, 0x02, 0x17, 0x8c, 0xc7, 0xd1, 0x4f, 0xc2, 0x50, 0xdc, 0x69, 0xb7, 0x1d, 0xe5, 0x3b, 0x2e, + 0x30, 0x21, 0x8f, 0xd3, 0x35, 0x44, 0x11, 0x6f, 0xc0, 0x92, 0x23, 0xba, 0x4d, 0x85, 0x6a, 0x2c, + 0xdc, 0x88, 0x6c, 0x15, 0x71, 0x9b, 0x60, 0x98, 0xbd, 0xd3, 0x7b, 0xa4, 0xe1, 0x8d, 0x73, 0x70, + 0xee, 0xed, 0x4e, 0x3c, 0x92, 0x6e, 0x5f, 0x08, 0x44, 0x8a, 0x5c, 0x2e, 0x4d, 0x74, 0x55, 0x56, + 0x5e, 0xa1, 0xaf, 0x2d, 0x0b, 0x02, 0x3c, 0xad, 0x2b, 0xaf, 0xb0, 0xe6, 0xde, 0x63, 0x66, 0x3e, + 0x8c, 0x16, 0xe1, 0x74, 0x23, 0xf0, 0x93, 0x28, 0xf0, 0x3c, 0x5e, 0x79, 0x88, 0xef, 0xf9, 0xb8, + 0x6f, 0xf9, 0x9d, 0xa2, 0xdb, 0xa7, 0x67, 0xba, 0x51, 0x70, 0xde, 0x73, 0xb6, 0x9f, 0x8e, 0x36, + 0x14, 0x83, 0xf3, 0x3c, 0x8c, 0x90, 0xed, 0x84, 0x44, 0xbe, 0xe3, 0xdd, 0xc0, 0x0b, 0xd2, 0xab, + 0xca, 0xd6, 0xc0, 0x25, 0xa3, 0x1d, 0xa7, 0xb0, 0x90, 0xad, 0x1c, 0x1d, 0x46, 0xda, 0x27, 0x77, + 0x74, 0x48, 0xb7, 0x86, 0xfd, 0xbf, 0x4b, 0x29, 0x83, 0x6c, 0x35, 0x22, 0x04, 0x05, 0x50, 0xf1, + 0x83, 0xa6, 0x92, 0xfd, 0x57, 0x8b, 0x91, 0xfd, 0xd7, 0x83, 0xa6, 0x51, 0x9e, 0x85, 0xfe, 0x8b, + 0x31, 0xe7, 0xc3, 0xea, 0x57, 0xc8, 0x42, 0x1f, 0x0c, 0x20, 0x36, 0x1a, 0x45, 0x72, 0x56, 0xf5, + 0x2b, 0x96, 0x4c, 0x46, 0x38, 0xcd, 0x17, 0x6d, 0x42, 0x65, 0x23, 0x88, 0x13, 0xb9, 0xfd, 0x38, + 0xe2, 0x4e, 0xe7, 0x4a, 0x10, 0x27, 0xcc, 0x8a, 0x50, 0xaf, 0x4d, 0x5b, 0x62, 0xcc, 0x79, 0xd8, + 0xff, 0xc5, 0x4a, 0xf9, 0xd0, 0x6f, 0xb1, 0xc8, 0xdb, 0x2d, 0xe2, 0xd3, 0x65, 0x6d, 0x86, 0x1a, + 0xfd, 0x70, 0x26, 0x8f, 0xf1, 0x5d, 0xbd, 0x0a, 0x6b, 0xdd, 0xa1, 0x14, 0x26, 0x19, 0x09, 0x23, + 0x2a, 0xe9, 0xe3, 0x56, 0x3a, 0xa3, 0xb4, 0x54, 0xc4, 0x06, 0xc3, 0xcc, 0xaa, 0xde, 0x37, 0x39, + 0xd5, 0xfe, 0xa2, 0x05, 0x43, 0x75, 0xa7, 0xb1, 0x19, 0xac, 0xaf, 0xa3, 0x67, 0xa0, 0xda, 0xec, + 0x44, 0x66, 0x72, 0xab, 0x72, 0x1c, 0xcc, 0x8a, 0x76, 0xac, 0x30, 0xe8, 0x1c, 0x5e, 0x77, 0x1a, + 0x32, 0xb7, 0xba, 0xcc, 0xe7, 0xf0, 0x65, 0xd6, 0x82, 0x05, 0x04, 0xbd, 0x00, 0xc3, 0x6d, 0x67, + 0x5b, 0x3e, 0x9c, 0x75, 0xe0, 0x2f, 0x6a, 0x10, 0x36, 0xf1, 0xec, 0x7f, 0x69, 0xc1, 0x78, 0xdd, + 0x89, 0xdd, 0xc6, 0x74, 0x27, 0xd9, 0xa8, 0xbb, 0xc9, 0x5a, 0xa7, 0xb1, 0x49, 0x12, 0x9e, 0x50, + 0x4f, 0x7b, 0xd9, 0x89, 0xe9, 0x52, 0x52, 0xfb, 0x3a, 0xd5, 0xcb, 0x1b, 0xa2, 0x1d, 0x2b, 0x0c, + 0xf4, 0x06, 0x0c, 0x87, 0x4e, 0x1c, 0xdf, 0x09, 0xa2, 0x26, 0x26, 0xeb, 0xc5, 0x94, 0xb3, 0x58, + 0x21, 0x8d, 0x88, 0x24, 0x98, 0xac, 0x8b, 0x43, 0x66, 0x4d, 0x1f, 0x9b, 0xcc, 0xec, 0xcf, 0x5b, + 0x70, 0xae, 0x4e, 0x9c, 0x88, 0x44, 0xac, 0xfa, 0x85, 0x7a, 0x91, 0x19, 0x2f, 0xe8, 0x34, 0xd1, + 0xeb, 0x50, 0x4d, 0x68, 0x33, 0xed, 0x96, 0x55, 0x6c, 0xb7, 0xd8, 0x19, 0xf1, 0xaa, 0x20, 0x8e, + 0x15, 0x1b, 0xfb, 0x6f, 0x58, 0x30, 0xc2, 0x8e, 0xdb, 0x66, 0x49, 0xe2, 0xb8, 0x5e, 0x57, 0x91, + 0x28, 0xab, 0xcf, 0x22, 0x51, 0x17, 0x60, 0x60, 0x23, 0x68, 0x93, 0xec, 0x51, 0xf1, 0x95, 0x80, + 0x6e, 0xab, 0x29, 0x04, 0x3d, 0x47, 0x3f, 0xbc, 0xeb, 0x27, 0x0e, 0x5d, 0x02, 0xd2, 0x9d, 0x7b, + 0x82, 0x7f, 0x74, 0xd5, 0x8c, 0x4d, 0x1c, 0xfb, 0xb7, 0x6a, 0x30, 0x24, 0xe2, 0x09, 0xfa, 0x2e, + 0xaa, 0x20, 0xf7, 0xf7, 0xa5, 0x9e, 0xfb, 0xfb, 0x18, 0x06, 0x1b, 0xac, 0x5a, 0x9d, 0x30, 0x23, + 0xaf, 0x15, 0x12, 0x80, 0xc2, 0x0b, 0xe0, 0xe9, 0x6e, 0xf1, 0xff, 0x58, 0xb0, 0x42, 0x5f, 0xb0, + 0xe0, 0x44, 0x23, 0xf0, 0x7d, 0xd2, 0xd0, 0x36, 0xce, 0x40, 0x11, 0x71, 0x06, 0x33, 0x69, 0xa2, + 0xfa, 0xac, 0x27, 0x03, 0xc0, 0x59, 0xf6, 0xe8, 0x25, 0x18, 0xe5, 0x63, 0x76, 0x33, 0xe5, 0x83, + 0xd6, 0xb5, 0x83, 0x4c, 0x20, 0x4e, 0xe3, 0xa2, 0x49, 0xee, 0xcb, 0x17, 0x55, 0x7a, 0x06, 0xb5, + 0xab, 0xce, 0xa8, 0xcf, 0x63, 0x60, 0xa0, 0x08, 0x50, 0x44, 0xd6, 0x23, 0x12, 0x6f, 0x88, 0x78, + 0x0b, 0x66, 0x5f, 0x0d, 0x1d, 0x2e, 0x01, 0x1b, 0x77, 0x51, 0xc2, 0x39, 0xd4, 0xd1, 0xa6, 0xd8, + 0x60, 0x56, 0x8b, 0x90, 0xa1, 0xe2, 0x33, 0xf7, 0xdc, 0x67, 0x4e, 0x40, 0x25, 0xde, 0x70, 0xa2, + 0x26, 0xb3, 0xeb, 0xca, 0x3c, 0xe9, 0x67, 0x85, 0x36, 0x60, 0xde, 0x8e, 0x66, 0xe1, 0x64, 0xa6, + 0xf2, 0x51, 0x2c, 0x7c, 0xc5, 0x2a, 0xc1, 0x23, 0x53, 0x33, 0x29, 0xc6, 0x5d, 0x4f, 0x98, 0xce, + 0x87, 0xe1, 0x7d, 0x9c, 0x0f, 0x3b, 0x2a, 0xaa, 0x8f, 0x7b, 0x71, 0x5f, 0x2e, 0x64, 0x00, 0xfa, + 0x0a, 0xe1, 0xfb, 0x5c, 0x26, 0x84, 0x6f, 0x94, 0x75, 0xe0, 0x66, 0x31, 0x1d, 0x38, 0x78, 0xbc, + 0xde, 0x83, 0x8c, 0xbf, 0xfb, 0x73, 0x0b, 0xe4, 0x77, 0x9d, 0x71, 0x1a, 0x1b, 0x84, 0x4e, 0x19, + 0xf4, 0x5e, 0x18, 0x53, 0x5b, 0xe8, 0x99, 0xa0, 0xe3, 0xf3, 0xd0, 0xbb, 0xb2, 0x3e, 0x14, 0xc6, + 0x29, 0x28, 0xce, 0x60, 0xa3, 0x29, 0xa8, 0xd1, 0x71, 0xe2, 0x8f, 0x72, 0x5d, 0xab, 0xb6, 0xe9, + 0xd3, 0xcb, 0xf3, 0xe2, 0x29, 0x8d, 0x83, 0x02, 0x38, 0xe5, 0x39, 0x71, 0xc2, 0x7a, 0x40, 0x77, + 0xd4, 0x87, 0x2c, 0x7f, 0xc0, 0xb2, 0x08, 0x16, 0xb2, 0x84, 0x70, 0x37, 0x6d, 0xfb, 0x5b, 0x03, + 0x30, 0x9a, 0x92, 0x8c, 0x07, 0x54, 0xd2, 0xcf, 0x40, 0x55, 0xea, 0xcd, 0x6c, 0xa1, 0x16, 0xa5, + 0x5c, 0x15, 0x06, 0x55, 0x5a, 0x6b, 0x5a, 0xab, 0x66, 0x8d, 0x0a, 0x43, 0xe1, 0x62, 0x13, 0x8f, + 0x09, 0xe5, 0xc4, 0x8b, 0x67, 0x3c, 0x97, 0xf8, 0x09, 0xef, 0x66, 0x31, 0x42, 0x79, 0x75, 0x61, + 0xc5, 0x24, 0xaa, 0x85, 0x72, 0x06, 0x80, 0xb3, 0xec, 0xd1, 0xa7, 0x2c, 0x18, 0x75, 0xee, 0xc4, + 0xba, 0xa4, 0xaa, 0x08, 0xd6, 0x3b, 0xa2, 0x92, 0x4a, 0x55, 0x69, 0xe5, 0x2e, 0xdf, 0x54, 0x13, + 0x4e, 0x33, 0x45, 0x6f, 0x59, 0x80, 0xc8, 0x36, 0x69, 0xc8, 0x70, 0x42, 0xd1, 0x97, 0xc1, 0x22, + 0x76, 0x9a, 0x97, 0xba, 0xe8, 0x72, 0xa9, 0xde, 0xdd, 0x8e, 0x73, 0xfa, 0x60, 0xff, 0xb3, 0xb2, + 0x5a, 0x50, 0x3a, 0x82, 0xd5, 0x31, 0x22, 0xe9, 0xac, 0xc3, 0x47, 0xd2, 0xe9, 0x88, 0x84, 0xee, + 0xac, 0xca, 0x54, 0x12, 0x56, 0xe9, 0x01, 0x25, 0x61, 0xfd, 0xb4, 0x95, 0x2a, 0x49, 0x34, 0x7c, + 0xf1, 0x95, 0x62, 0xa3, 0x67, 0x27, 0x79, 0xb4, 0x44, 0x46, 0xba, 0xa7, 0x83, 0x64, 0xa8, 0x34, + 0x35, 0xd0, 0x0e, 0x24, 0x0d, 0xff, 0x7d, 0x19, 0x86, 0x0d, 0x4d, 0x9a, 0x6b, 0x16, 0x59, 0x0f, + 0x99, 0x59, 0x54, 0x3a, 0x80, 0x59, 0xf4, 0x53, 0x50, 0x6b, 0x48, 0x29, 0x5f, 0x4c, 0x51, 0xde, + 0xac, 0xee, 0xd0, 0x82, 0x5e, 0x35, 0x61, 0xcd, 0x13, 0xcd, 0xa5, 0x52, 0x77, 0x84, 0x86, 0x18, + 0x60, 0x1a, 0x22, 0x2f, 0xb7, 0x46, 0x68, 0x8a, 0xee, 0x67, 0x58, 0xe5, 0xaa, 0xd0, 0x15, 0xef, + 0x25, 0x63, 0xdc, 0x79, 0xe5, 0xaa, 0xe5, 0x79, 0xd9, 0x8c, 0x4d, 0x1c, 0xfb, 0x5b, 0x96, 0xfa, + 0xb8, 0xf7, 0xa1, 0x46, 0xc3, 0xed, 0x74, 0x8d, 0x86, 0x4b, 0x85, 0x0c, 0x73, 0x8f, 0xe2, 0x0c, + 0xd7, 0x61, 0x68, 0x26, 0x68, 0xb7, 0x1d, 0xbf, 0x89, 0xbe, 0x1f, 0x86, 0x1a, 0xfc, 0xa7, 0x70, + 0xec, 0xb0, 0xe3, 0x41, 0x01, 0xc5, 0x12, 0x86, 0x1e, 0x83, 0x01, 0x27, 0x6a, 0x49, 0x67, 0x0e, + 0x0b, 0xae, 0x99, 0x8e, 0x5a, 0x31, 0x66, 0xad, 0xf6, 0x3f, 0x1a, 0x00, 0x76, 0xa6, 0xed, 0x44, + 0xa4, 0xb9, 0x1a, 0xb0, 0xa2, 0x80, 0xc7, 0x7a, 0xa8, 0xa6, 0x37, 0x4b, 0x0f, 0xf3, 0xc1, 0x9a, + 0x71, 0xb8, 0x52, 0xbe, 0xcf, 0x87, 0x2b, 0x3d, 0xce, 0xcb, 0x06, 0x1e, 0xa2, 0xf3, 0x32, 0xfb, + 0xb3, 0x16, 0x20, 0x15, 0x08, 0xa1, 0x0f, 0xb4, 0xa7, 0xa0, 0xa6, 0x42, 0x22, 0x84, 0x61, 0xa5, + 0x45, 0x84, 0x04, 0x60, 0x8d, 0xd3, 0xc7, 0x0e, 0xf9, 0x49, 0x29, 0xbf, 0xcb, 0xe9, 0xb8, 0x5c, + 0x26, 0xf5, 0x85, 0x38, 0xb7, 0x7f, 0xbb, 0x04, 0x8f, 0x70, 0x95, 0xbc, 0xe8, 0xf8, 0x4e, 0x8b, + 0xb4, 0x69, 0xaf, 0xfa, 0x0d, 0x51, 0x68, 0xd0, 0xad, 0x99, 0x2b, 0xe3, 0x6c, 0x8f, 0xba, 0x76, + 0xf9, 0x9a, 0xe3, 0xab, 0x6c, 0xde, 0x77, 0x13, 0xcc, 0x88, 0xa3, 0x18, 0xaa, 0xb2, 0x62, 0xbd, + 0x90, 0xc5, 0x05, 0x31, 0x52, 0x62, 0x49, 0xe8, 0x4d, 0x82, 0x15, 0x23, 0x6a, 0xb8, 0x7a, 0x41, + 0x63, 0x13, 0x93, 0x30, 0x60, 0x72, 0xd7, 0x08, 0x73, 0x5c, 0x10, 0xed, 0x58, 0x61, 0xd8, 0xbf, + 0x6d, 0x41, 0x56, 0x23, 0x19, 0xd5, 0xd7, 0xac, 0x3d, 0xab, 0xaf, 0x1d, 0xa0, 0xfc, 0xd9, 0x4f, + 0xc0, 0xb0, 0x93, 0x50, 0x23, 0x82, 0x6f, 0xbb, 0xcb, 0x87, 0x3b, 0xd6, 0x58, 0x0c, 0x9a, 0xee, + 0xba, 0xcb, 0xb6, 0xdb, 0x26, 0x39, 0xfb, 0x7f, 0x0c, 0xc0, 0xa9, 0xae, 0x6c, 0x10, 0xf4, 0x22, + 0x8c, 0x34, 0xc4, 0xf4, 0x08, 0xa5, 0x43, 0xab, 0x66, 0x86, 0xc5, 0x69, 0x18, 0x4e, 0x61, 0xf6, + 0x31, 0x41, 0xe7, 0xe1, 0x74, 0x44, 0x37, 0xfa, 0x1d, 0x32, 0xbd, 0x9e, 0x90, 0x68, 0x85, 0x34, + 0x02, 0xbf, 0xc9, 0x6b, 0x04, 0x96, 0xeb, 0x8f, 0xde, 0xdd, 0x9d, 0x38, 0x8d, 0xbb, 0xc1, 0x38, + 0xef, 0x19, 0x14, 0xc2, 0xa8, 0x67, 0xda, 0x80, 0x62, 0x03, 0x70, 0x28, 0xf3, 0x51, 0xd9, 0x08, + 0xa9, 0x66, 0x9c, 0x66, 0x90, 0x36, 0x24, 0x2b, 0x0f, 0xc8, 0x90, 0xfc, 0xa4, 0x36, 0x24, 0xf9, + 0xf9, 0xfb, 0x07, 0x0b, 0xce, 0x06, 0x3a, 0x6e, 0x4b, 0xf2, 0x65, 0xa8, 0xca, 0xd8, 0xa4, 0xbe, + 0x62, 0x7a, 0x4c, 0x3a, 0x3d, 0x24, 0xda, 0xbd, 0x12, 0xe4, 0x6c, 0x42, 0xe8, 0x3a, 0xd3, 0x1a, + 0x3f, 0xb5, 0xce, 0x0e, 0xa6, 0xf5, 0xd1, 0x36, 0x8f, 0xcb, 0xe2, 0xba, 0xed, 0x03, 0x45, 0x6f, + 0xa2, 0x74, 0xa8, 0x96, 0x4a, 0x92, 0x50, 0xe1, 0x5a, 0x17, 0x01, 0xb4, 0xa1, 0x26, 0x42, 0xe0, + 0xd5, 0xb1, 0xaf, 0xb6, 0xe7, 0xb0, 0x81, 0x45, 0xf7, 0xd4, 0xae, 0x1f, 0x27, 0x8e, 0xe7, 0x5d, + 0x71, 0xfd, 0x44, 0x38, 0x07, 0x95, 0x12, 0x9f, 0xd7, 0x20, 0x6c, 0xe2, 0x9d, 0x7f, 0x8f, 0xf1, + 0x5d, 0x0e, 0xf2, 0x3d, 0x37, 0xe0, 0xdc, 0x9c, 0x9b, 0xa8, 0xc4, 0x0d, 0x35, 0x8f, 0xa8, 0x1d, + 0xa6, 0x12, 0x91, 0xac, 0x9e, 0x89, 0x48, 0x46, 0xe2, 0x44, 0x29, 0x9d, 0xe7, 0x91, 0x4d, 0x9c, + 0xb0, 0x5f, 0x84, 0x33, 0x73, 0x6e, 0x72, 0xd9, 0xf5, 0xc8, 0x01, 0x99, 0xd8, 0xbf, 0x39, 0x08, + 0x23, 0x66, 0xea, 0xdf, 0x41, 0x72, 0xa9, 0x3e, 0x4f, 0x4d, 0x2d, 0xf1, 0x76, 0xae, 0x3a, 0x34, + 0xbb, 0x75, 0xe4, 0x3c, 0xc4, 0xfc, 0x11, 0x33, 0xac, 0x2d, 0xcd, 0x13, 0x9b, 0x1d, 0x40, 0x77, + 0xa0, 0xb2, 0xce, 0x02, 0xfb, 0xcb, 0x45, 0x44, 0x16, 0xe4, 0x8d, 0xa8, 0x5e, 0x66, 0x3c, 0x35, + 0x80, 0xf3, 0xa3, 0x1a, 0x32, 0x4a, 0x67, 0x8b, 0x19, 0xc1, 0xa8, 0x22, 0x4f, 0x4c, 0x61, 0xf4, + 0x12, 0xf5, 0x95, 0x43, 0x88, 0xfa, 0x94, 0xe0, 0x1d, 0x7c, 0x40, 0x82, 0x97, 0x25, 0x69, 0x24, + 0x1b, 0xcc, 0x7e, 0x13, 0xd1, 0xf3, 0x43, 0x6c, 0x10, 0x8c, 0x24, 0x8d, 0x14, 0x18, 0x67, 0xf1, + 0xd1, 0xc7, 0x94, 0xe8, 0xae, 0x16, 0xe1, 0x57, 0x35, 0x67, 0xf4, 0x71, 0x4b, 0xed, 0xcf, 0x96, + 0x60, 0x6c, 0xce, 0xef, 0x2c, 0xcf, 0x2d, 0x77, 0xd6, 0x3c, 0xb7, 0x71, 0x8d, 0xec, 0x50, 0xd1, + 0xbc, 0x49, 0x76, 0xe6, 0x67, 0xc5, 0x0a, 0x52, 0x73, 0xe6, 0x1a, 0x6d, 0xc4, 0x1c, 0x46, 0x85, + 0xd1, 0xba, 0xeb, 0xb7, 0x48, 0x14, 0x46, 0xae, 0x70, 0x79, 0x1a, 0xc2, 0xe8, 0xb2, 0x06, 0x61, + 0x13, 0x8f, 0xd2, 0x0e, 0xee, 0xf8, 0x24, 0xca, 0x1a, 0xb2, 0x4b, 0xb4, 0x11, 0x73, 0x18, 0x45, + 0x4a, 0xa2, 0x4e, 0x9c, 0x88, 0xc9, 0xa8, 0x90, 0x56, 0x69, 0x23, 0xe6, 0x30, 0xba, 0xd2, 0xe3, + 0xce, 0x1a, 0x0b, 0xdc, 0xc8, 0x84, 0xea, 0xaf, 0xf0, 0x66, 0x2c, 0xe1, 0x14, 0x75, 0x93, 0xec, + 0xcc, 0xd2, 0x5d, 0x6f, 0x26, 0x63, 0xe7, 0x1a, 0x6f, 0xc6, 0x12, 0xce, 0x8a, 0x1b, 0xa6, 0x87, + 0xe3, 0xbb, 0xae, 0xb8, 0x61, 0xba, 0xfb, 0x3d, 0xf6, 0xcf, 0xbf, 0x6c, 0xc1, 0x88, 0x19, 0x6e, + 0x85, 0x5a, 0x19, 0x1b, 0x77, 0xa9, 0xab, 0x36, 0xee, 0x8f, 0xe5, 0x5d, 0x2c, 0xd6, 0x72, 0x93, + 0x20, 0x8c, 0x9f, 0x25, 0x7e, 0xcb, 0xf5, 0x09, 0x3b, 0x45, 0xe7, 0x61, 0x5a, 0xa9, 0x58, 0xae, + 0x99, 0xa0, 0x49, 0x0e, 0x61, 0x24, 0xdb, 0xb7, 0xe0, 0x54, 0x57, 0x9a, 0x56, 0x1f, 0xa6, 0xc5, + 0xbe, 0x49, 0xb2, 0x36, 0x86, 0x61, 0x4a, 0x58, 0x16, 0xd8, 0x99, 0x81, 0x53, 0x7c, 0x21, 0x51, + 0x4e, 0x2b, 0x8d, 0x0d, 0xd2, 0x56, 0xa9, 0x77, 0xcc, 0xbf, 0x7e, 0x33, 0x0b, 0xc4, 0xdd, 0xf8, + 0xf6, 0xe7, 0x2c, 0x18, 0x4d, 0x65, 0xce, 0x15, 0x64, 0x04, 0xb1, 0x95, 0x16, 0xb0, 0xe8, 0x3f, + 0x16, 0x02, 0x5d, 0x66, 0xca, 0x54, 0xaf, 0x34, 0x0d, 0xc2, 0x26, 0x9e, 0xfd, 0xc5, 0x12, 0x54, + 0x65, 0x04, 0x45, 0x1f, 0x5d, 0xf9, 0x8c, 0x05, 0xa3, 0xea, 0x4c, 0x83, 0x39, 0xcb, 0x4a, 0x45, + 0xa4, 0x39, 0xd0, 0x1e, 0xa8, 0xed, 0xb6, 0xbf, 0x1e, 0x68, 0x8b, 0x1c, 0x9b, 0xcc, 0x70, 0x9a, + 0x37, 0xba, 0x09, 0x10, 0xef, 0xc4, 0x09, 0x69, 0x1b, 0x6e, 0x3b, 0xdb, 0x58, 0x71, 0x93, 0x8d, + 0x20, 0x22, 0x74, 0x7d, 0x5d, 0x0f, 0x9a, 0x64, 0x45, 0x61, 0x6a, 0x13, 0x4a, 0xb7, 0x61, 0x83, + 0x92, 0xfd, 0x0f, 0x4a, 0x70, 0x32, 0xdb, 0x25, 0xf4, 0x41, 0x18, 0x91, 0xdc, 0x8d, 0x3b, 0xd2, + 0x64, 0xd8, 0xc8, 0x08, 0x36, 0x60, 0xf7, 0x76, 0x27, 0x26, 0xba, 0x2f, 0xa9, 0x9b, 0x34, 0x51, + 0x70, 0x8a, 0x18, 0x3f, 0x58, 0x12, 0x27, 0xa0, 0xf5, 0x9d, 0xe9, 0x30, 0x14, 0xa7, 0x43, 0xc6, + 0xc1, 0x92, 0x09, 0xc5, 0x19, 0x6c, 0xb4, 0x0c, 0x67, 0x8c, 0x96, 0xeb, 0xc4, 0x6d, 0x6d, 0xac, + 0x05, 0x91, 0xdc, 0x59, 0x3d, 0xa6, 0x03, 0xbb, 0xba, 0x71, 0x70, 0xee, 0x93, 0x54, 0xdb, 0x37, + 0x9c, 0xd0, 0x69, 0xb8, 0xc9, 0x8e, 0xf0, 0x43, 0x2a, 0xd9, 0x34, 0x23, 0xda, 0xb1, 0xc2, 0xb0, + 0x17, 0x61, 0xa0, 0xcf, 0x19, 0xd4, 0x97, 0x45, 0xff, 0x32, 0x54, 0x29, 0x39, 0x69, 0xde, 0x15, + 0x41, 0x32, 0x80, 0xaa, 0xbc, 0xbb, 0x04, 0xd9, 0x50, 0x76, 0x1d, 0x79, 0x76, 0xa7, 0x5e, 0x6b, + 0x3e, 0x8e, 0x3b, 0x6c, 0x93, 0x4c, 0x81, 0xe8, 0x49, 0x28, 0x93, 0xed, 0x30, 0x7b, 0x48, 0x77, + 0x69, 0x3b, 0x74, 0x23, 0x12, 0x53, 0x24, 0xb2, 0x1d, 0xa2, 0xf3, 0x50, 0x72, 0x9b, 0x42, 0x49, + 0x81, 0xc0, 0x29, 0xcd, 0xcf, 0xe2, 0x92, 0xdb, 0xb4, 0xb7, 0xa1, 0xa6, 0x2e, 0x4b, 0x41, 0x9b, + 0x52, 0x76, 0x5b, 0x45, 0x84, 0x3c, 0x49, 0xba, 0x3d, 0xa4, 0x76, 0x07, 0x40, 0xa7, 0x10, 0x16, + 0x25, 0x5f, 0x2e, 0xc0, 0x40, 0x23, 0x10, 0xe9, 0xcd, 0x55, 0x4d, 0x86, 0x09, 0x6d, 0x06, 0xb1, + 0x6f, 0xc1, 0xd8, 0x35, 0x3f, 0xb8, 0xc3, 0x2a, 0xbd, 0xb3, 0xc2, 0x66, 0x94, 0xf0, 0x3a, 0xfd, + 0x91, 0x35, 0x11, 0x18, 0x14, 0x73, 0x98, 0xaa, 0xf8, 0x54, 0xea, 0x55, 0xf1, 0xc9, 0xfe, 0xb8, + 0x05, 0x23, 0x2a, 0x17, 0x69, 0x6e, 0x6b, 0x93, 0xd2, 0x6d, 0x45, 0x41, 0x27, 0xcc, 0xd2, 0x65, + 0xd7, 0x19, 0x61, 0x0e, 0x33, 0x93, 0xf4, 0x4a, 0xfb, 0x24, 0xe9, 0x5d, 0x80, 0x81, 0x4d, 0xd7, + 0x6f, 0x66, 0xef, 0xe7, 0xb8, 0xe6, 0xfa, 0x4d, 0xcc, 0x20, 0xb4, 0x0b, 0x27, 0x55, 0x17, 0xa4, + 0x42, 0x78, 0x11, 0x46, 0xd6, 0x3a, 0xae, 0xd7, 0x94, 0x15, 0xdb, 0x32, 0x9e, 0x92, 0xba, 0x01, + 0xc3, 0x29, 0x4c, 0xba, 0xaf, 0x5b, 0x73, 0x7d, 0x27, 0xda, 0x59, 0xd6, 0x1a, 0x48, 0x09, 0xa5, + 0xba, 0x82, 0x60, 0x03, 0xcb, 0x7e, 0xb3, 0x0c, 0x63, 0xe9, 0x8c, 0xac, 0x3e, 0xb6, 0x57, 0x4f, + 0x42, 0x85, 0x25, 0x69, 0x65, 0x3f, 0x2d, 0x2f, 0x72, 0xc6, 0x61, 0x28, 0x86, 0x41, 0x5e, 0xde, + 0xa1, 0x98, 0xbb, 0x6d, 0x54, 0x27, 0x95, 0x7f, 0x85, 0xc5, 0x93, 0x89, 0x8a, 0x12, 0x82, 0x15, + 0xfa, 0x94, 0x05, 0x43, 0x41, 0x68, 0x56, 0x0a, 0xfa, 0x40, 0x91, 0xd9, 0x6a, 0x22, 0x59, 0x46, + 0x58, 0xc4, 0xea, 0xd3, 0xcb, 0xcf, 0x21, 0x59, 0x9f, 0xff, 0x11, 0x18, 0x31, 0x31, 0xf7, 0x33, + 0x8a, 0xab, 0xa6, 0x51, 0xfc, 0x19, 0x73, 0x52, 0x88, 0x7c, 0xbc, 0x3e, 0x96, 0xdb, 0x0d, 0xa8, + 0x34, 0x54, 0x00, 0xc0, 0xa1, 0xea, 0x7c, 0xaa, 0x7a, 0x0b, 0xec, 0x10, 0x88, 0x53, 0xb3, 0xbf, + 0x65, 0x19, 0xf3, 0x03, 0x93, 0x78, 0xbe, 0x89, 0x22, 0x28, 0xb7, 0xb6, 0x36, 0x85, 0x29, 0x7a, + 0xb5, 0xa0, 0xe1, 0x9d, 0xdb, 0xda, 0xd4, 0x73, 0xdc, 0x6c, 0xc5, 0x94, 0x59, 0x1f, 0x4e, 0xc0, + 0x54, 0xda, 0x66, 0x79, 0xff, 0xb4, 0x4d, 0xfb, 0xad, 0x12, 0x9c, 0xea, 0x9a, 0x54, 0xe8, 0x0d, + 0xa8, 0x44, 0xf4, 0x2d, 0xc5, 0xeb, 0x2d, 0x14, 0x96, 0x68, 0x19, 0xcf, 0x37, 0xb5, 0xde, 0x4d, + 0xb7, 0x63, 0xce, 0x12, 0x5d, 0x05, 0xa4, 0xc3, 0x54, 0x94, 0x07, 0x92, 0xbf, 0xf2, 0x79, 0xf1, + 0x28, 0x9a, 0xee, 0xc2, 0xc0, 0x39, 0x4f, 0xa1, 0x97, 0xb2, 0x8e, 0xcc, 0x72, 0xfa, 0xdc, 0x72, + 0x2f, 0x9f, 0xa4, 0xfd, 0xcf, 0x4b, 0x30, 0x9a, 0x2a, 0xdc, 0x84, 0x3c, 0xa8, 0x12, 0x8f, 0x39, + 0xf5, 0xa5, 0xb2, 0x39, 0x6a, 0x1d, 0x64, 0xa5, 0x20, 0x2f, 0x09, 0xba, 0x58, 0x71, 0x78, 0x38, + 0x0e, 0xd7, 0x5f, 0x84, 0x11, 0xd9, 0xa1, 0x0f, 0x38, 0x6d, 0x4f, 0x0c, 0xa0, 0x9a, 0xa3, 0x97, + 0x0c, 0x18, 0x4e, 0x61, 0xda, 0xbf, 0x53, 0x86, 0x71, 0x7e, 0x0a, 0xd2, 0x54, 0x33, 0x6f, 0x51, + 0xee, 0xb7, 0xfe, 0x8a, 0x2e, 0xaf, 0xc6, 0x07, 0x72, 0xed, 0xa8, 0xd7, 0x0e, 0xe4, 0x33, 0xea, + 0x2b, 0x32, 0xeb, 0x2b, 0x99, 0xc8, 0x2c, 0x6e, 0x76, 0xb7, 0x8e, 0xa9, 0x47, 0xdf, 0x5d, 0xa1, + 0x5a, 0x7f, 0xb7, 0x04, 0x27, 0x32, 0x77, 0x3a, 0xa0, 0x37, 0xd3, 0x65, 0x80, 0xad, 0x22, 0x7c, + 0xe5, 0x7b, 0x96, 0xf9, 0x3f, 0x58, 0x31, 0xe0, 0x07, 0xb4, 0x54, 0xec, 0x3f, 0x28, 0xc1, 0x58, + 0xfa, 0x32, 0x8a, 0x87, 0x70, 0xa4, 0xde, 0x0d, 0x35, 0x56, 0x6f, 0x9d, 0x5d, 0xb2, 0xc9, 0x5d, + 0xf2, 0xbc, 0xb4, 0xb5, 0x6c, 0xc4, 0x1a, 0xfe, 0x50, 0xd4, 0x58, 0xb6, 0xff, 0x9e, 0x05, 0x67, + 0xf9, 0x5b, 0x66, 0xe7, 0xe1, 0x5f, 0xcd, 0x1b, 0xdd, 0x57, 0x8b, 0xed, 0x60, 0xa6, 0x2c, 0xe0, + 0x7e, 0xe3, 0xcb, 0x2e, 0xf7, 0x13, 0xbd, 0x4d, 0x4f, 0x85, 0x87, 0xb0, 0xb3, 0x07, 0x9a, 0x0c, + 0xf6, 0x1f, 0x94, 0x41, 0xdf, 0x67, 0x88, 0x5c, 0x91, 0xe3, 0x58, 0x48, 0x79, 0xc4, 0x95, 0x1d, + 0xbf, 0xa1, 0x6f, 0x4e, 0xac, 0x66, 0x52, 0x1c, 0x7f, 0xce, 0x82, 0x61, 0xd7, 0x77, 0x13, 0xd7, + 0x61, 0xdb, 0xe8, 0x62, 0xee, 0x5a, 0x53, 0xec, 0xe6, 0x39, 0xe5, 0x20, 0x32, 0xcf, 0x71, 0x14, + 0x33, 0x6c, 0x72, 0x46, 0x1f, 0x16, 0xc1, 0xd3, 0xe5, 0xc2, 0xb2, 0x73, 0xab, 0x99, 0x88, 0xe9, + 0x90, 0x1a, 0x5e, 0x49, 0x54, 0x50, 0x52, 0x3b, 0xa6, 0xa4, 0x54, 0xa5, 0x5d, 0x7d, 0xb3, 0x34, + 0x6d, 0xc6, 0x9c, 0x91, 0x1d, 0x03, 0xea, 0x1e, 0x8b, 0x03, 0x06, 0xa6, 0x4e, 0x41, 0xcd, 0xe9, + 0x24, 0x41, 0x9b, 0x0e, 0x93, 0x38, 0x6a, 0xd2, 0xa1, 0xb7, 0x12, 0x80, 0x35, 0x8e, 0xfd, 0x66, + 0x05, 0x32, 0x49, 0x87, 0x68, 0xdb, 0xbc, 0x8b, 0xd3, 0x2a, 0xf6, 0x2e, 0x4e, 0xd5, 0x99, 0xbc, + 0xfb, 0x38, 0x51, 0x0b, 0x2a, 0xe1, 0x86, 0x13, 0x4b, 0xb3, 0xfa, 0x65, 0xb5, 0x8f, 0xa3, 0x8d, + 0xf7, 0x76, 0x27, 0x7e, 0xbc, 0x3f, 0xaf, 0x2b, 0x9d, 0xab, 0x53, 0xbc, 0x7c, 0x89, 0x66, 0xcd, + 0x68, 0x60, 0x4e, 0xff, 0x20, 0xb7, 0xcd, 0x7d, 0x42, 0x14, 0x96, 0xc7, 0x24, 0xee, 0x78, 0x89, + 0x98, 0x0d, 0x2f, 0x17, 0xb8, 0xca, 0x38, 0x61, 0x9d, 0x2e, 0xcf, 0xff, 0x63, 0x83, 0x29, 0xfa, + 0x20, 0xd4, 0xe2, 0xc4, 0x89, 0x92, 0x43, 0x26, 0xb8, 0xaa, 0x41, 0x5f, 0x91, 0x44, 0xb0, 0xa6, + 0x87, 0x5e, 0x61, 0xd5, 0x62, 0xdd, 0x78, 0xe3, 0x90, 0x39, 0x0f, 0xb2, 0xb2, 0xac, 0xa0, 0x80, + 0x0d, 0x6a, 0xe8, 0x22, 0x00, 0x9b, 0xdb, 0x3c, 0xd0, 0xaf, 0xca, 0xbc, 0x4c, 0x4a, 0x14, 0x62, + 0x05, 0xc1, 0x06, 0x96, 0xfd, 0x83, 0x90, 0xae, 0xf7, 0x80, 0x26, 0x64, 0x79, 0x09, 0xee, 0x85, + 0x66, 0xb9, 0x0b, 0xa9, 0x4a, 0x10, 0xbf, 0x6e, 0x81, 0x59, 0x94, 0x02, 0xbd, 0xce, 0xab, 0x5f, + 0x58, 0x45, 0x9c, 0x1c, 0x1a, 0x74, 0x27, 0x17, 0x9d, 0x30, 0x73, 0x84, 0x2d, 0x4b, 0x60, 0x9c, + 0x7f, 0x0f, 0x54, 0x25, 0xf4, 0x40, 0x46, 0xdd, 0xc7, 0xe0, 0x74, 0xf6, 0xa6, 0x72, 0x71, 0xea, + 0xb4, 0xbf, 0xeb, 0x47, 0xfa, 0x73, 0x4a, 0xbd, 0xfc, 0x39, 0x7d, 0xdc, 0xc8, 0xfa, 0x1b, 0x16, + 0x5c, 0xd8, 0xef, 0x42, 0x75, 0xf4, 0x18, 0x0c, 0xdc, 0x71, 0x22, 0x59, 0xc6, 0x9b, 0x09, 0xca, + 0x5b, 0x4e, 0xe4, 0x63, 0xd6, 0x8a, 0x76, 0x60, 0x90, 0x47, 0x83, 0x09, 0x6b, 0xfd, 0xe5, 0x62, + 0xaf, 0x77, 0xbf, 0x46, 0x8c, 0xed, 0x02, 0x8f, 0x44, 0xc3, 0x82, 0xa1, 0xfd, 0x6d, 0x0b, 0xd0, + 0xd2, 0x16, 0x89, 0x22, 0xb7, 0x69, 0xc4, 0xaf, 0xb1, 0x0b, 0x5a, 0x8c, 0x8b, 0x58, 0xcc, 0x14, + 0xd7, 0xcc, 0x05, 0x2d, 0xc6, 0xbf, 0xfc, 0x0b, 0x5a, 0x4a, 0x07, 0xbb, 0xa0, 0x05, 0x2d, 0xc1, + 0xd9, 0x36, 0xdf, 0x6e, 0xf0, 0x4b, 0x0f, 0xf8, 0xde, 0x43, 0x25, 0x94, 0x9d, 0xbb, 0xbb, 0x3b, + 0x71, 0x76, 0x31, 0x0f, 0x01, 0xe7, 0x3f, 0x67, 0xbf, 0x07, 0x10, 0x0f, 0x5b, 0x9b, 0xc9, 0x8b, + 0x41, 0xea, 0xe9, 0x7e, 0xb1, 0xbf, 0x5c, 0x81, 0x13, 0x99, 0x22, 0xaf, 0x74, 0xab, 0xd7, 0x1d, + 0xf4, 0x74, 0x64, 0xfd, 0xdd, 0xdd, 0xbd, 0xbe, 0xc2, 0xa8, 0x7c, 0xa8, 0xb8, 0x7e, 0xd8, 0x49, + 0x8a, 0xc9, 0x21, 0xe5, 0x9d, 0x98, 0xa7, 0x04, 0x0d, 0x77, 0x31, 0xfd, 0x8b, 0x39, 0x9b, 0x22, + 0x83, 0xb2, 0x52, 0xc6, 0xf8, 0xc0, 0x03, 0x72, 0x07, 0x7c, 0x42, 0x87, 0x48, 0x55, 0x8a, 0x70, + 0x2c, 0x66, 0x26, 0xcb, 0x71, 0x1f, 0xb5, 0xff, 0x5a, 0x09, 0x86, 0x8d, 0x8f, 0x86, 0x7e, 0x29, + 0x5d, 0xb2, 0xc9, 0x2a, 0xee, 0x95, 0x18, 0xfd, 0x49, 0x5d, 0x94, 0x89, 0xbf, 0xd2, 0x53, 0xdd, + 0xd5, 0x9a, 0xee, 0xed, 0x4e, 0x9c, 0xcc, 0xd4, 0x63, 0x4a, 0x55, 0x70, 0x3a, 0xff, 0x51, 0x38, + 0x91, 0x21, 0x93, 0xf3, 0xca, 0xab, 0xe9, 0x8b, 0xe8, 0x8f, 0xe8, 0x96, 0x32, 0x87, 0xec, 0xeb, + 0x74, 0xc8, 0x44, 0x1a, 0x5d, 0xe0, 0x91, 0x3e, 0x7c, 0xb0, 0x99, 0x6c, 0xd9, 0x52, 0x9f, 0xd9, + 0xb2, 0x4f, 0x43, 0x35, 0x0c, 0x3c, 0xb7, 0xe1, 0xaa, 0xba, 0x86, 0x2c, 0x3f, 0x77, 0x59, 0xb4, + 0x61, 0x05, 0x45, 0x77, 0xa0, 0xa6, 0xee, 0xec, 0x17, 0xfe, 0xed, 0xa2, 0x0e, 0x7d, 0x94, 0xd1, + 0xa2, 0xef, 0xe2, 0xd7, 0xbc, 0x90, 0x0d, 0x83, 0x4c, 0x09, 0xca, 0xd0, 0x7f, 0xe6, 0x7b, 0x67, + 0xda, 0x31, 0xc6, 0x02, 0x62, 0x7f, 0xad, 0x06, 0x67, 0xf2, 0x2a, 0x6d, 0xa3, 0x8f, 0xc0, 0x20, + 0xef, 0x63, 0x31, 0x97, 0x39, 0xe4, 0xf1, 0x98, 0x63, 0x04, 0x45, 0xb7, 0xd8, 0x6f, 0x2c, 0x78, + 0x0a, 0xee, 0x9e, 0xb3, 0x26, 0x66, 0xc8, 0xf1, 0x70, 0x5f, 0x70, 0x34, 0xf7, 0x05, 0x87, 0x73, + 0xf7, 0x9c, 0x35, 0xb4, 0x0d, 0x95, 0x96, 0x9b, 0x10, 0x47, 0x38, 0x11, 0x6e, 0x1d, 0x0b, 0x73, + 0xe2, 0x70, 0x2b, 0x8d, 0xfd, 0xc4, 0x9c, 0x21, 0xfa, 0xaa, 0x05, 0x27, 0xd6, 0xd2, 0xa9, 0xf1, + 0x42, 0x78, 0x3a, 0xc7, 0x50, 0x4d, 0x3d, 0xcd, 0x88, 0xdf, 0x50, 0x94, 0x69, 0xc4, 0xd9, 0xee, + 0xa0, 0x4f, 0x5a, 0x30, 0xb4, 0xee, 0x7a, 0x46, 0x61, 0xdd, 0x63, 0xf8, 0x38, 0x97, 0x19, 0x03, + 0xbd, 0xe3, 0xe0, 0xff, 0x63, 0x2c, 0x39, 0xf7, 0xd2, 0x54, 0x83, 0x47, 0xd5, 0x54, 0x43, 0x0f, + 0x48, 0x53, 0x7d, 0xda, 0x82, 0x9a, 0x1a, 0x69, 0x91, 0xee, 0xfc, 0xc1, 0x63, 0xfc, 0xe4, 0xdc, + 0x73, 0xa2, 0xfe, 0x62, 0xcd, 0x1c, 0x7d, 0xc1, 0x82, 0x61, 0xe7, 0x8d, 0x4e, 0x44, 0x9a, 0x64, + 0x2b, 0x08, 0x63, 0x71, 0xbd, 0xe1, 0xab, 0xc5, 0x77, 0x66, 0x9a, 0x32, 0x99, 0x25, 0x5b, 0x4b, + 0x61, 0x2c, 0xd2, 0x92, 0x74, 0x03, 0x36, 0xbb, 0x60, 0xef, 0x96, 0x60, 0x62, 0x1f, 0x0a, 0xe8, + 0x45, 0x18, 0x09, 0xa2, 0x96, 0xe3, 0xbb, 0x6f, 0x98, 0xb5, 0x2e, 0x94, 0x95, 0xb5, 0x64, 0xc0, + 0x70, 0x0a, 0xd3, 0x4c, 0xc8, 0x2e, 0xed, 0x93, 0x90, 0x7d, 0x01, 0x06, 0x22, 0x12, 0x06, 0xd9, + 0xcd, 0x02, 0x4b, 0x09, 0x60, 0x10, 0xf4, 0x38, 0x94, 0x9d, 0xd0, 0x15, 0x81, 0x68, 0x6a, 0x0f, + 0x34, 0xbd, 0x3c, 0x8f, 0x69, 0x7b, 0xaa, 0x3e, 0x44, 0xe5, 0xbe, 0xd4, 0x87, 0xa0, 0x6a, 0x40, + 0x9c, 0x5d, 0x0c, 0x6a, 0x35, 0x90, 0x3e, 0x53, 0xb0, 0xdf, 0x2a, 0xc3, 0xe3, 0x7b, 0xce, 0x17, + 0x1d, 0x87, 0x67, 0xed, 0x11, 0x87, 0x27, 0x87, 0xa7, 0xb4, 0xdf, 0xf0, 0x94, 0x7b, 0x0c, 0xcf, + 0x27, 0xe9, 0x32, 0x90, 0x35, 0x42, 0x8a, 0xb9, 0xa0, 0xae, 0x57, 0xc9, 0x11, 0xb1, 0x02, 0x24, + 0x14, 0x6b, 0xbe, 0x74, 0x0f, 0x90, 0x4a, 0x46, 0xae, 0x14, 0xa1, 0x06, 0x7a, 0xd6, 0x0c, 0xe1, + 0x73, 0xbf, 0x57, 0x86, 0xb3, 0xfd, 0xf3, 0x25, 0x78, 0xb2, 0x0f, 0xe9, 0x6d, 0xce, 0x62, 0xab, + 0xcf, 0x59, 0xfc, 0xdd, 0xfd, 0x99, 0xec, 0xbf, 0x66, 0xc1, 0xf9, 0xde, 0xca, 0x03, 0x3d, 0x07, + 0xc3, 0x6b, 0x91, 0xe3, 0x37, 0x36, 0xd8, 0xa5, 0x9b, 0x72, 0x50, 0xd8, 0x58, 0xeb, 0x66, 0x6c, + 0xe2, 0xd0, 0xed, 0x2d, 0x8f, 0x49, 0x30, 0x30, 0x64, 0xf2, 0x28, 0xdd, 0xde, 0xae, 0x66, 0x81, + 0xb8, 0x1b, 0xdf, 0xfe, 0xb3, 0x52, 0x7e, 0xb7, 0xb8, 0x91, 0x71, 0x90, 0xef, 0x24, 0xbe, 0x42, + 0xa9, 0x0f, 0x59, 0x52, 0xbe, 0xdf, 0xb2, 0x64, 0xa0, 0x97, 0x2c, 0x41, 0xb3, 0x70, 0xd2, 0xb8, + 0x94, 0x85, 0x27, 0x04, 0xf3, 0x80, 0x5b, 0x55, 0x25, 0x63, 0x39, 0x03, 0xc7, 0x5d, 0x4f, 0xa0, + 0x67, 0xa0, 0xea, 0xfa, 0x31, 0x69, 0x74, 0x22, 0x1e, 0xe8, 0x6d, 0x24, 0x61, 0xcd, 0x8b, 0x76, + 0xac, 0x30, 0xec, 0x5f, 0x2e, 0xc1, 0xb9, 0x9e, 0x76, 0xd6, 0x7d, 0x92, 0x5d, 0xe6, 0xe7, 0x18, + 0xb8, 0x3f, 0x9f, 0xc3, 0x1c, 0xa4, 0xca, 0xbe, 0x83, 0xf4, 0x87, 0xbd, 0x27, 0x26, 0xb5, 0xb9, + 0xbf, 0x67, 0x47, 0xe9, 0x25, 0x18, 0x75, 0xc2, 0x90, 0xe3, 0xb1, 0x78, 0xcd, 0x4c, 0x95, 0x9c, + 0x69, 0x13, 0x88, 0xd3, 0xb8, 0x7d, 0x69, 0xcf, 0x3f, 0xb6, 0xa0, 0x86, 0xc9, 0x3a, 0x97, 0x0e, + 0xe8, 0xb6, 0x18, 0x22, 0xab, 0x88, 0x7a, 0x9a, 0x74, 0x60, 0x63, 0x97, 0xd5, 0x99, 0xcc, 0x1b, + 0xec, 0xee, 0xcb, 0x7b, 0x4a, 0x07, 0xba, 0xbc, 0x47, 0x5d, 0xdf, 0x52, 0xee, 0x7d, 0x7d, 0x8b, + 0xfd, 0xf5, 0x21, 0xfa, 0x7a, 0x61, 0x30, 0x13, 0x91, 0x66, 0x4c, 0xbf, 0x6f, 0x27, 0xf2, 0xc4, + 0x24, 0x51, 0xdf, 0xf7, 0x06, 0x5e, 0xc0, 0xb4, 0x3d, 0x75, 0x14, 0x53, 0x3a, 0x50, 0x8d, 0x90, + 0xf2, 0xbe, 0x35, 0x42, 0x5e, 0x82, 0xd1, 0x38, 0xde, 0x58, 0x8e, 0xdc, 0x2d, 0x27, 0x21, 0xd7, + 0xc8, 0x8e, 0xb0, 0xb2, 0x74, 0x5e, 0xff, 0xca, 0x15, 0x0d, 0xc4, 0x69, 0x5c, 0x34, 0x07, 0xa7, + 0x74, 0xa5, 0x0e, 0x12, 0x25, 0x2c, 0xba, 0x9f, 0xcf, 0x04, 0x95, 0xc4, 0xab, 0x6b, 0x7b, 0x08, + 0x04, 0xdc, 0xfd, 0x0c, 0x95, 0x6f, 0xa9, 0x46, 0xda, 0x91, 0xc1, 0xb4, 0x7c, 0x4b, 0xd1, 0xa1, + 0x7d, 0xe9, 0x7a, 0x02, 0x2d, 0xc2, 0x69, 0x3e, 0x31, 0xa6, 0xc3, 0xd0, 0x78, 0xa3, 0xa1, 0x74, + 0x1d, 0xc3, 0xb9, 0x6e, 0x14, 0x9c, 0xf7, 0x1c, 0x7a, 0x01, 0x86, 0x55, 0xf3, 0xfc, 0xac, 0x38, + 0x45, 0x50, 0x5e, 0x0c, 0x45, 0x66, 0xbe, 0x89, 0x4d, 0x3c, 0xf4, 0x01, 0x78, 0x54, 0xff, 0xe5, + 0x29, 0x60, 0xfc, 0x68, 0x6d, 0x56, 0x14, 0x41, 0x52, 0x97, 0x85, 0xcc, 0xe5, 0xa2, 0x35, 0x71, + 0xaf, 0xe7, 0xd1, 0x1a, 0x9c, 0x57, 0xa0, 0x4b, 0x7e, 0xc2, 0xf2, 0x39, 0x62, 0x52, 0x77, 0x62, + 0x72, 0x23, 0xf2, 0xc4, 0x6d, 0xab, 0xea, 0x1e, 0xc7, 0x39, 0x37, 0xb9, 0x92, 0x87, 0x89, 0x17, + 0xf0, 0x1e, 0x54, 0xd0, 0x14, 0xd4, 0x88, 0xef, 0xac, 0x79, 0x64, 0x69, 0x66, 0x9e, 0x15, 0x53, + 0x32, 0x4e, 0xf2, 0x2e, 0x49, 0x00, 0xd6, 0x38, 0x2a, 0xc2, 0x74, 0xa4, 0xe7, 0x9d, 0xa2, 0xcb, + 0x70, 0xa6, 0xd5, 0x08, 0xa9, 0xed, 0xe1, 0x36, 0xc8, 0x74, 0x83, 0x05, 0xd4, 0xd1, 0x0f, 0xc3, + 0x0b, 0x4c, 0xaa, 0xf0, 0xe9, 0xb9, 0x99, 0xe5, 0x2e, 0x1c, 0x9c, 0xfb, 0x24, 0x0b, 0xbc, 0x8c, + 0x82, 0xed, 0x9d, 0xf1, 0xd3, 0x99, 0xc0, 0x4b, 0xda, 0x88, 0x39, 0x0c, 0x5d, 0x05, 0xc4, 0x62, + 0xf1, 0xaf, 0x24, 0x49, 0xa8, 0x8c, 0x9d, 0xf1, 0x33, 0xec, 0x95, 0x54, 0x18, 0xd9, 0xe5, 0x2e, + 0x0c, 0x9c, 0xf3, 0x94, 0xfd, 0x1f, 0x2c, 0x18, 0x55, 0xeb, 0xf5, 0x3e, 0x64, 0xa3, 0x78, 0xe9, + 0x6c, 0x94, 0xb9, 0xa3, 0x4b, 0x3c, 0xd6, 0xf3, 0x1e, 0x21, 0xcd, 0x3f, 0x33, 0x0c, 0xa0, 0xa5, + 0xa2, 0x52, 0x48, 0x56, 0x4f, 0x85, 0xf4, 0xd0, 0x4a, 0xa4, 0xbc, 0xca, 0x29, 0x95, 0x07, 0x5b, + 0x39, 0x65, 0x05, 0xce, 0x4a, 0x73, 0x81, 0x9f, 0x15, 0x5d, 0x09, 0x62, 0x25, 0xe0, 0xaa, 0xf5, + 0xc7, 0x05, 0xa1, 0xb3, 0xf3, 0x79, 0x48, 0x38, 0xff, 0xd9, 0x94, 0x95, 0x32, 0xb4, 0x9f, 0x95, + 0xa2, 0xd7, 0xf4, 0xc2, 0xba, 0xbc, 0x15, 0x24, 0xb3, 0xa6, 0x17, 0x2e, 0xaf, 0x60, 0x8d, 0x93, + 0x2f, 0xd8, 0x6b, 0x05, 0x09, 0x76, 0x38, 0xb0, 0x60, 0x97, 0x22, 0x66, 0xb8, 0xa7, 0x88, 0x91, + 0x3e, 0xe9, 0x91, 0x9e, 0x3e, 0xe9, 0xf7, 0xc2, 0x98, 0xeb, 0x6f, 0x90, 0xc8, 0x4d, 0x48, 0x93, + 0xad, 0x05, 0x26, 0x7e, 0xaa, 0x5a, 0xad, 0xcf, 0xa7, 0xa0, 0x38, 0x83, 0x9d, 0x96, 0x8b, 0x63, + 0x7d, 0xc8, 0xc5, 0x1e, 0xda, 0xe8, 0x44, 0x31, 0xda, 0xe8, 0xe4, 0xd1, 0xb5, 0xd1, 0xa9, 0x63, + 0xd5, 0x46, 0xa8, 0x10, 0x6d, 0xd4, 0x97, 0xa0, 0x37, 0xb6, 0x7f, 0x67, 0xf6, 0xd9, 0xfe, 0xf5, + 0x52, 0x45, 0x67, 0x0f, 0xad, 0x8a, 0xf2, 0xb5, 0xcc, 0x23, 0x87, 0xd2, 0x32, 0x9f, 0x2e, 0xc1, + 0x59, 0x2d, 0x87, 0xe9, 0xec, 0x77, 0xd7, 0xa9, 0x24, 0x62, 0x17, 0x4b, 0xf1, 0x73, 0x1b, 0x23, + 0x39, 0x4a, 0xe7, 0x59, 0x29, 0x08, 0x36, 0xb0, 0x58, 0x8e, 0x11, 0x89, 0x58, 0x19, 0xdd, 0xac, + 0x90, 0x9e, 0x11, 0xed, 0x58, 0x61, 0xd0, 0xf9, 0x45, 0x7f, 0x8b, 0xbc, 0xcd, 0x6c, 0xb1, 0xb8, + 0x19, 0x0d, 0xc2, 0x26, 0x1e, 0x7a, 0x9a, 0x33, 0x61, 0x02, 0x82, 0x0a, 0xea, 0x11, 0x71, 0xd3, + 0xac, 0x94, 0x09, 0x0a, 0x2a, 0xbb, 0xc3, 0x92, 0xc9, 0x2a, 0xdd, 0xdd, 0x61, 0x21, 0x50, 0x0a, + 0xc3, 0xfe, 0x9f, 0x16, 0x9c, 0xcb, 0x1d, 0x8a, 0xfb, 0xa0, 0x7c, 0xb7, 0xd3, 0xca, 0x77, 0xa5, + 0xa8, 0xed, 0x86, 0xf1, 0x16, 0x3d, 0x14, 0xf1, 0xbf, 0xb3, 0x60, 0x4c, 0xe3, 0xdf, 0x87, 0x57, + 0x75, 0xd3, 0xaf, 0x5a, 0xdc, 0xce, 0xaa, 0xd6, 0xf5, 0x6e, 0xbf, 0x53, 0x02, 0x55, 0xc0, 0x71, + 0xba, 0x21, 0xcb, 0xe3, 0xee, 0x73, 0x92, 0xb8, 0x03, 0x83, 0xec, 0x20, 0x34, 0x2e, 0x26, 0xc8, + 0x23, 0xcd, 0x9f, 0x1d, 0xaa, 0xea, 0x43, 0x66, 0xf6, 0x37, 0xc6, 0x82, 0x21, 0x2b, 0xf2, 0xec, + 0xc6, 0x54, 0x9a, 0x37, 0x45, 0x5a, 0x96, 0x2e, 0xf2, 0x2c, 0xda, 0xb1, 0xc2, 0xa0, 0xea, 0xc1, + 0x6d, 0x04, 0xfe, 0x8c, 0xe7, 0xc4, 0xf2, 0x36, 0x45, 0xa5, 0x1e, 0xe6, 0x25, 0x00, 0x6b, 0x1c, + 0x76, 0x46, 0xea, 0xc6, 0xa1, 0xe7, 0xec, 0x18, 0xfb, 0x67, 0xa3, 0x3e, 0x81, 0x02, 0x61, 0x13, + 0xcf, 0x6e, 0xc3, 0x78, 0xfa, 0x25, 0x66, 0xc9, 0x3a, 0x0b, 0x50, 0xec, 0x6b, 0x38, 0xa7, 0xa0, + 0xe6, 0xb0, 0xa7, 0x16, 0x3a, 0x4e, 0xf6, 0x12, 0xf4, 0x69, 0x09, 0xc0, 0x1a, 0xc7, 0xfe, 0x55, + 0x0b, 0x4e, 0xe7, 0x0c, 0x5a, 0x81, 0x69, 0x6f, 0x89, 0x96, 0x36, 0x79, 0x8a, 0xfd, 0x07, 0x60, + 0xa8, 0x49, 0xd6, 0x1d, 0x19, 0x02, 0x67, 0xc8, 0xf6, 0x59, 0xde, 0x8c, 0x25, 0xdc, 0xfe, 0xef, + 0x16, 0x9c, 0x48, 0xf7, 0x35, 0x66, 0xa9, 0x24, 0x7c, 0x98, 0xdc, 0xb8, 0x11, 0x6c, 0x91, 0x68, + 0x87, 0xbe, 0xb9, 0x95, 0x49, 0x25, 0xe9, 0xc2, 0xc0, 0x39, 0x4f, 0xb1, 0xf2, 0xad, 0x4d, 0x35, + 0xda, 0x72, 0x46, 0xde, 0x2c, 0x72, 0x46, 0xea, 0x8f, 0x69, 0x1e, 0x97, 0x2b, 0x96, 0xd8, 0xe4, + 0x6f, 0x7f, 0x7b, 0x00, 0x54, 0x5e, 0x2c, 0x8b, 0x3f, 0x2a, 0x28, 0x7a, 0xeb, 0xa0, 0x19, 0x44, + 0x6a, 0x32, 0x0c, 0xec, 0x15, 0x10, 0xc0, 0xbd, 0x24, 0xa6, 0xeb, 0x52, 0xbd, 0xe1, 0xaa, 0x06, + 0x61, 0x13, 0x8f, 0xf6, 0xc4, 0x73, 0xb7, 0x08, 0x7f, 0x68, 0x30, 0xdd, 0x93, 0x05, 0x09, 0xc0, + 0x1a, 0x87, 0xf6, 0xa4, 0xe9, 0xae, 0xaf, 0x8b, 0x2d, 0xbf, 0xea, 0x09, 0x1d, 0x1d, 0xcc, 0x20, + 0xbc, 0x22, 0x77, 0xb0, 0x29, 0xac, 0x60, 0xa3, 0x22, 0x77, 0xb0, 0x89, 0x19, 0x84, 0xda, 0x6d, + 0x7e, 0x10, 0xb5, 0xd9, 0x25, 0xf5, 0x4d, 0xc5, 0x45, 0x58, 0xbf, 0xca, 0x6e, 0xbb, 0xde, 0x8d, + 0x82, 0xf3, 0x9e, 0xa3, 0x33, 0x30, 0x8c, 0x48, 0xd3, 0x6d, 0x24, 0x26, 0x35, 0x48, 0xcf, 0xc0, + 0xe5, 0x2e, 0x0c, 0x9c, 0xf3, 0x14, 0x9a, 0x86, 0x13, 0x32, 0xaf, 0x59, 0x56, 0xad, 0x19, 0x4e, + 0x57, 0xc9, 0xc0, 0x69, 0x30, 0xce, 0xe2, 0x53, 0xa9, 0xd6, 0x16, 0x05, 0xab, 0x98, 0xb1, 0x6c, + 0x48, 0x35, 0x59, 0xc8, 0x0a, 0x2b, 0x0c, 0xfb, 0x13, 0x65, 0xaa, 0x85, 0x7b, 0x14, 0x6a, 0xbb, + 0x6f, 0xd1, 0x82, 0xe9, 0x19, 0x39, 0xd0, 0xc7, 0x8c, 0x7c, 0x1e, 0x46, 0x6e, 0xc7, 0x81, 0xaf, + 0x22, 0xf1, 0x2a, 0x3d, 0x23, 0xf1, 0x0c, 0xac, 0xfc, 0x48, 0xbc, 0xc1, 0xa2, 0x22, 0xf1, 0x86, + 0x0e, 0x19, 0x89, 0xf7, 0xcd, 0x0a, 0xa8, 0xab, 0x41, 0xae, 0x93, 0xe4, 0x4e, 0x10, 0x6d, 0xba, + 0x7e, 0x8b, 0xe5, 0x83, 0x7f, 0xd5, 0x82, 0x11, 0xbe, 0x5e, 0x16, 0xcc, 0x4c, 0xaa, 0xf5, 0x82, + 0xee, 0x9c, 0x48, 0x31, 0x9b, 0x5c, 0x35, 0x18, 0x65, 0x2e, 0xf3, 0x34, 0x41, 0x38, 0xd5, 0x23, + 0xf4, 0x51, 0x00, 0xe9, 0x1f, 0x5d, 0x97, 0x22, 0x73, 0xbe, 0x98, 0xfe, 0x61, 0xb2, 0xae, 0x6d, + 0xe0, 0x55, 0xc5, 0x04, 0x1b, 0x0c, 0xd1, 0xa7, 0x75, 0x96, 0x19, 0x0f, 0xd9, 0xff, 0xf0, 0xb1, + 0x8c, 0x4d, 0x3f, 0x39, 0x66, 0x18, 0x86, 0x5c, 0xbf, 0x45, 0xe7, 0x89, 0x88, 0x58, 0x7a, 0x57, + 0x5e, 0x2d, 0x85, 0x85, 0xc0, 0x69, 0xd6, 0x1d, 0xcf, 0xf1, 0x1b, 0x24, 0x9a, 0xe7, 0xe8, 0xe6, + 0x15, 0xd6, 0xac, 0x01, 0x4b, 0x42, 0x5d, 0x97, 0xaa, 0x54, 0xfa, 0xb9, 0x54, 0xe5, 0xfc, 0xfb, + 0xe0, 0x54, 0xd7, 0xc7, 0x3c, 0x50, 0x4a, 0xd9, 0xe1, 0xb3, 0xd1, 0xec, 0x7f, 0x31, 0xa8, 0x95, + 0xd6, 0xf5, 0xa0, 0xc9, 0xaf, 0xf6, 0x88, 0xf4, 0x17, 0x15, 0x36, 0x6e, 0x81, 0x53, 0xc4, 0xb8, + 0x06, 0x5b, 0x35, 0x62, 0x93, 0x25, 0x9d, 0xa3, 0xa1, 0x13, 0x11, 0xff, 0xb8, 0xe7, 0xe8, 0xb2, + 0x62, 0x82, 0x0d, 0x86, 0x68, 0x23, 0x95, 0x53, 0x72, 0xf9, 0xe8, 0x39, 0x25, 0xac, 0xca, 0x54, + 0x5e, 0x35, 0xfe, 0x2f, 0x58, 0x30, 0xe6, 0xa7, 0x66, 0x6e, 0x31, 0x61, 0xa4, 0xf9, 0xab, 0x82, + 0xdf, 0x2c, 0x95, 0x6e, 0xc3, 0x19, 0xfe, 0x79, 0x2a, 0xad, 0x72, 0x40, 0x95, 0xa6, 0xef, 0x08, + 0x1a, 0xec, 0x75, 0x47, 0x10, 0xf2, 0xd5, 0x25, 0x69, 0x43, 0x85, 0x5f, 0x92, 0x06, 0x39, 0x17, + 0xa4, 0xdd, 0x82, 0x5a, 0x23, 0x22, 0x4e, 0x72, 0xc8, 0xfb, 0xb2, 0xd8, 0x01, 0xfd, 0x8c, 0x24, + 0x80, 0x35, 0x2d, 0xfb, 0xff, 0x0c, 0xc0, 0x49, 0x39, 0x22, 0x32, 0x04, 0x9d, 0xea, 0x47, 0xce, + 0x57, 0x1b, 0xb7, 0x4a, 0x3f, 0x5e, 0x91, 0x00, 0xac, 0x71, 0xa8, 0x3d, 0xd6, 0x89, 0xc9, 0x52, + 0x48, 0xfc, 0x05, 0x77, 0x2d, 0x16, 0xe7, 0x9c, 0x6a, 0xa1, 0xdc, 0xd0, 0x20, 0x6c, 0xe2, 0x51, + 0x63, 0x9c, 0xdb, 0xc5, 0x71, 0x36, 0x7d, 0x45, 0xd8, 0xdb, 0x58, 0xc2, 0xd1, 0x2f, 0xe4, 0x56, + 0x8e, 0x2d, 0x26, 0x71, 0xab, 0x2b, 0xf2, 0xfe, 0x80, 0x57, 0x2c, 0xfe, 0x6d, 0x0b, 0xce, 0xf2, + 0x56, 0x39, 0x92, 0x37, 0xc2, 0xa6, 0x93, 0x90, 0xb8, 0x98, 0x4a, 0xee, 0x39, 0xfd, 0xd3, 0x4e, + 0xde, 0x3c, 0xb6, 0x38, 0xbf, 0x37, 0xe8, 0x4d, 0x0b, 0x4e, 0x6c, 0xa6, 0x6a, 0x7e, 0x48, 0xd5, + 0x71, 0xd4, 0x74, 0xfc, 0x14, 0x51, 0xbd, 0xd4, 0xd2, 0xed, 0x31, 0xce, 0x72, 0xb7, 0xff, 0xcc, + 0x02, 0x53, 0x8c, 0xde, 0xff, 0x52, 0x21, 0x07, 0x37, 0x05, 0xa5, 0x75, 0x59, 0xe9, 0x69, 0x5d, + 0x3e, 0x0e, 0xe5, 0x8e, 0xdb, 0x14, 0xfb, 0x0b, 0x7d, 0xfa, 0x3a, 0x3f, 0x8b, 0x69, 0xbb, 0xfd, + 0x4f, 0x2b, 0xda, 0x6f, 0x21, 0xf2, 0xa2, 0xbe, 0x27, 0x5e, 0x7b, 0x5d, 0x15, 0x1b, 0xe3, 0x6f, + 0x7e, 0xbd, 0xab, 0xd8, 0xd8, 0x8f, 0x1e, 0x3c, 0xed, 0x8d, 0x0f, 0x50, 0xaf, 0x5a, 0x63, 0x43, + 0xfb, 0xe4, 0xbc, 0xdd, 0x86, 0x2a, 0xdd, 0x82, 0x31, 0x07, 0x64, 0x35, 0xd5, 0xa9, 0xea, 0x15, + 0xd1, 0x7e, 0x6f, 0x77, 0xe2, 0x47, 0x0e, 0xde, 0x2d, 0xf9, 0x34, 0x56, 0xf4, 0x51, 0x0c, 0x35, + 0xfa, 0x9b, 0xa5, 0xe7, 0x89, 0xcd, 0xdd, 0x0d, 0x25, 0x33, 0x25, 0xa0, 0x90, 0xdc, 0x3f, 0xcd, + 0x07, 0xf9, 0x50, 0x63, 0xb7, 0xd1, 0x32, 0xa6, 0x7c, 0x0f, 0xb8, 0xac, 0x92, 0xe4, 0x24, 0xe0, + 0xde, 0xee, 0xc4, 0x4b, 0x07, 0x67, 0xaa, 0x1e, 0xc7, 0x9a, 0x85, 0xfd, 0xc5, 0x01, 0x3d, 0x77, + 0x45, 0x8d, 0xb9, 0xef, 0x89, 0xb9, 0xfb, 0x62, 0x66, 0xee, 0x5e, 0xe8, 0x9a, 0xbb, 0x63, 0xfa, + 0xd6, 0xd4, 0xd4, 0x6c, 0xbc, 0xdf, 0x86, 0xc0, 0xfe, 0xfe, 0x06, 0x66, 0x01, 0xbd, 0xde, 0x71, + 0x23, 0x12, 0x2f, 0x47, 0x1d, 0xdf, 0xf5, 0x5b, 0x6c, 0x3a, 0x56, 0x4d, 0x0b, 0x28, 0x05, 0xc6, + 0x59, 0x7c, 0xba, 0xa9, 0xa7, 0xdf, 0xfc, 0x96, 0xb3, 0xc5, 0x67, 0x95, 0x51, 0x76, 0x6b, 0x45, + 0xb4, 0x63, 0x85, 0x61, 0x7f, 0x9d, 0x9d, 0x65, 0x1b, 0x79, 0xc1, 0x74, 0x4e, 0x78, 0xec, 0xfa, + 0x5f, 0x5e, 0xb3, 0x4b, 0xcd, 0x09, 0x7e, 0xe7, 0x2f, 0x87, 0xa1, 0x3b, 0x30, 0xb4, 0xc6, 0xef, + 0xbf, 0x2b, 0xa6, 0x3e, 0xb9, 0xb8, 0x4c, 0x8f, 0xdd, 0x72, 0x22, 0x6f, 0xd6, 0xbb, 0xa7, 0x7f, + 0x62, 0xc9, 0xcd, 0xfe, 0xfd, 0x0a, 0x9c, 0xc8, 0x5c, 0x10, 0x9b, 0xaa, 0x96, 0x5a, 0xda, 0xb7, + 0x5a, 0xea, 0x87, 0x00, 0x9a, 0x24, 0xf4, 0x82, 0x1d, 0x66, 0x8e, 0x0d, 0x1c, 0xd8, 0x1c, 0x53, + 0x16, 0xfc, 0xac, 0xa2, 0x82, 0x0d, 0x8a, 0xa2, 0x50, 0x19, 0x2f, 0xbe, 0x9a, 0x29, 0x54, 0x66, + 0xdc, 0x62, 0x30, 0x78, 0x7f, 0x6f, 0x31, 0x70, 0xe1, 0x04, 0xef, 0xa2, 0xca, 0xbe, 0x3d, 0x44, + 0x92, 0x2d, 0xcb, 0x5f, 0x98, 0x4d, 0x93, 0xc1, 0x59, 0xba, 0x0f, 0xf2, 0xfe, 0x67, 0xf4, 0x6e, + 0xa8, 0xc9, 0xef, 0x1c, 0x8f, 0xd7, 0x74, 0x05, 0x03, 0x39, 0x0d, 0xd8, 0xbd, 0xcc, 0xe2, 0x67, + 0x57, 0x21, 0x01, 0x78, 0x50, 0x85, 0x04, 0xec, 0xcf, 0x97, 0xa8, 0x1d, 0xcf, 0xfb, 0xa5, 0x6a, + 0xe2, 0x3c, 0x05, 0x83, 0x4e, 0x27, 0xd9, 0x08, 0xba, 0x6e, 0xf3, 0x9b, 0x66, 0xad, 0x58, 0x40, + 0xd1, 0x02, 0x0c, 0x34, 0x75, 0x9d, 0x93, 0x83, 0x7c, 0x4f, 0xed, 0x12, 0x75, 0x12, 0x82, 0x19, + 0x15, 0xf4, 0x18, 0x0c, 0x24, 0x4e, 0x4b, 0xa6, 0x5c, 0xb1, 0x34, 0xdb, 0x55, 0xa7, 0x15, 0x63, + 0xd6, 0x6a, 0xaa, 0xef, 0x81, 0x7d, 0xd4, 0xf7, 0x4b, 0x30, 0x1a, 0xbb, 0x2d, 0xdf, 0x49, 0x3a, + 0x11, 0x31, 0x8e, 0xf9, 0x74, 0xe4, 0x86, 0x09, 0xc4, 0x69, 0x5c, 0xfb, 0x37, 0x47, 0xe0, 0xcc, + 0xca, 0xcc, 0xa2, 0xac, 0xde, 0x7d, 0x6c, 0x59, 0x53, 0x79, 0x3c, 0xee, 0x5f, 0xd6, 0x54, 0x0f, + 0xee, 0x9e, 0x91, 0x35, 0xe5, 0x19, 0x59, 0x53, 0xe9, 0x14, 0x96, 0x72, 0x11, 0x29, 0x2c, 0x79, + 0x3d, 0xe8, 0x27, 0x85, 0xe5, 0xd8, 0xd2, 0xa8, 0xf6, 0xec, 0xd0, 0x81, 0xd2, 0xa8, 0x54, 0x8e, + 0x59, 0x21, 0xc9, 0x05, 0x3d, 0x3e, 0x55, 0x6e, 0x8e, 0x99, 0xca, 0xef, 0xe1, 0x89, 0x33, 0x42, + 0xd4, 0xbf, 0x5a, 0x7c, 0x07, 0xfa, 0xc8, 0xef, 0x11, 0xb9, 0x3b, 0x66, 0x4e, 0xd9, 0x50, 0x11, + 0x39, 0x65, 0x79, 0xdd, 0xd9, 0x37, 0xa7, 0xec, 0x25, 0x18, 0x6d, 0x78, 0x81, 0x4f, 0x96, 0xa3, + 0x20, 0x09, 0x1a, 0x81, 0x27, 0xcc, 0x7a, 0x25, 0x12, 0x66, 0x4c, 0x20, 0x4e, 0xe3, 0xf6, 0x4a, + 0x48, 0xab, 0x1d, 0x35, 0x21, 0x0d, 0x1e, 0x50, 0x42, 0xda, 0xcf, 0xea, 0xd4, 0xe9, 0x61, 0xf6, + 0x45, 0x3e, 0x54, 0xfc, 0x17, 0xe9, 0x27, 0x7f, 0x1a, 0xbd, 0xc5, 0xaf, 0xd3, 0xa3, 0x86, 0xf1, + 0x4c, 0xd0, 0xa6, 0x86, 0xdf, 0x08, 0x1b, 0x92, 0xd7, 0x8e, 0x61, 0xc2, 0xde, 0x5a, 0xd1, 0x6c, + 0xd4, 0x15, 0x7b, 0xba, 0x09, 0xa7, 0x3b, 0x72, 0x94, 0xd4, 0xee, 0x2f, 0x97, 0xe0, 0xfb, 0xf6, + 0xed, 0x02, 0xba, 0x03, 0x90, 0x38, 0x2d, 0x31, 0x51, 0xc5, 0x81, 0xc9, 0x11, 0xc3, 0x2b, 0x57, + 0x25, 0x3d, 0x5e, 0x93, 0x44, 0xfd, 0x65, 0x47, 0x11, 0xf2, 0x37, 0x8b, 0xaa, 0x0c, 0xbc, 0xae, + 0xd2, 0x8d, 0x38, 0xf0, 0x08, 0x66, 0x10, 0xaa, 0xfe, 0x23, 0xd2, 0xd2, 0xf7, 0x3f, 0xab, 0xcf, + 0x87, 0x59, 0x2b, 0x16, 0x50, 0xf4, 0x02, 0x0c, 0x3b, 0x9e, 0xc7, 0xf3, 0x63, 0x48, 0x2c, 0xee, + 0xd3, 0xd1, 0x35, 0xe4, 0x34, 0x08, 0x9b, 0x78, 0xf6, 0x9f, 0x96, 0x60, 0x62, 0x1f, 0x99, 0xd2, + 0x95, 0xf1, 0x57, 0xe9, 0x3b, 0xe3, 0x4f, 0xe4, 0x28, 0x0c, 0xf6, 0xc8, 0x51, 0x78, 0x01, 0x86, + 0x13, 0xe2, 0xb4, 0x45, 0x40, 0x96, 0xf0, 0x04, 0xe8, 0x13, 0x60, 0x0d, 0xc2, 0x26, 0x1e, 0x95, + 0x62, 0x63, 0x4e, 0xa3, 0x41, 0xe2, 0x58, 0x26, 0x21, 0x08, 0x6f, 0x6a, 0x61, 0x19, 0x0e, 0xcc, + 0x49, 0x3d, 0x9d, 0x62, 0x81, 0x33, 0x2c, 0xb3, 0x03, 0x5e, 0xeb, 0x73, 0xc0, 0xbf, 0x56, 0x82, + 0xc7, 0xf7, 0xd4, 0x6e, 0x7d, 0xe7, 0x87, 0x74, 0x62, 0x12, 0x65, 0x27, 0xce, 0x8d, 0x98, 0x44, + 0x98, 0x41, 0xf8, 0x28, 0x85, 0xa1, 0x71, 0xbf, 0x76, 0xd1, 0xc9, 0x4b, 0x7c, 0x94, 0x52, 0x2c, + 0x70, 0x86, 0xe5, 0x61, 0xa7, 0xe5, 0xdf, 0x2f, 0xc1, 0x93, 0x7d, 0xd8, 0x00, 0x05, 0x26, 0x79, + 0xa5, 0x53, 0xed, 0xca, 0x0f, 0x28, 0x23, 0xf2, 0x90, 0xc3, 0xf5, 0xf5, 0x12, 0x9c, 0xef, 0xad, + 0x8a, 0xd1, 0x8f, 0xc1, 0x89, 0x48, 0x45, 0x61, 0x99, 0x59, 0x7a, 0xa7, 0xb9, 0x27, 0x21, 0x05, + 0xc2, 0x59, 0x5c, 0x34, 0x09, 0x10, 0x3a, 0xc9, 0x46, 0x7c, 0x69, 0xdb, 0x8d, 0x13, 0x51, 0x85, + 0x66, 0x8c, 0x9f, 0x5d, 0xc9, 0x56, 0x6c, 0x60, 0x50, 0x76, 0xec, 0xdf, 0x6c, 0x70, 0x3d, 0x48, + 0xf8, 0x43, 0x7c, 0x1b, 0x71, 0x5a, 0xde, 0xd9, 0x61, 0x80, 0x70, 0x16, 0x97, 0xb2, 0x63, 0xa7, + 0xa3, 0xbc, 0xa3, 0x7c, 0x7f, 0xc1, 0xd8, 0x2d, 0xa8, 0x56, 0x6c, 0x60, 0x64, 0xf3, 0x0f, 0x2b, + 0xfb, 0xe7, 0x1f, 0xda, 0xff, 0xa4, 0x04, 0xe7, 0x7a, 0x9a, 0x72, 0xfd, 0x2d, 0xc0, 0x87, 0x2f, + 0x67, 0xf0, 0x70, 0x73, 0xe7, 0x80, 0xb9, 0x6d, 0x7f, 0xdc, 0x63, 0xa6, 0x89, 0xdc, 0xb6, 0xc3, + 0x27, 0x87, 0x3f, 0x7c, 0xe3, 0xd9, 0x95, 0xce, 0x36, 0x70, 0x80, 0x74, 0xb6, 0xcc, 0xc7, 0xa8, + 0xf4, 0xb9, 0x90, 0xff, 0xbc, 0xdc, 0x73, 0x78, 0xe9, 0xd6, 0xaf, 0x2f, 0x3f, 0xed, 0x2c, 0x9c, + 0x74, 0x7d, 0x76, 0x7f, 0xd3, 0x4a, 0x67, 0x4d, 0x14, 0x26, 0x29, 0xa5, 0x6f, 0x4f, 0x9f, 0xcf, + 0xc0, 0x71, 0xd7, 0x13, 0x0f, 0x61, 0x7a, 0xe1, 0xe1, 0x86, 0xf4, 0x60, 0x09, 0xae, 0x68, 0x09, + 0xce, 0xca, 0xa1, 0xd8, 0x70, 0x22, 0xd2, 0x14, 0x6a, 0x24, 0x16, 0x09, 0x15, 0xe7, 0x78, 0x52, + 0x46, 0x0e, 0x02, 0xce, 0x7f, 0x8e, 0x5d, 0x99, 0x13, 0x84, 0x6e, 0x43, 0x6c, 0x72, 0xf4, 0x95, + 0x39, 0xb4, 0x11, 0x73, 0x98, 0xfd, 0x21, 0xa8, 0xa9, 0xf7, 0xe7, 0x61, 0xdd, 0x6a, 0xd2, 0x75, + 0x85, 0x75, 0xab, 0x19, 0x67, 0x60, 0xd1, 0xaf, 0x45, 0x4d, 0xe2, 0xcc, 0xea, 0xb9, 0x46, 0x76, + 0x98, 0x7d, 0x6c, 0xff, 0x10, 0x8c, 0x28, 0x3f, 0x4b, 0xbf, 0x17, 0x09, 0xd9, 0x5f, 0x1c, 0x84, + 0xd1, 0x54, 0x71, 0xc0, 0x94, 0x83, 0xd5, 0xda, 0xd7, 0xc1, 0xca, 0xc2, 0xf4, 0x3b, 0xbe, 0xbc, + 0x65, 0xcc, 0x08, 0xd3, 0xef, 0xf8, 0x04, 0x73, 0x18, 0x35, 0x6f, 0x9b, 0xd1, 0x0e, 0xee, 0xf8, + 0x22, 0x9c, 0x56, 0x99, 0xb7, 0xb3, 0xac, 0x15, 0x0b, 0x28, 0xfa, 0xb8, 0x05, 0x23, 0x31, 0xf3, + 0xde, 0x73, 0xf7, 0xb4, 0x98, 0x74, 0x57, 0x8f, 0x5e, 0xfb, 0x50, 0x15, 0xc2, 0x64, 0x11, 0x32, + 0x66, 0x0b, 0x4e, 0x71, 0x44, 0x9f, 0xb2, 0xa0, 0xa6, 0x2e, 0x43, 0x11, 0x57, 0x01, 0xae, 0x14, + 0x5b, 0x7b, 0x91, 0xfb, 0x35, 0xd5, 0x41, 0x88, 0x2a, 0x82, 0x87, 0x35, 0x63, 0x14, 0x2b, 0xdf, + 0xf1, 0xd0, 0xf1, 0xf8, 0x8e, 0x21, 0xc7, 0x6f, 0xfc, 0x6e, 0xa8, 0xb5, 0x1d, 0xdf, 0x5d, 0x27, + 0x71, 0xc2, 0xdd, 0xb9, 0xb2, 0x24, 0xac, 0x6c, 0xc4, 0x1a, 0x4e, 0x15, 0x72, 0xcc, 0x5e, 0x2c, + 0x31, 0xfc, 0xaf, 0x4c, 0x21, 0xaf, 0xe8, 0x66, 0x6c, 0xe2, 0x98, 0xce, 0x62, 0x78, 0xa0, 0xce, + 0xe2, 0xe1, 0xbd, 0x9d, 0xc5, 0xf6, 0x3f, 0xb4, 0xe0, 0x6c, 0xee, 0x57, 0x7b, 0x78, 0x03, 0x1f, + 0xed, 0x2f, 0x55, 0xe0, 0x74, 0x4e, 0x95, 0x4f, 0xb4, 0x63, 0xce, 0x67, 0xab, 0x88, 0x18, 0x82, + 0xf4, 0x91, 0xb8, 0x1c, 0xc6, 0x9c, 0x49, 0x7c, 0xb0, 0xa3, 0x1a, 0x7d, 0x5c, 0x52, 0xbe, 0xbf, + 0xc7, 0x25, 0xc6, 0xb4, 0x1c, 0x78, 0xa0, 0xd3, 0xb2, 0xb2, 0xcf, 0x19, 0xc6, 0xaf, 0x59, 0x30, + 0xde, 0xee, 0x51, 0x5a, 0x5e, 0x38, 0x1e, 0x6f, 0x1e, 0x4f, 0xe1, 0xfa, 0xfa, 0x63, 0x77, 0x77, + 0x27, 0x7a, 0x56, 0xf4, 0xc7, 0x3d, 0x7b, 0x65, 0x7f, 0xbb, 0x0c, 0xac, 0xc4, 0x2c, 0xab, 0xe4, + 0xb6, 0x83, 0x3e, 0x66, 0x16, 0x0b, 0xb6, 0x8a, 0x2a, 0x6c, 0xcb, 0x89, 0xab, 0x62, 0xc3, 0x7c, + 0x04, 0xf3, 0x6a, 0x0f, 0x67, 0x85, 0x56, 0xa9, 0x0f, 0xa1, 0xe5, 0xc9, 0xaa, 0xcc, 0xe5, 0xe2, + 0xab, 0x32, 0xd7, 0xb2, 0x15, 0x99, 0xf7, 0xfe, 0xc4, 0x03, 0x0f, 0xe5, 0x27, 0xfe, 0x45, 0x8b, + 0x0b, 0x9e, 0xcc, 0x57, 0xd0, 0x96, 0x81, 0xb5, 0x87, 0x65, 0xf0, 0x0c, 0x54, 0x63, 0xe2, 0xad, + 0x5f, 0x21, 0x8e, 0x27, 0x2c, 0x08, 0x7d, 0x7e, 0x2d, 0xda, 0xb1, 0xc2, 0x60, 0xd7, 0xb6, 0x7a, + 0x5e, 0x70, 0xe7, 0x52, 0x3b, 0x4c, 0x76, 0x84, 0x2d, 0xa1, 0xaf, 0x6d, 0x55, 0x10, 0x6c, 0x60, + 0xd9, 0x7f, 0xab, 0xc4, 0x67, 0xa0, 0x08, 0x82, 0x78, 0x31, 0x73, 0xd1, 0x5e, 0xff, 0xf1, 0x03, + 0x1f, 0x01, 0x68, 0xa8, 0x2b, 0xea, 0xc5, 0x99, 0xd0, 0x95, 0x23, 0xdf, 0x9f, 0x2d, 0xe8, 0xe9, + 0xd7, 0xd0, 0x6d, 0xd8, 0xe0, 0x97, 0x92, 0xa5, 0xe5, 0x7d, 0x65, 0x69, 0x4a, 0xac, 0x0c, 0xec, + 0xa3, 0xed, 0xfe, 0xd4, 0x82, 0x94, 0x45, 0x84, 0x42, 0xa8, 0xd0, 0xee, 0xee, 0x14, 0x73, 0xfb, + 0xbe, 0x49, 0x9a, 0x8a, 0x46, 0x31, 0xed, 0xd9, 0x4f, 0xcc, 0x19, 0x21, 0x4f, 0xc4, 0x4a, 0xf0, + 0x51, 0xbd, 0x5e, 0x1c, 0xc3, 0x2b, 0x41, 0xb0, 0xc9, 0x0f, 0x36, 0x75, 0xdc, 0x85, 0xfd, 0x22, + 0x9c, 0xea, 0xea, 0x14, 0xbb, 0x53, 0x2b, 0xa0, 0xda, 0x27, 0x33, 0x5d, 0x59, 0x02, 0x27, 0xe6, + 0x30, 0xfb, 0xeb, 0x16, 0x9c, 0xcc, 0x92, 0x47, 0x6f, 0x59, 0x70, 0x2a, 0xce, 0xd2, 0x3b, 0xae, + 0xb1, 0x53, 0xf1, 0x8e, 0x5d, 0x20, 0xdc, 0xdd, 0x09, 0xfb, 0xff, 0x8a, 0xc9, 0x7f, 0xcb, 0xf5, + 0x9b, 0xc1, 0x1d, 0x65, 0x98, 0x58, 0x3d, 0x0d, 0x13, 0xba, 0x1e, 0x1b, 0x1b, 0xa4, 0xd9, 0xf1, + 0xba, 0x32, 0x47, 0x57, 0x44, 0x3b, 0x56, 0x18, 0x2c, 0x51, 0xae, 0x23, 0xca, 0xb6, 0x67, 0x26, + 0xe5, 0xac, 0x68, 0xc7, 0x0a, 0x03, 0x3d, 0x0f, 0x23, 0xc6, 0x4b, 0xca, 0x79, 0xc9, 0x0c, 0x72, + 0x43, 0x65, 0xc6, 0x38, 0x85, 0x85, 0x26, 0x01, 0x94, 0x91, 0x23, 0x55, 0x24, 0x73, 0x14, 0x29, + 0x49, 0x14, 0x63, 0x03, 0x83, 0xa5, 0xa5, 0x7a, 0x9d, 0x98, 0xf9, 0xf8, 0x07, 0x75, 0x29, 0xd1, + 0x19, 0xd1, 0x86, 0x15, 0x94, 0x4a, 0x93, 0xb6, 0xe3, 0x77, 0x1c, 0x8f, 0x8e, 0x90, 0xd8, 0xfa, + 0xa9, 0x65, 0xb8, 0xa8, 0x20, 0xd8, 0xc0, 0xa2, 0x6f, 0x9c, 0xb8, 0x6d, 0xf2, 0x4a, 0xe0, 0xcb, + 0x38, 0x35, 0x7d, 0xec, 0x23, 0xda, 0xb1, 0xc2, 0xb0, 0xff, 0xab, 0x05, 0x27, 0x74, 0x92, 0x3b, + 0xbf, 0x3d, 0xdb, 0xdc, 0xa9, 0x5a, 0xfb, 0xee, 0x54, 0xd3, 0xd9, 0xbf, 0xa5, 0xbe, 0xb2, 0x7f, + 0xcd, 0xc4, 0xdc, 0xf2, 0x9e, 0x89, 0xb9, 0xdf, 0xaf, 0x6f, 0x66, 0xe5, 0x19, 0xbc, 0xc3, 0x79, + 0xb7, 0xb2, 0x22, 0x1b, 0x06, 0x1b, 0x8e, 0xaa, 0xf0, 0x32, 0xc2, 0xf7, 0x0e, 0x33, 0xd3, 0x0c, + 0x49, 0x40, 0xec, 0x25, 0xa8, 0xa9, 0xd3, 0x0f, 0xb9, 0x51, 0xb5, 0xf2, 0x37, 0xaa, 0x7d, 0x25, + 0x08, 0xd6, 0xd7, 0xbe, 0xf1, 0x9d, 0x27, 0xde, 0xf1, 0x7b, 0xdf, 0x79, 0xe2, 0x1d, 0x7f, 0xf4, + 0x9d, 0x27, 0xde, 0xf1, 0xf1, 0xbb, 0x4f, 0x58, 0xdf, 0xb8, 0xfb, 0x84, 0xf5, 0x7b, 0x77, 0x9f, + 0xb0, 0xfe, 0xe8, 0xee, 0x13, 0xd6, 0xb7, 0xef, 0x3e, 0x61, 0x7d, 0xe1, 0x3f, 0x3d, 0xf1, 0x8e, + 0x57, 0x72, 0x03, 0x15, 0xe9, 0x8f, 0x67, 0x1b, 0xcd, 0xa9, 0xad, 0x8b, 0x2c, 0x56, 0x8e, 0x2e, + 0xaf, 0x29, 0x63, 0x4e, 0x4d, 0xc9, 0xe5, 0xf5, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x87, 0xd4, + 0x96, 0xc0, 0xad, 0xe1, 0x00, 0x00, } func (m *AWSAuthConfig) Marshal() (dAtA []byte, err error) { @@ -4543,6 +5161,11 @@ func (m *AWSAuthConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + i -= len(m.Profile) + copy(dAtA[i:], m.Profile) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Profile))) + i-- + dAtA[i] = 0x1a i -= len(m.RoleARN) copy(dAtA[i:], m.RoleARN) i = encodeVarintGenerated(dAtA, i, uint64(len(m.RoleARN))) @@ -5123,6 +5746,47 @@ func (m *ApplicationMatchExpression) MarshalToSizedBuffer(dAtA []byte) (int, err return len(dAtA) - i, nil } +func (m *ApplicationPreservedFields) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ApplicationPreservedFields) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ApplicationPreservedFields) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Labels) > 0 { + for iNdEx := len(m.Labels) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Labels[iNdEx]) + copy(dAtA[i:], m.Labels[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Labels[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Annotations) > 0 { + for iNdEx := len(m.Annotations) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Annotations[iNdEx]) + copy(dAtA[i:], m.Annotations[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Annotations[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *ApplicationSet) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -5196,11 +5860,16 @@ func (m *ApplicationSetApplicationStatus) MarshalToSizedBuffer(dAtA []byte) (int _ = i var l int _ = l + i -= len(m.Step) + copy(dAtA[i:], m.Step) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Step))) + i-- + dAtA[i] = 0x2a i -= len(m.Status) copy(dAtA[i:], m.Status) i = encodeVarintGenerated(dAtA, i, uint64(len(m.Status))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x22 i -= len(m.Message) copy(dAtA[i:], m.Message) i = encodeVarintGenerated(dAtA, i, uint64(len(m.Message))) @@ -5301,6 +5970,18 @@ func (m *ApplicationSetGenerator) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if m.Plugin != nil { + { + size, err := m.Plugin.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } if m.Selector != nil { { size, err := m.Selector.MarshalToSizedBuffer(dAtA[:i]) @@ -5479,6 +6160,18 @@ func (m *ApplicationSetNestedGenerator) MarshalToSizedBuffer(dAtA []byte) (int, _ = i var l int _ = l + if m.Plugin != nil { + { + size, err := m.Plugin.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } if m.Selector != nil { { size, err := m.Selector.MarshalToSizedBuffer(dAtA[:i]) @@ -5590,6 +6283,52 @@ func (m *ApplicationSetNestedGenerator) MarshalToSizedBuffer(dAtA []byte) (int, return len(dAtA) - i, nil } +func (m *ApplicationSetResourceIgnoreDifferences) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ApplicationSetResourceIgnoreDifferences) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ApplicationSetResourceIgnoreDifferences) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.JQPathExpressions) > 0 { + for iNdEx := len(m.JQPathExpressions) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.JQPathExpressions[iNdEx]) + copy(dAtA[i:], m.JQPathExpressions[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.JQPathExpressions[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.JSONPointers) > 0 { + for iNdEx := len(m.JSONPointers) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.JSONPointers[iNdEx]) + copy(dAtA[i:], m.JSONPointers[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.JSONPointers[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *ApplicationSetRolloutStep) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -5696,6 +6435,56 @@ func (m *ApplicationSetSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.TemplatePatch != nil { + i -= len(*m.TemplatePatch) + copy(dAtA[i:], *m.TemplatePatch) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.TemplatePatch))) + i-- + dAtA[i] = 0x52 + } + if len(m.IgnoreApplicationDifferences) > 0 { + for iNdEx := len(m.IgnoreApplicationDifferences) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IgnoreApplicationDifferences[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + i-- + if m.ApplyNestedSelectors { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + if len(m.GoTemplateOptions) > 0 { + for iNdEx := len(m.GoTemplateOptions) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.GoTemplateOptions[iNdEx]) + copy(dAtA[i:], m.GoTemplateOptions[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.GoTemplateOptions[iNdEx]))) + i-- + dAtA[i] = 0x3a + } + } + if m.PreservedFields != nil { + { + size, err := m.PreservedFields.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } if m.Strategy != nil { { size, err := m.Strategy.MarshalToSizedBuffer(dAtA[:i]) @@ -5866,6 +6655,13 @@ func (m *ApplicationSetSyncPolicy) MarshalToSizedBuffer(dAtA []byte) (int, error _ = i var l int _ = l + if m.ApplicationsSync != nil { + i -= len(*m.ApplicationsSync) + copy(dAtA[i:], *m.ApplicationsSync) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.ApplicationsSync))) + i-- + dAtA[i] = 0x12 + } i-- if m.PreserveResourcesOnDeletion { dAtA[i] = 1 @@ -6030,6 +6826,30 @@ func (m *ApplicationSetTerminalGenerator) MarshalToSizedBuffer(dAtA []byte) (int _ = i var l int _ = l + if m.Selector != nil { + { + size, err := m.Selector.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if m.Plugin != nil { + { + size, err := m.Plugin.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } if m.PullRequest != nil { { size, err := m.PullRequest.MarshalToSizedBuffer(dAtA[:i]) @@ -6272,6 +7092,18 @@ func (m *ApplicationSourceHelm) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.ValuesObject != nil { + { + size, err := m.ValuesObject.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } i-- if m.SkipCrds { dAtA[i] = 1 @@ -6432,6 +7264,64 @@ func (m *ApplicationSourceKustomize) MarshalToSizedBuffer(dAtA []byte) (int, err var l int _ = l i-- + if m.LabelWithoutSelector { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x70 + if len(m.Components) > 0 { + for iNdEx := len(m.Components) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Components[iNdEx]) + copy(dAtA[i:], m.Components[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Components[iNdEx]))) + i-- + dAtA[i] = 0x6a + } + } + if len(m.Patches) > 0 { + for iNdEx := len(m.Patches) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Patches[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } + } + if len(m.Replicas) > 0 { + for iNdEx := len(m.Replicas) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Replicas[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } + } + i-- + if m.CommonAnnotationsEnvsubst { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x50 + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0x4a + i-- if m.ForceCommonAnnotations { dAtA[i] = 1 } else { @@ -6605,38 +7495,29 @@ func (m *ApplicationSourcePluginParameter) MarshalToSizedBuffer(dAtA []byte) (in i-- dAtA[i] = 0x2a } - if len(m.Array) > 0 { - for iNdEx := len(m.Array) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Array[iNdEx]) - copy(dAtA[i:], m.Array[iNdEx]) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.Array[iNdEx]))) - i-- - dAtA[i] = 0x22 + if m.OptionalArray != nil { + { + size, err := m.OptionalArray.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x22 } - if len(m.Map) > 0 { - keysForMap := make([]string, 0, len(m.Map)) - for k := range m.Map { - keysForMap = append(keysForMap, string(k)) - } - github_com_gogo_protobuf_sortkeys.Strings(keysForMap) - for iNdEx := len(keysForMap) - 1; iNdEx >= 0; iNdEx-- { - v := m.Map[string(keysForMap[iNdEx])] - baseI := i - i -= len(v) - copy(dAtA[i:], v) - i = encodeVarintGenerated(dAtA, i, uint64(len(v))) - i-- - dAtA[i] = 0x12 - i -= len(keysForMap[iNdEx]) - copy(dAtA[i:], keysForMap[iNdEx]) - i = encodeVarintGenerated(dAtA, i, uint64(len(keysForMap[iNdEx]))) - i-- - dAtA[i] = 0xa - i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) - i-- - dAtA[i] = 0x1a + if m.OptionalMap != nil { + { + size, err := m.OptionalMap.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x1a } i -= len(m.Name) copy(dAtA[i:], m.Name) @@ -6775,6 +7656,11 @@ func (m *ApplicationStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + i -= len(m.ControllerNamespace) + copy(dAtA[i:], m.ControllerNamespace) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.ControllerNamespace))) + i-- + dAtA[i] = 0x6a if len(m.SourceTypes) > 0 { for iNdEx := len(m.SourceTypes) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.SourceTypes[iNdEx]) @@ -7127,6 +8013,83 @@ func (m *BasicAuthBitbucketServer) MarshalToSizedBuffer(dAtA []byte) (int, error return len(dAtA) - i, nil } +func (m *BearerTokenBitbucketCloud) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BearerTokenBitbucketCloud) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BearerTokenBitbucketCloud) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TokenRef != nil { + { + size, err := m.TokenRef.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ChartDetails) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ChartDetails) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ChartDetails) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Maintainers) > 0 { + for iNdEx := len(m.Maintainers) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Maintainers[iNdEx]) + copy(dAtA[i:], m.Maintainers[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Maintainers[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + i -= len(m.Home) + copy(dAtA[i:], m.Home) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Home))) + i-- + dAtA[i] = 0x12 + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *Cluster) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -7630,6 +8593,20 @@ func (m *ComparedTo) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.IgnoreDifferences) > 0 { + for iNdEx := len(m.IgnoreDifferences) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IgnoreDifferences[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } if len(m.Sources) > 0 { for iNdEx := len(m.Sources) - 1; iNdEx >= 0; iNdEx-- { { @@ -8078,6 +9055,30 @@ func (m *GitGenerator) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Values) > 0 { + keysForValues := make([]string, 0, len(m.Values)) + for k := range m.Values { + keysForValues = append(keysForValues, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForValues) + for iNdEx := len(keysForValues) - 1; iNdEx >= 0; iNdEx-- { + v := m.Values[string(keysForValues[iNdEx])] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintGenerated(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(keysForValues[iNdEx]) + copy(dAtA[i:], keysForValues[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForValues[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x42 + } + } i -= len(m.PathParamPrefix) copy(dAtA[i:], m.PathParamPrefix) i = encodeVarintGenerated(dAtA, i, uint64(len(m.PathParamPrefix))) @@ -8678,6 +9679,44 @@ func (m *KnownTypeField) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *KustomizeGvk) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KustomizeGvk) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *KustomizeGvk) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.Kind) + copy(dAtA[i:], m.Kind) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Kind))) + i-- + dAtA[i] = 0x1a + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x12 + i -= len(m.Group) + copy(dAtA[i:], m.Group) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Group))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *KustomizeOptions) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -8711,6 +9750,202 @@ func (m *KustomizeOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *KustomizePatch) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KustomizePatch) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *KustomizePatch) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Options) > 0 { + keysForOptions := make([]string, 0, len(m.Options)) + for k := range m.Options { + keysForOptions = append(keysForOptions, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForOptions) + for iNdEx := len(keysForOptions) - 1; iNdEx >= 0; iNdEx-- { + v := m.Options[string(keysForOptions[iNdEx])] + baseI := i + i-- + if v { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + i -= len(keysForOptions[iNdEx]) + copy(dAtA[i:], keysForOptions[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForOptions[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 + } + } + if m.Target != nil { + { + size, err := m.Target.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + i -= len(m.Patch) + copy(dAtA[i:], m.Patch) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Patch))) + i-- + dAtA[i] = 0x12 + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *KustomizeReplica) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KustomizeReplica) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *KustomizeReplica) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Count.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *KustomizeResId) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KustomizeResId) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *KustomizeResId) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0x1a + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + { + size, err := m.KustomizeGvk.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *KustomizeSelector) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KustomizeSelector) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *KustomizeSelector) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.LabelSelector) + copy(dAtA[i:], m.LabelSelector) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.LabelSelector))) + i-- + dAtA[i] = 0x1a + i -= len(m.AnnotationSelector) + copy(dAtA[i:], m.AnnotationSelector) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.AnnotationSelector))) + i-- + dAtA[i] = 0x12 + { + size, err := m.KustomizeResId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *ListGenerator) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -8731,6 +9966,11 @@ func (m *ListGenerator) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + i -= len(m.ElementsYaml) + copy(dAtA[i:], m.ElementsYaml) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.ElementsYaml))) + i-- + dAtA[i] = 0x1a { size, err := m.Template.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -9200,6 +10440,85 @@ func (m *OperationState) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *OptionalArray) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *OptionalArray) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *OptionalArray) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Array) > 0 { + for iNdEx := len(m.Array) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Array[iNdEx]) + copy(dAtA[i:], m.Array[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Array[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *OptionalMap) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *OptionalMap) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *OptionalMap) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Map) > 0 { + keysForMap := make([]string, 0, len(m.Map)) + for k := range m.Map { + keysForMap = append(keysForMap, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForMap) + for iNdEx := len(keysForMap) - 1; iNdEx >= 0; iNdEx-- { + v := m.Map[string(keysForMap[iNdEx])] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintGenerated(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(keysForMap[iNdEx]) + copy(dAtA[i:], keysForMap[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForMap[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *OrphanedResourceKey) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -9335,6 +10654,168 @@ func (m *OverrideIgnoreDiff) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *PluginConfigMapRef) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PluginConfigMapRef) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PluginConfigMapRef) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *PluginGenerator) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PluginGenerator) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PluginGenerator) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Values) > 0 { + keysForValues := make([]string, 0, len(m.Values)) + for k := range m.Values { + keysForValues = append(keysForValues, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForValues) + for iNdEx := len(keysForValues) - 1; iNdEx >= 0; iNdEx-- { + v := m.Values[string(keysForValues[iNdEx])] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintGenerated(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(keysForValues[iNdEx]) + copy(dAtA[i:], keysForValues[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForValues[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x2a + } + } + { + size, err := m.Template.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if m.RequeueAfterSeconds != nil { + i = encodeVarintGenerated(dAtA, i, uint64(*m.RequeueAfterSeconds)) + i-- + dAtA[i] = 0x18 + } + { + size, err := m.Input.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.ConfigMapRef.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *PluginInput) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PluginInput) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PluginInput) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Parameters) > 0 { + keysForParameters := make([]string, 0, len(m.Parameters)) + for k := range m.Parameters { + keysForParameters = append(keysForParameters, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForParameters) + for iNdEx := len(keysForParameters) - 1; iNdEx >= 0; iNdEx-- { + v := m.Parameters[string(keysForParameters[iNdEx])] + baseI := i + { + size, err := (&v).MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + i -= len(keysForParameters[iNdEx]) + copy(dAtA[i:], keysForParameters[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForParameters[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *ProjectRole) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -9420,6 +10901,30 @@ func (m *PullRequestGenerator) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.AzureDevOps != nil { + { + size, err := m.AzureDevOps.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + if m.Bitbucket != nil { + { + size, err := m.Bitbucket.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } { size, err := m.Template.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -9500,7 +11005,7 @@ func (m *PullRequestGenerator) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *PullRequestGeneratorBitbucketServer) Marshal() (dAtA []byte, err error) { +func (m *PullRequestGeneratorAzureDevOps) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -9510,12 +11015,138 @@ func (m *PullRequestGeneratorBitbucketServer) Marshal() (dAtA []byte, err error) return dAtA[:n], nil } -func (m *PullRequestGeneratorBitbucketServer) MarshalTo(dAtA []byte) (int, error) { +func (m *PullRequestGeneratorAzureDevOps) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *PullRequestGeneratorBitbucketServer) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *PullRequestGeneratorAzureDevOps) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Labels) > 0 { + for iNdEx := len(m.Labels) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Labels[iNdEx]) + copy(dAtA[i:], m.Labels[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Labels[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if m.TokenRef != nil { + { + size, err := m.TokenRef.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + i -= len(m.API) + copy(dAtA[i:], m.API) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.API))) + i-- + dAtA[i] = 0x22 + i -= len(m.Repo) + copy(dAtA[i:], m.Repo) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Repo))) + i-- + dAtA[i] = 0x1a + i -= len(m.Project) + copy(dAtA[i:], m.Project) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Project))) + i-- + dAtA[i] = 0x12 + i -= len(m.Organization) + copy(dAtA[i:], m.Organization) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Organization))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *PullRequestGeneratorBitbucket) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PullRequestGeneratorBitbucket) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PullRequestGeneratorBitbucket) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BearerToken != nil { + { + size, err := m.BearerToken.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.BasicAuth != nil { + { + size, err := m.BasicAuth.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + i -= len(m.API) + copy(dAtA[i:], m.API) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.API))) + i-- + dAtA[i] = 0x1a + i -= len(m.Repo) + copy(dAtA[i:], m.Repo) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Repo))) + i-- + dAtA[i] = 0x12 + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *PullRequestGeneratorBitbucketServer) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PullRequestGeneratorBitbucketServer) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PullRequestGeneratorBitbucketServer) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -9570,6 +11201,13 @@ func (m *PullRequestGeneratorFilter) MarshalToSizedBuffer(dAtA []byte) (int, err _ = i var l int _ = l + if m.TargetBranchMatch != nil { + i -= len(*m.TargetBranchMatch) + copy(dAtA[i:], *m.TargetBranchMatch) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.TargetBranchMatch))) + i-- + dAtA[i] = 0x12 + } if m.BranchMatch != nil { i -= len(*m.BranchMatch) copy(dAtA[i:], *m.BranchMatch) @@ -9600,6 +11238,14 @@ func (m *PullRequestGeneratorGitLab) MarshalToSizedBuffer(dAtA []byte) (int, err _ = i var l int _ = l + i-- + if m.Insecure { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 i -= len(m.PullRequestState) copy(dAtA[i:], m.PullRequestState) i = encodeVarintGenerated(dAtA, i, uint64(len(m.PullRequestState))) @@ -9824,6 +11470,16 @@ func (m *RepoCreds) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + i-- + if m.ForceHttpBasicAuth { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa0 i -= len(m.Proxy) copy(dAtA[i:], m.Proxy) i = encodeVarintGenerated(dAtA, i, uint64(len(m.Proxy))) @@ -9965,6 +11621,16 @@ func (m *Repository) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + i-- + if m.ForceHttpBasicAuth { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xb0 i -= len(m.GCPServiceAccountKey) copy(dAtA[i:], m.GCPServiceAccountKey) i = encodeVarintGenerated(dAtA, i, uint64(len(m.GCPServiceAccountKey))) @@ -10265,6 +11931,16 @@ func (m *ResourceAction) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + i -= len(m.DisplayName) + copy(dAtA[i:], m.DisplayName) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.DisplayName))) + i-- + dAtA[i] = 0x2a + i -= len(m.IconClass) + copy(dAtA[i:], m.IconClass) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.IconClass))) + i-- + dAtA[i] = 0x22 i-- if m.Disabled { dAtA[i] = 1 @@ -10811,6 +12487,16 @@ func (m *ResourceOverride) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.IgnoreResourceUpdates.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 i-- if m.UseOpenLibs { dAtA[i] = 1 @@ -11124,6 +12810,16 @@ func (m *RevisionHistory) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.InitiatedBy.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 if len(m.Revisions) > 0 { for iNdEx := len(m.Revisions) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Revisions[iNdEx]) @@ -11267,6 +12963,42 @@ func (m *SCMProviderGenerator) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.AWSCodeCommit != nil { + { + size, err := m.AWSCodeCommit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } + if len(m.Values) > 0 { + keysForValues := make([]string, 0, len(m.Values)) + for k := range m.Values { + keysForValues = append(keysForValues, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForValues) + for iNdEx := len(keysForValues) - 1; iNdEx >= 0; iNdEx-- { + v := m.Values[string(keysForValues[iNdEx])] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintGenerated(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(keysForValues[iNdEx]) + copy(dAtA[i:], keysForValues[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForValues[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x5a + } + } { size, err := m.Template.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -11376,6 +13108,61 @@ func (m *SCMProviderGenerator) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *SCMProviderGeneratorAWSCodeCommit) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SCMProviderGeneratorAWSCodeCommit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SCMProviderGeneratorAWSCodeCommit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i-- + if m.AllBranches { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + i -= len(m.Region) + copy(dAtA[i:], m.Region) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Region))) + i-- + dAtA[i] = 0x1a + i -= len(m.Role) + copy(dAtA[i:], m.Role) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Role))) + i-- + dAtA[i] = 0x12 + if len(m.TagFilters) > 0 { + for iNdEx := len(m.TagFilters) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TagFilters[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *SCMProviderGeneratorAzureDevOps) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -11741,6 +13528,29 @@ func (m *SCMProviderGeneratorGitlab) MarshalToSizedBuffer(dAtA []byte) (int, err _ = i var l int _ = l + i -= len(m.Topic) + copy(dAtA[i:], m.Topic) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Topic))) + i-- + dAtA[i] = 0x42 + if m.IncludeSharedProjects != nil { + i-- + if *m.IncludeSharedProjects { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } + i-- + if m.Insecure { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 i-- if m.AllBranches { dAtA[i] = 1 @@ -12029,6 +13839,18 @@ func (m *SyncOperationResult) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.ManagedNamespaceMetadata != nil { + { + size, err := m.ManagedNamespaceMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } if len(m.Revisions) > 0 { for iNdEx := len(m.Revisions) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Revisions[iNdEx]) @@ -12497,6 +14319,39 @@ func (m *TLSClientConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *TagFilter) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TagFilter) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TagFilter) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x12 + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int { offset -= sovGenerated(v) base := offset @@ -12518,6 +14373,8 @@ func (m *AWSAuthConfig) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) l = len(m.RoleARN) n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Profile) + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -12734,6 +14591,27 @@ func (m *ApplicationMatchExpression) Size() (n int) { return n } +func (m *ApplicationPreservedFields) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Annotations) > 0 { + for _, s := range m.Annotations { + l = len(s) + n += 1 + l + sovGenerated(uint64(l)) + } + } + if len(m.Labels) > 0 { + for _, s := range m.Labels { + l = len(s) + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + func (m *ApplicationSet) Size() (n int) { if m == nil { return 0 @@ -12765,6 +14643,8 @@ func (m *ApplicationSetApplicationStatus) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) l = len(m.Status) n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Step) + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -12831,6 +14711,10 @@ func (m *ApplicationSetGenerator) Size() (n int) { l = m.Selector.Size() n += 1 + l + sovGenerated(uint64(l)) } + if m.Plugin != nil { + l = m.Plugin.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -12893,6 +14777,33 @@ func (m *ApplicationSetNestedGenerator) Size() (n int) { l = m.Selector.Size() n += 1 + l + sovGenerated(uint64(l)) } + if m.Plugin != nil { + l = m.Plugin.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + return n +} + +func (m *ApplicationSetResourceIgnoreDifferences) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + n += 1 + l + sovGenerated(uint64(l)) + if len(m.JSONPointers) > 0 { + for _, s := range m.JSONPointers { + l = len(s) + n += 1 + l + sovGenerated(uint64(l)) + } + } + if len(m.JQPathExpressions) > 0 { + for _, s := range m.JQPathExpressions { + l = len(s) + n += 1 + l + sovGenerated(uint64(l)) + } + } return n } @@ -12953,6 +14864,27 @@ func (m *ApplicationSetSpec) Size() (n int) { l = m.Strategy.Size() n += 1 + l + sovGenerated(uint64(l)) } + if m.PreservedFields != nil { + l = m.PreservedFields.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if len(m.GoTemplateOptions) > 0 { + for _, s := range m.GoTemplateOptions { + l = len(s) + n += 1 + l + sovGenerated(uint64(l)) + } + } + n += 2 + if len(m.IgnoreApplicationDifferences) > 0 { + for _, e := range m.IgnoreApplicationDifferences { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + if m.TemplatePatch != nil { + l = len(*m.TemplatePatch) + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -12999,6 +14931,10 @@ func (m *ApplicationSetSyncPolicy) Size() (n int) { var l int _ = l n += 2 + if m.ApplicationsSync != nil { + l = len(*m.ApplicationsSync) + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -13080,6 +15016,14 @@ func (m *ApplicationSetTerminalGenerator) Size() (n int) { l = m.PullRequest.Size() n += 1 + l + sovGenerated(uint64(l)) } + if m.Plugin != nil { + l = m.Plugin.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if m.Selector != nil { + l = m.Selector.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -13167,6 +15111,10 @@ func (m *ApplicationSourceHelm) Size() (n int) { n += 2 n += 2 n += 2 + if m.ValuesObject != nil { + l = m.ValuesObject.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -13233,6 +15181,28 @@ func (m *ApplicationSourceKustomize) Size() (n int) { } n += 2 n += 2 + l = len(m.Namespace) + n += 1 + l + sovGenerated(uint64(l)) + n += 2 + if len(m.Replicas) > 0 { + for _, e := range m.Replicas { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + if len(m.Patches) > 0 { + for _, e := range m.Patches { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + if len(m.Components) > 0 { + for _, s := range m.Components { + l = len(s) + n += 1 + l + sovGenerated(uint64(l)) + } + } + n += 2 return n } @@ -13267,19 +15237,13 @@ func (m *ApplicationSourcePluginParameter) Size() (n int) { _ = l l = len(m.Name) n += 1 + l + sovGenerated(uint64(l)) - if len(m.Map) > 0 { - for k, v := range m.Map { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v))) - n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) - } + if m.OptionalMap != nil { + l = m.OptionalMap.Size() + n += 1 + l + sovGenerated(uint64(l)) } - if len(m.Array) > 0 { - for _, s := range m.Array { - l = len(s) - n += 1 + l + sovGenerated(uint64(l)) - } + if m.OptionalArray != nil { + l = m.OptionalArray.Size() + n += 1 + l + sovGenerated(uint64(l)) } if m.String_ != nil { l = len(*m.String_) @@ -13382,6 +15346,8 @@ func (m *ApplicationStatus) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } + l = len(m.ControllerNamespace) + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -13477,6 +15443,38 @@ func (m *BasicAuthBitbucketServer) Size() (n int) { return n } +func (m *BearerTokenBitbucketCloud) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TokenRef != nil { + l = m.TokenRef.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + return n +} + +func (m *ChartDetails) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Description) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Home) + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Maintainers) > 0 { + for _, s := range m.Maintainers { + l = len(s) + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + func (m *Cluster) Size() (n int) { if m == nil { return 0 @@ -13667,6 +15665,12 @@ func (m *ComparedTo) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } + if len(m.IgnoreDifferences) > 0 { + for _, e := range m.IgnoreDifferences { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } return n } @@ -13842,6 +15846,14 @@ func (m *GitGenerator) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) l = len(m.PathParamPrefix) n += 1 + l + sovGenerated(uint64(l)) + if len(m.Values) > 0 { + for k, v := range m.Values { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v))) + n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) + } + } return n } @@ -14052,6 +16064,21 @@ func (m *KnownTypeField) Size() (n int) { return n } +func (m *KustomizeGvk) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Group) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Version) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Kind) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + func (m *KustomizeOptions) Size() (n int) { if m == nil { return 0 @@ -14065,6 +16092,74 @@ func (m *KustomizeOptions) Size() (n int) { return n } +func (m *KustomizePatch) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Patch) + n += 1 + l + sovGenerated(uint64(l)) + if m.Target != nil { + l = m.Target.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if len(m.Options) > 0 { + for k, v := range m.Options { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + 1 + n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) + } + } + return n +} + +func (m *KustomizeReplica) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + n += 1 + l + sovGenerated(uint64(l)) + l = m.Count.Size() + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *KustomizeResId) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.KustomizeGvk.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Name) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Namespace) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *KustomizeSelector) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.KustomizeResId.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.AnnotationSelector) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.LabelSelector) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + func (m *ListGenerator) Size() (n int) { if m == nil { return 0 @@ -14079,6 +16174,8 @@ func (m *ListGenerator) Size() (n int) { } l = m.Template.Size() n += 1 + l + sovGenerated(uint64(l)) + l = len(m.ElementsYaml) + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -14244,6 +16341,38 @@ func (m *OperationState) Size() (n int) { return n } +func (m *OptionalArray) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Array) > 0 { + for _, s := range m.Array { + l = len(s) + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *OptionalMap) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Map) > 0 { + for k, v := range m.Map { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v))) + n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) + } + } + return n +} + func (m *OrphanedResourceKey) Size() (n int) { if m == nil { return 0 @@ -14304,6 +16433,61 @@ func (m *OverrideIgnoreDiff) Size() (n int) { return n } +func (m *PluginConfigMapRef) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *PluginGenerator) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ConfigMapRef.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = m.Input.Size() + n += 1 + l + sovGenerated(uint64(l)) + if m.RequeueAfterSeconds != nil { + n += 1 + sovGenerated(uint64(*m.RequeueAfterSeconds)) + } + l = m.Template.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Values) > 0 { + for k, v := range m.Values { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v))) + n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) + } + } + return n +} + +func (m *PluginInput) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Parameters) > 0 { + for k, v := range m.Parameters { + _ = k + _ = v + l = v.Size() + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + l + sovGenerated(uint64(l)) + n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) + } + } + return n +} + func (m *ProjectRole) Size() (n int) { if m == nil { return 0 @@ -14368,6 +16552,64 @@ func (m *PullRequestGenerator) Size() (n int) { } l = m.Template.Size() n += 1 + l + sovGenerated(uint64(l)) + if m.Bitbucket != nil { + l = m.Bitbucket.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if m.AzureDevOps != nil { + l = m.AzureDevOps.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + return n +} + +func (m *PullRequestGeneratorAzureDevOps) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Organization) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Project) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Repo) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.API) + n += 1 + l + sovGenerated(uint64(l)) + if m.TokenRef != nil { + l = m.TokenRef.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if len(m.Labels) > 0 { + for _, s := range m.Labels { + l = len(s) + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *PullRequestGeneratorBitbucket) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Owner) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Repo) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.API) + n += 1 + l + sovGenerated(uint64(l)) + if m.BasicAuth != nil { + l = m.BasicAuth.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if m.BearerToken != nil { + l = m.BearerToken.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -14400,6 +16642,10 @@ func (m *PullRequestGeneratorFilter) Size() (n int) { l = len(*m.BranchMatch) n += 1 + l + sovGenerated(uint64(l)) } + if m.TargetBranchMatch != nil { + l = len(*m.TargetBranchMatch) + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -14425,6 +16671,7 @@ func (m *PullRequestGeneratorGitLab) Size() (n int) { } l = len(m.PullRequestState) n += 1 + l + sovGenerated(uint64(l)) + n += 2 return n } @@ -14521,6 +16768,7 @@ func (m *RepoCreds) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) l = len(m.Proxy) n += 2 + l + sovGenerated(uint64(l)) + n += 3 return n } @@ -14582,6 +16830,7 @@ func (m *Repository) Size() (n int) { n += 2 + l + sovGenerated(uint64(l)) l = len(m.GCPServiceAccountKey) n += 2 + l + sovGenerated(uint64(l)) + n += 3 return n } @@ -14655,6 +16904,10 @@ func (m *ResourceAction) Size() (n int) { } } n += 2 + l = len(m.IconClass) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.DisplayName) + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -14876,6 +17129,8 @@ func (m *ResourceOverride) Size() (n int) { } } n += 2 + l = m.IgnoreResourceUpdates.Size() + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -15000,6 +17255,8 @@ func (m *RevisionHistory) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } + l = m.InitiatedBy.Size() + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -15069,6 +17326,38 @@ func (m *SCMProviderGenerator) Size() (n int) { } l = m.Template.Size() n += 1 + l + sovGenerated(uint64(l)) + if len(m.Values) > 0 { + for k, v := range m.Values { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v))) + n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) + } + } + if m.AWSCodeCommit != nil { + l = m.AWSCodeCommit.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + return n +} + +func (m *SCMProviderGeneratorAWSCodeCommit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.TagFilters) > 0 { + for _, e := range m.TagFilters { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + l = len(m.Role) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Region) + n += 1 + l + sovGenerated(uint64(l)) + n += 2 return n } @@ -15216,6 +17505,12 @@ func (m *SCMProviderGeneratorGitlab) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } n += 2 + n += 2 + if m.IncludeSharedProjects != nil { + n += 2 + } + l = len(m.Topic) + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -15339,6 +17634,10 @@ func (m *SyncOperationResult) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } + if m.ManagedNamespaceMetadata != nil { + l = m.ManagedNamespaceMetadata.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -15500,6 +17799,19 @@ func (m *TLSClientConfig) Size() (n int) { return n } +func (m *TagFilter) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Value) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + func sovGenerated(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -15513,6 +17825,7 @@ func (this *AWSAuthConfig) String() string { s := strings.Join([]string{`&AWSAuthConfig{`, `ClusterName:` + fmt.Sprintf("%v", this.ClusterName) + `,`, `RoleARN:` + fmt.Sprintf("%v", this.RoleARN) + `,`, + `Profile:` + fmt.Sprintf("%v", this.Profile) + `,`, `}`, }, "") return s @@ -15692,6 +18005,17 @@ func (this *ApplicationMatchExpression) String() string { }, "") return s } +func (this *ApplicationPreservedFields) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ApplicationPreservedFields{`, + `Annotations:` + fmt.Sprintf("%v", this.Annotations) + `,`, + `Labels:` + fmt.Sprintf("%v", this.Labels) + `,`, + `}`, + }, "") + return s +} func (this *ApplicationSet) String() string { if this == nil { return "nil" @@ -15713,6 +18037,7 @@ func (this *ApplicationSetApplicationStatus) String() string { `LastTransitionTime:` + strings.Replace(fmt.Sprintf("%v", this.LastTransitionTime), "Time", "v1.Time", 1) + `,`, `Message:` + fmt.Sprintf("%v", this.Message) + `,`, `Status:` + fmt.Sprintf("%v", this.Status) + `,`, + `Step:` + fmt.Sprintf("%v", this.Step) + `,`, `}`, }, "") return s @@ -15745,6 +18070,7 @@ func (this *ApplicationSetGenerator) String() string { `Matrix:` + strings.Replace(this.Matrix.String(), "MatrixGenerator", "MatrixGenerator", 1) + `,`, `Merge:` + strings.Replace(this.Merge.String(), "MergeGenerator", "MergeGenerator", 1) + `,`, `Selector:` + strings.Replace(fmt.Sprintf("%v", this.Selector), "LabelSelector", "v1.LabelSelector", 1) + `,`, + `Plugin:` + strings.Replace(this.Plugin.String(), "PluginGenerator", "PluginGenerator", 1) + `,`, `}`, }, "") return s @@ -15779,6 +18105,19 @@ func (this *ApplicationSetNestedGenerator) String() string { `Matrix:` + strings.Replace(fmt.Sprintf("%v", this.Matrix), "JSON", "v11.JSON", 1) + `,`, `Merge:` + strings.Replace(fmt.Sprintf("%v", this.Merge), "JSON", "v11.JSON", 1) + `,`, `Selector:` + strings.Replace(fmt.Sprintf("%v", this.Selector), "LabelSelector", "v1.LabelSelector", 1) + `,`, + `Plugin:` + strings.Replace(this.Plugin.String(), "PluginGenerator", "PluginGenerator", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ApplicationSetResourceIgnoreDifferences) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ApplicationSetResourceIgnoreDifferences{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `JSONPointers:` + fmt.Sprintf("%v", this.JSONPointers) + `,`, + `JQPathExpressions:` + fmt.Sprintf("%v", this.JQPathExpressions) + `,`, `}`, }, "") return s @@ -15823,12 +18162,22 @@ func (this *ApplicationSetSpec) String() string { repeatedStringForGenerators += strings.Replace(strings.Replace(f.String(), "ApplicationSetGenerator", "ApplicationSetGenerator", 1), `&`, ``, 1) + "," } repeatedStringForGenerators += "}" + repeatedStringForIgnoreApplicationDifferences := "[]ApplicationSetResourceIgnoreDifferences{" + for _, f := range this.IgnoreApplicationDifferences { + repeatedStringForIgnoreApplicationDifferences += strings.Replace(strings.Replace(f.String(), "ApplicationSetResourceIgnoreDifferences", "ApplicationSetResourceIgnoreDifferences", 1), `&`, ``, 1) + "," + } + repeatedStringForIgnoreApplicationDifferences += "}" s := strings.Join([]string{`&ApplicationSetSpec{`, `GoTemplate:` + fmt.Sprintf("%v", this.GoTemplate) + `,`, `Generators:` + repeatedStringForGenerators + `,`, `Template:` + strings.Replace(strings.Replace(this.Template.String(), "ApplicationSetTemplate", "ApplicationSetTemplate", 1), `&`, ``, 1) + `,`, `SyncPolicy:` + strings.Replace(this.SyncPolicy.String(), "ApplicationSetSyncPolicy", "ApplicationSetSyncPolicy", 1) + `,`, `Strategy:` + strings.Replace(this.Strategy.String(), "ApplicationSetStrategy", "ApplicationSetStrategy", 1) + `,`, + `PreservedFields:` + strings.Replace(this.PreservedFields.String(), "ApplicationPreservedFields", "ApplicationPreservedFields", 1) + `,`, + `GoTemplateOptions:` + fmt.Sprintf("%v", this.GoTemplateOptions) + `,`, + `ApplyNestedSelectors:` + fmt.Sprintf("%v", this.ApplyNestedSelectors) + `,`, + `IgnoreApplicationDifferences:` + repeatedStringForIgnoreApplicationDifferences + `,`, + `TemplatePatch:` + valueToStringGenerated(this.TemplatePatch) + `,`, `}`, }, "") return s @@ -15871,6 +18220,7 @@ func (this *ApplicationSetSyncPolicy) String() string { } s := strings.Join([]string{`&ApplicationSetSyncPolicy{`, `PreserveResourcesOnDeletion:` + fmt.Sprintf("%v", this.PreserveResourcesOnDeletion) + `,`, + `ApplicationsSync:` + valueToStringGenerated(this.ApplicationsSync) + `,`, `}`, }, "") return s @@ -15931,6 +18281,8 @@ func (this *ApplicationSetTerminalGenerator) String() string { `SCMProvider:` + strings.Replace(this.SCMProvider.String(), "SCMProviderGenerator", "SCMProviderGenerator", 1) + `,`, `ClusterDecisionResource:` + strings.Replace(this.ClusterDecisionResource.String(), "DuckTypeGenerator", "DuckTypeGenerator", 1) + `,`, `PullRequest:` + strings.Replace(this.PullRequest.String(), "PullRequestGenerator", "PullRequestGenerator", 1) + `,`, + `Plugin:` + strings.Replace(this.Plugin.String(), "PluginGenerator", "PluginGenerator", 1) + `,`, + `Selector:` + strings.Replace(fmt.Sprintf("%v", this.Selector), "LabelSelector", "v1.LabelSelector", 1) + `,`, `}`, }, "") return s @@ -15990,6 +18342,7 @@ func (this *ApplicationSourceHelm) String() string { `PassCredentials:` + fmt.Sprintf("%v", this.PassCredentials) + `,`, `IgnoreMissingValueFiles:` + fmt.Sprintf("%v", this.IgnoreMissingValueFiles) + `,`, `SkipCrds:` + fmt.Sprintf("%v", this.SkipCrds) + `,`, + `ValuesObject:` + strings.Replace(fmt.Sprintf("%v", this.ValuesObject), "RawExtension", "runtime.RawExtension", 1) + `,`, `}`, }, "") return s @@ -16020,6 +18373,16 @@ func (this *ApplicationSourceKustomize) String() string { if this == nil { return "nil" } + repeatedStringForReplicas := "[]KustomizeReplica{" + for _, f := range this.Replicas { + repeatedStringForReplicas += strings.Replace(strings.Replace(f.String(), "KustomizeReplica", "KustomizeReplica", 1), `&`, ``, 1) + "," + } + repeatedStringForReplicas += "}" + repeatedStringForPatches := "[]KustomizePatch{" + for _, f := range this.Patches { + repeatedStringForPatches += strings.Replace(strings.Replace(f.String(), "KustomizePatch", "KustomizePatch", 1), `&`, ``, 1) + "," + } + repeatedStringForPatches += "}" keysForCommonLabels := make([]string, 0, len(this.CommonLabels)) for k := range this.CommonLabels { keysForCommonLabels = append(keysForCommonLabels, k) @@ -16049,6 +18412,12 @@ func (this *ApplicationSourceKustomize) String() string { `CommonAnnotations:` + mapStringForCommonAnnotations + `,`, `ForceCommonLabels:` + fmt.Sprintf("%v", this.ForceCommonLabels) + `,`, `ForceCommonAnnotations:` + fmt.Sprintf("%v", this.ForceCommonAnnotations) + `,`, + `Namespace:` + fmt.Sprintf("%v", this.Namespace) + `,`, + `CommonAnnotationsEnvsubst:` + fmt.Sprintf("%v", this.CommonAnnotationsEnvsubst) + `,`, + `Replicas:` + repeatedStringForReplicas + `,`, + `Patches:` + repeatedStringForPatches + `,`, + `Components:` + fmt.Sprintf("%v", this.Components) + `,`, + `LabelWithoutSelector:` + fmt.Sprintf("%v", this.LabelWithoutSelector) + `,`, `}`, }, "") return s @@ -16079,20 +18448,10 @@ func (this *ApplicationSourcePluginParameter) String() string { if this == nil { return "nil" } - keysForMap := make([]string, 0, len(this.Map)) - for k := range this.Map { - keysForMap = append(keysForMap, k) - } - github_com_gogo_protobuf_sortkeys.Strings(keysForMap) - mapStringForMap := "map[string]string{" - for _, k := range keysForMap { - mapStringForMap += fmt.Sprintf("%v: %v,", k, this.Map[k]) - } - mapStringForMap += "}" s := strings.Join([]string{`&ApplicationSourcePluginParameter{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, - `Map:` + mapStringForMap + `,`, - `Array:` + fmt.Sprintf("%v", this.Array) + `,`, + `OptionalMap:` + strings.Replace(this.OptionalMap.String(), "OptionalMap", "OptionalMap", 1) + `,`, + `OptionalArray:` + strings.Replace(this.OptionalArray.String(), "OptionalArray", "OptionalArray", 1) + `,`, `String_:` + valueToStringGenerated(this.String_) + `,`, `}`, }, "") @@ -16162,6 +18521,7 @@ func (this *ApplicationStatus) String() string { `Summary:` + strings.Replace(strings.Replace(this.Summary.String(), "ApplicationSummary", "ApplicationSummary", 1), `&`, ``, 1) + `,`, `ResourceHealthSource:` + fmt.Sprintf("%v", this.ResourceHealthSource) + `,`, `SourceTypes:` + fmt.Sprintf("%v", this.SourceTypes) + `,`, + `ControllerNamespace:` + fmt.Sprintf("%v", this.ControllerNamespace) + `,`, `}`, }, "") return s @@ -16238,6 +18598,28 @@ func (this *BasicAuthBitbucketServer) String() string { }, "") return s } +func (this *BearerTokenBitbucketCloud) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&BearerTokenBitbucketCloud{`, + `TokenRef:` + strings.Replace(this.TokenRef.String(), "SecretRef", "SecretRef", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ChartDetails) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ChartDetails{`, + `Description:` + fmt.Sprintf("%v", this.Description) + `,`, + `Home:` + fmt.Sprintf("%v", this.Home) + `,`, + `Maintainers:` + fmt.Sprintf("%v", this.Maintainers) + `,`, + `}`, + }, "") + return s +} func (this *Cluster) String() string { if this == nil { return "nil" @@ -16379,10 +18761,16 @@ func (this *ComparedTo) String() string { repeatedStringForSources += strings.Replace(strings.Replace(f.String(), "ApplicationSource", "ApplicationSource", 1), `&`, ``, 1) + "," } repeatedStringForSources += "}" + repeatedStringForIgnoreDifferences := "[]ResourceIgnoreDifferences{" + for _, f := range this.IgnoreDifferences { + repeatedStringForIgnoreDifferences += strings.Replace(strings.Replace(f.String(), "ResourceIgnoreDifferences", "ResourceIgnoreDifferences", 1), `&`, ``, 1) + "," + } + repeatedStringForIgnoreDifferences += "}" s := strings.Join([]string{`&ComparedTo{`, `Source:` + strings.Replace(strings.Replace(this.Source.String(), "ApplicationSource", "ApplicationSource", 1), `&`, ``, 1) + `,`, `Destination:` + strings.Replace(strings.Replace(this.Destination.String(), "ApplicationDestination", "ApplicationDestination", 1), `&`, ``, 1) + `,`, `Sources:` + repeatedStringForSources + `,`, + `IgnoreDifferences:` + repeatedStringForIgnoreDifferences + `,`, `}`, }, "") return s @@ -16519,6 +18907,16 @@ func (this *GitGenerator) String() string { repeatedStringForFiles += strings.Replace(strings.Replace(f.String(), "GitFileGeneratorItem", "GitFileGeneratorItem", 1), `&`, ``, 1) + "," } repeatedStringForFiles += "}" + keysForValues := make([]string, 0, len(this.Values)) + for k := range this.Values { + keysForValues = append(keysForValues, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForValues) + mapStringForValues := "map[string]string{" + for _, k := range keysForValues { + mapStringForValues += fmt.Sprintf("%v: %v,", k, this.Values[k]) + } + mapStringForValues += "}" s := strings.Join([]string{`&GitGenerator{`, `RepoURL:` + fmt.Sprintf("%v", this.RepoURL) + `,`, `Directories:` + repeatedStringForDirectories + `,`, @@ -16527,6 +18925,7 @@ func (this *GitGenerator) String() string { `RequeueAfterSeconds:` + valueToStringGenerated(this.RequeueAfterSeconds) + `,`, `Template:` + strings.Replace(strings.Replace(this.Template.String(), "ApplicationSetTemplate", "ApplicationSetTemplate", 1), `&`, ``, 1) + `,`, `PathParamPrefix:` + fmt.Sprintf("%v", this.PathParamPrefix) + `,`, + `Values:` + mapStringForValues + `,`, `}`, }, "") return s @@ -16708,6 +19107,18 @@ func (this *KnownTypeField) String() string { }, "") return s } +func (this *KustomizeGvk) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&KustomizeGvk{`, + `Group:` + fmt.Sprintf("%v", this.Group) + `,`, + `Version:` + fmt.Sprintf("%v", this.Version) + `,`, + `Kind:` + fmt.Sprintf("%v", this.Kind) + `,`, + `}`, + }, "") + return s +} func (this *KustomizeOptions) String() string { if this == nil { return "nil" @@ -16719,6 +19130,64 @@ func (this *KustomizeOptions) String() string { }, "") return s } +func (this *KustomizePatch) String() string { + if this == nil { + return "nil" + } + keysForOptions := make([]string, 0, len(this.Options)) + for k := range this.Options { + keysForOptions = append(keysForOptions, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForOptions) + mapStringForOptions := "map[string]bool{" + for _, k := range keysForOptions { + mapStringForOptions += fmt.Sprintf("%v: %v,", k, this.Options[k]) + } + mapStringForOptions += "}" + s := strings.Join([]string{`&KustomizePatch{`, + `Path:` + fmt.Sprintf("%v", this.Path) + `,`, + `Patch:` + fmt.Sprintf("%v", this.Patch) + `,`, + `Target:` + strings.Replace(this.Target.String(), "KustomizeSelector", "KustomizeSelector", 1) + `,`, + `Options:` + mapStringForOptions + `,`, + `}`, + }, "") + return s +} +func (this *KustomizeReplica) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&KustomizeReplica{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Count:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Count), "IntOrString", "intstr.IntOrString", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *KustomizeResId) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&KustomizeResId{`, + `KustomizeGvk:` + strings.Replace(strings.Replace(this.KustomizeGvk.String(), "KustomizeGvk", "KustomizeGvk", 1), `&`, ``, 1) + `,`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Namespace:` + fmt.Sprintf("%v", this.Namespace) + `,`, + `}`, + }, "") + return s +} +func (this *KustomizeSelector) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&KustomizeSelector{`, + `KustomizeResId:` + strings.Replace(strings.Replace(this.KustomizeResId.String(), "KustomizeResId", "KustomizeResId", 1), `&`, ``, 1) + `,`, + `AnnotationSelector:` + fmt.Sprintf("%v", this.AnnotationSelector) + `,`, + `LabelSelector:` + fmt.Sprintf("%v", this.LabelSelector) + `,`, + `}`, + }, "") + return s +} func (this *ListGenerator) String() string { if this == nil { return "nil" @@ -16731,6 +19200,7 @@ func (this *ListGenerator) String() string { s := strings.Join([]string{`&ListGenerator{`, `Elements:` + repeatedStringForElements + `,`, `Template:` + strings.Replace(strings.Replace(this.Template.String(), "ApplicationSetTemplate", "ApplicationSetTemplate", 1), `&`, ``, 1) + `,`, + `ElementsYaml:` + fmt.Sprintf("%v", this.ElementsYaml) + `,`, `}`, }, "") return s @@ -16875,6 +19345,36 @@ func (this *OperationState) String() string { }, "") return s } +func (this *OptionalArray) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&OptionalArray{`, + `Array:` + fmt.Sprintf("%v", this.Array) + `,`, + `}`, + }, "") + return s +} +func (this *OptionalMap) String() string { + if this == nil { + return "nil" + } + keysForMap := make([]string, 0, len(this.Map)) + for k := range this.Map { + keysForMap = append(keysForMap, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForMap) + mapStringForMap := "map[string]string{" + for _, k := range keysForMap { + mapStringForMap += fmt.Sprintf("%v: %v,", k, this.Map[k]) + } + mapStringForMap += "}" + s := strings.Join([]string{`&OptionalMap{`, + `Map:` + mapStringForMap + `,`, + `}`, + }, "") + return s +} func (this *OrphanedResourceKey) String() string { if this == nil { return "nil" @@ -16915,6 +19415,60 @@ func (this *OverrideIgnoreDiff) String() string { }, "") return s } +func (this *PluginConfigMapRef) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PluginConfigMapRef{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `}`, + }, "") + return s +} +func (this *PluginGenerator) String() string { + if this == nil { + return "nil" + } + keysForValues := make([]string, 0, len(this.Values)) + for k := range this.Values { + keysForValues = append(keysForValues, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForValues) + mapStringForValues := "map[string]string{" + for _, k := range keysForValues { + mapStringForValues += fmt.Sprintf("%v: %v,", k, this.Values[k]) + } + mapStringForValues += "}" + s := strings.Join([]string{`&PluginGenerator{`, + `ConfigMapRef:` + strings.Replace(strings.Replace(this.ConfigMapRef.String(), "PluginConfigMapRef", "PluginConfigMapRef", 1), `&`, ``, 1) + `,`, + `Input:` + strings.Replace(strings.Replace(this.Input.String(), "PluginInput", "PluginInput", 1), `&`, ``, 1) + `,`, + `RequeueAfterSeconds:` + valueToStringGenerated(this.RequeueAfterSeconds) + `,`, + `Template:` + strings.Replace(strings.Replace(this.Template.String(), "ApplicationSetTemplate", "ApplicationSetTemplate", 1), `&`, ``, 1) + `,`, + `Values:` + mapStringForValues + `,`, + `}`, + }, "") + return s +} +func (this *PluginInput) String() string { + if this == nil { + return "nil" + } + keysForParameters := make([]string, 0, len(this.Parameters)) + for k := range this.Parameters { + keysForParameters = append(keysForParameters, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForParameters) + mapStringForParameters := "PluginParameters{" + for _, k := range keysForParameters { + mapStringForParameters += fmt.Sprintf("%v: %v,", k, this.Parameters[k]) + } + mapStringForParameters += "}" + s := strings.Join([]string{`&PluginInput{`, + `Parameters:` + mapStringForParameters + `,`, + `}`, + }, "") + return s +} func (this *ProjectRole) String() string { if this == nil { return "nil" @@ -16951,6 +19505,37 @@ func (this *PullRequestGenerator) String() string { `Filters:` + repeatedStringForFilters + `,`, `RequeueAfterSeconds:` + valueToStringGenerated(this.RequeueAfterSeconds) + `,`, `Template:` + strings.Replace(strings.Replace(this.Template.String(), "ApplicationSetTemplate", "ApplicationSetTemplate", 1), `&`, ``, 1) + `,`, + `Bitbucket:` + strings.Replace(this.Bitbucket.String(), "PullRequestGeneratorBitbucket", "PullRequestGeneratorBitbucket", 1) + `,`, + `AzureDevOps:` + strings.Replace(this.AzureDevOps.String(), "PullRequestGeneratorAzureDevOps", "PullRequestGeneratorAzureDevOps", 1) + `,`, + `}`, + }, "") + return s +} +func (this *PullRequestGeneratorAzureDevOps) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PullRequestGeneratorAzureDevOps{`, + `Organization:` + fmt.Sprintf("%v", this.Organization) + `,`, + `Project:` + fmt.Sprintf("%v", this.Project) + `,`, + `Repo:` + fmt.Sprintf("%v", this.Repo) + `,`, + `API:` + fmt.Sprintf("%v", this.API) + `,`, + `TokenRef:` + strings.Replace(this.TokenRef.String(), "SecretRef", "SecretRef", 1) + `,`, + `Labels:` + fmt.Sprintf("%v", this.Labels) + `,`, + `}`, + }, "") + return s +} +func (this *PullRequestGeneratorBitbucket) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PullRequestGeneratorBitbucket{`, + `Owner:` + fmt.Sprintf("%v", this.Owner) + `,`, + `Repo:` + fmt.Sprintf("%v", this.Repo) + `,`, + `API:` + fmt.Sprintf("%v", this.API) + `,`, + `BasicAuth:` + strings.Replace(this.BasicAuth.String(), "BasicAuthBitbucketServer", "BasicAuthBitbucketServer", 1) + `,`, + `BearerToken:` + strings.Replace(this.BearerToken.String(), "BearerTokenBitbucketCloud", "BearerTokenBitbucketCloud", 1) + `,`, `}`, }, "") return s @@ -16974,6 +19559,7 @@ func (this *PullRequestGeneratorFilter) String() string { } s := strings.Join([]string{`&PullRequestGeneratorFilter{`, `BranchMatch:` + valueToStringGenerated(this.BranchMatch) + `,`, + `TargetBranchMatch:` + valueToStringGenerated(this.TargetBranchMatch) + `,`, `}`, }, "") return s @@ -16988,6 +19574,7 @@ func (this *PullRequestGeneratorGitLab) String() string { `TokenRef:` + strings.Replace(this.TokenRef.String(), "SecretRef", "SecretRef", 1) + `,`, `Labels:` + fmt.Sprintf("%v", this.Labels) + `,`, `PullRequestState:` + fmt.Sprintf("%v", this.PullRequestState) + `,`, + `Insecure:` + fmt.Sprintf("%v", this.Insecure) + `,`, `}`, }, "") return s @@ -17052,6 +19639,7 @@ func (this *RepoCreds) String() string { `Type:` + fmt.Sprintf("%v", this.Type) + `,`, `GCPServiceAccountKey:` + fmt.Sprintf("%v", this.GCPServiceAccountKey) + `,`, `Proxy:` + fmt.Sprintf("%v", this.Proxy) + `,`, + `ForceHttpBasicAuth:` + fmt.Sprintf("%v", this.ForceHttpBasicAuth) + `,`, `}`, }, "") return s @@ -17098,6 +19686,7 @@ func (this *Repository) String() string { `Proxy:` + fmt.Sprintf("%v", this.Proxy) + `,`, `Project:` + fmt.Sprintf("%v", this.Project) + `,`, `GCPServiceAccountKey:` + fmt.Sprintf("%v", this.GCPServiceAccountKey) + `,`, + `ForceHttpBasicAuth:` + fmt.Sprintf("%v", this.ForceHttpBasicAuth) + `,`, `}`, }, "") return s @@ -17161,6 +19750,8 @@ func (this *ResourceAction) String() string { `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Params:` + repeatedStringForParams + `,`, `Disabled:` + fmt.Sprintf("%v", this.Disabled) + `,`, + `IconClass:` + fmt.Sprintf("%v", this.IconClass) + `,`, + `DisplayName:` + fmt.Sprintf("%v", this.DisplayName) + `,`, `}`, }, "") return s @@ -17328,6 +19919,7 @@ func (this *ResourceOverride) String() string { `Actions:` + fmt.Sprintf("%v", this.Actions) + `,`, `KnownTypeFields:` + repeatedStringForKnownTypeFields + `,`, `UseOpenLibs:` + fmt.Sprintf("%v", this.UseOpenLibs) + `,`, + `IgnoreResourceUpdates:` + strings.Replace(strings.Replace(this.IgnoreResourceUpdates.String(), "OverrideIgnoreDiff", "OverrideIgnoreDiff", 1), `&`, ``, 1) + `,`, `}`, }, "") return s @@ -17413,6 +20005,7 @@ func (this *RevisionHistory) String() string { `DeployStartedAt:` + strings.Replace(fmt.Sprintf("%v", this.DeployStartedAt), "Time", "v1.Time", 1) + `,`, `Sources:` + repeatedStringForSources + `,`, `Revisions:` + fmt.Sprintf("%v", this.Revisions) + `,`, + `InitiatedBy:` + strings.Replace(strings.Replace(this.InitiatedBy.String(), "OperationInitiator", "OperationInitiator", 1), `&`, ``, 1) + `,`, `}`, }, "") return s @@ -17440,6 +20033,16 @@ func (this *SCMProviderGenerator) String() string { repeatedStringForFilters += strings.Replace(strings.Replace(f.String(), "SCMProviderGeneratorFilter", "SCMProviderGeneratorFilter", 1), `&`, ``, 1) + "," } repeatedStringForFilters += "}" + keysForValues := make([]string, 0, len(this.Values)) + for k := range this.Values { + keysForValues = append(keysForValues, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForValues) + mapStringForValues := "map[string]string{" + for _, k := range keysForValues { + mapStringForValues += fmt.Sprintf("%v: %v,", k, this.Values[k]) + } + mapStringForValues += "}" s := strings.Join([]string{`&SCMProviderGenerator{`, `Github:` + strings.Replace(this.Github.String(), "SCMProviderGeneratorGithub", "SCMProviderGeneratorGithub", 1) + `,`, `Gitlab:` + strings.Replace(this.Gitlab.String(), "SCMProviderGeneratorGitlab", "SCMProviderGeneratorGitlab", 1) + `,`, @@ -17451,6 +20054,26 @@ func (this *SCMProviderGenerator) String() string { `CloneProtocol:` + fmt.Sprintf("%v", this.CloneProtocol) + `,`, `RequeueAfterSeconds:` + valueToStringGenerated(this.RequeueAfterSeconds) + `,`, `Template:` + strings.Replace(strings.Replace(this.Template.String(), "ApplicationSetTemplate", "ApplicationSetTemplate", 1), `&`, ``, 1) + `,`, + `Values:` + mapStringForValues + `,`, + `AWSCodeCommit:` + strings.Replace(this.AWSCodeCommit.String(), "SCMProviderGeneratorAWSCodeCommit", "SCMProviderGeneratorAWSCodeCommit", 1) + `,`, + `}`, + }, "") + return s +} +func (this *SCMProviderGeneratorAWSCodeCommit) String() string { + if this == nil { + return "nil" + } + repeatedStringForTagFilters := "[]*TagFilter{" + for _, f := range this.TagFilters { + repeatedStringForTagFilters += strings.Replace(f.String(), "TagFilter", "TagFilter", 1) + "," + } + repeatedStringForTagFilters += "}" + s := strings.Join([]string{`&SCMProviderGeneratorAWSCodeCommit{`, + `TagFilters:` + repeatedStringForTagFilters + `,`, + `Role:` + fmt.Sprintf("%v", this.Role) + `,`, + `Region:` + fmt.Sprintf("%v", this.Region) + `,`, + `AllBranches:` + fmt.Sprintf("%v", this.AllBranches) + `,`, `}`, }, "") return s @@ -17547,6 +20170,9 @@ func (this *SCMProviderGeneratorGitlab) String() string { `API:` + fmt.Sprintf("%v", this.API) + `,`, `TokenRef:` + strings.Replace(this.TokenRef.String(), "SecretRef", "SecretRef", 1) + `,`, `AllBranches:` + fmt.Sprintf("%v", this.AllBranches) + `,`, + `Insecure:` + fmt.Sprintf("%v", this.Insecure) + `,`, + `IncludeSharedProjects:` + valueToStringGenerated(this.IncludeSharedProjects) + `,`, + `Topic:` + fmt.Sprintf("%v", this.Topic) + `,`, `}`, }, "") return s @@ -17634,6 +20260,7 @@ func (this *SyncOperationResult) String() string { `Source:` + strings.Replace(strings.Replace(this.Source.String(), "ApplicationSource", "ApplicationSource", 1), `&`, ``, 1) + `,`, `Sources:` + repeatedStringForSources + `,`, `Revisions:` + fmt.Sprintf("%v", this.Revisions) + `,`, + `ManagedNamespaceMetadata:` + strings.Replace(this.ManagedNamespaceMetadata.String(), "ManagedNamespaceMetadata", "ManagedNamespaceMetadata", 1) + `,`, `}`, }, "") return s @@ -17738,6 +20365,17 @@ func (this *TLSClientConfig) String() string { }, "") return s } +func (this *TagFilter) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TagFilter{`, + `Key:` + fmt.Sprintf("%v", this.Key) + `,`, + `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `}`, + }, "") + return s +} func valueToStringGenerated(v interface{}) string { rv := reflect.ValueOf(v) if rv.IsNil() { @@ -17839,6 +20477,38 @@ func (m *AWSAuthConfig) Unmarshal(dAtA []byte) error { } m.RoleARN = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Profile", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Profile = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -19523,6 +22193,120 @@ func (m *ApplicationMatchExpression) Unmarshal(dAtA []byte) error { } return nil } +func (m *ApplicationPreservedFields) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ApplicationPreservedFields: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ApplicationPreservedFields: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Annotations", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Annotations = append(m.Annotations, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Labels = append(m.Labels, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *ApplicationSet) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -19801,7 +22585,7 @@ func (m *ApplicationSetApplicationStatus) Unmarshal(dAtA []byte) error { } m.Message = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } @@ -19833,6 +22617,38 @@ func (m *ApplicationSetApplicationStatus) Unmarshal(dAtA []byte) error { } m.Status = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Step", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Step = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -20421,6 +23237,42 @@ func (m *ApplicationSetGenerator) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Plugin", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Plugin == nil { + m.Plugin = &PluginGenerator{} + } + if err := m.Plugin.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -20912,6 +23764,188 @@ func (m *ApplicationSetNestedGenerator) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Plugin", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Plugin == nil { + m.Plugin = &PluginGenerator{} + } + if err := m.Plugin.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ApplicationSetResourceIgnoreDifferences) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ApplicationSetResourceIgnoreDifferences: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ApplicationSetResourceIgnoreDifferences: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field JSONPointers", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.JSONPointers = append(m.JSONPointers, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field JQPathExpressions", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.JQPathExpressions = append(m.JQPathExpressions, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -21325,6 +24359,161 @@ func (m *ApplicationSetSpec) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PreservedFields", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PreservedFields == nil { + m.PreservedFields = &ApplicationPreservedFields{} + } + if err := m.PreservedFields.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GoTemplateOptions", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.GoTemplateOptions = append(m.GoTemplateOptions, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ApplyNestedSelectors", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ApplyNestedSelectors = bool(v != 0) + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IgnoreApplicationDifferences", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IgnoreApplicationDifferences = append(m.IgnoreApplicationDifferences, ApplicationSetResourceIgnoreDifferences{}) + if err := m.IgnoreApplicationDifferences[len(m.IgnoreApplicationDifferences)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TemplatePatch", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.TemplatePatch = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -21631,6 +24820,39 @@ func (m *ApplicationSetSyncPolicy) Unmarshal(dAtA []byte) error { } } m.PreserveResourcesOnDeletion = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ApplicationsSync", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := ApplicationsSyncPolicy(dAtA[iNdEx:postIndex]) + m.ApplicationsSync = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -22413,155 +25635,9 @@ func (m *ApplicationSetTerminalGenerator) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipGenerated(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ApplicationSource) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ApplicationSource: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ApplicationSource: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RepoURL", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.RepoURL = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TargetRevision", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TargetRevision = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Helm", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Plugin", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -22588,88 +25664,306 @@ func (m *ApplicationSource) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Helm == nil { - m.Helm = &ApplicationSourceHelm{} + if m.Plugin == nil { + m.Plugin = &PluginGenerator{} } - if err := m.Helm.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Plugin.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Kustomize", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Kustomize == nil { - m.Kustomize = &ApplicationSourceKustomize{} - } - if err := m.Kustomize.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 10: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Directory", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Directory == nil { - m.Directory = &ApplicationSourceDirectory{} - } - if err := m.Directory.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 11: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Plugin", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Selector", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Selector == nil { + m.Selector = &v1.LabelSelector{} + } + if err := m.Selector.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ApplicationSource) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ApplicationSource: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ApplicationSource: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RepoURL", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RepoURL = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TargetRevision", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TargetRevision = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Helm", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Helm == nil { + m.Helm = &ApplicationSourceHelm{} + } + if err := m.Helm.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Kustomize", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Kustomize == nil { + m.Kustomize = &ApplicationSourceKustomize{} + } + if err := m.Kustomize.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Directory", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Directory == nil { + m.Directory = &ApplicationSourceDirectory{} + } + if err := m.Directory.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Plugin", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -23240,6 +26534,42 @@ func (m *ApplicationSourceHelm) Unmarshal(dAtA []byte) error { } } m.SkipCrds = bool(v != 0) + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValuesObject", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ValuesObject == nil { + m.ValuesObject = &runtime.RawExtension{} + } + if err := m.ValuesObject.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -23862,6 +27192,178 @@ func (m *ApplicationSourceKustomize) Unmarshal(dAtA []byte) error { } } m.ForceCommonAnnotations = bool(v != 0) + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Namespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CommonAnnotationsEnvsubst", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.CommonAnnotationsEnvsubst = bool(v != 0) + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Replicas", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Replicas = append(m.Replicas, KustomizeReplica{}) + if err := m.Replicas[len(m.Replicas)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Patches", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Patches = append(m.Patches, KustomizePatch{}) + if err := m.Patches[len(m.Patches)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Components", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Components = append(m.Components, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 14: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LabelWithoutSelector", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.LabelWithoutSelector = bool(v != 0) default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -24096,7 +27598,7 @@ func (m *ApplicationSourcePluginParameter) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Map", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field OptionalMap", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -24123,109 +27625,18 @@ func (m *ApplicationSourcePluginParameter) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Map == nil { - m.Map = make(map[string]string) + if m.OptionalMap == nil { + m.OptionalMap = &OptionalMap{} } - var mapkey string - var mapvalue string - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthGenerated - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey < 0 { - return ErrInvalidLengthGenerated - } - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var stringLenmapvalue uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapvalue |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapvalue := int(stringLenmapvalue) - if intStringLenmapvalue < 0 { - return ErrInvalidLengthGenerated - } - postStringIndexmapvalue := iNdEx + intStringLenmapvalue - if postStringIndexmapvalue < 0 { - return ErrInvalidLengthGenerated - } - if postStringIndexmapvalue > l { - return io.ErrUnexpectedEOF - } - mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) - iNdEx = postStringIndexmapvalue - } else { - iNdEx = entryPreIndex - skippy, err := skipGenerated(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } + if err := m.OptionalMap.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } - m.Map[mapkey] = mapvalue iNdEx = postIndex case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Array", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field OptionalArray", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -24235,23 +27646,27 @@ func (m *ApplicationSourcePluginParameter) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.Array = append(m.Array, string(dAtA[iNdEx:postIndex])) + if m.OptionalArray == nil { + m.OptionalArray = &OptionalArray{} + } + if err := m.OptionalArray.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex case 5: if wireType != 2 { @@ -25050,6 +28465,38 @@ func (m *ApplicationStatus) Unmarshal(dAtA []byte) error { } m.SourceTypes = append(m.SourceTypes, ApplicationSourceType(dAtA[iNdEx:postIndex])) iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ControllerNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ControllerNamespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -25704,7 +29151,7 @@ func (m *BasicAuthBitbucketServer) Unmarshal(dAtA []byte) error { } return nil } -func (m *Cluster) Unmarshal(dAtA []byte) error { +func (m *BearerTokenBitbucketCloud) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -25727,17 +29174,17 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: Cluster: wiretype end group for non-group") + return fmt.Errorf("proto: BearerTokenBitbucketCloud: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: Cluster: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: BearerTokenBitbucketCloud: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Server", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field TokenRef", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -25747,27 +29194,81 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.Server = string(dAtA[iNdEx:postIndex]) + if m.TokenRef == nil { + m.TokenRef = &SecretRef{} + } + if err := m.TokenRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 2: + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ChartDetails) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ChartDetails: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ChartDetails: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -25795,13 +29296,13 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Name = string(dAtA[iNdEx:postIndex]) + m.Description = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Home", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -25811,30 +29312,29 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Config.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Home = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConnectionState", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Maintainers", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -25844,28 +29344,77 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.ConnectionState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Maintainers = append(m.Maintainers, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { return err } - iNdEx = postIndex - case 5: + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Cluster) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Cluster: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Cluster: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ServerVersion", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Server", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -25893,11 +29442,11 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ServerVersion = string(dAtA[iNdEx:postIndex]) + m.Server = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Namespaces", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -25925,11 +29474,11 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Namespaces = append(m.Namespaces, string(dAtA[iNdEx:postIndex])) + m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 7: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RefreshRequestedAt", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -25956,16 +29505,13 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.RefreshRequestedAt == nil { - m.RefreshRequestedAt = &v1.Time{} - } - if err := m.RefreshRequestedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Config.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 8: + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Info", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionState", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -25992,15 +29538,15 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Info.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.ConnectionState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 9: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Shard", wireType) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ServerVersion", wireType) } - var v int64 + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -26010,35 +29556,27 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int64(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - m.Shard = &v - case 10: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ClusterResources", wireType) + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated } - m.ClusterResources = bool(v != 0) - case 11: + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ServerVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Namespaces", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -26066,11 +29604,152 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Project = string(dAtA[iNdEx:postIndex]) + m.Namespaces = append(m.Namespaces, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 12: + case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field RefreshRequestedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.RefreshRequestedAt == nil { + m.RefreshRequestedAt = &v1.Time{} + } + if err := m.RefreshRequestedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Info", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Info.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Shard", wireType) + } + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Shard = &v + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ClusterResources", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ClusterResources = bool(v != 0) + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Project = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -27520,93 +31199,11 @@ func (m *ComparedTo) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipGenerated(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ComponentParameter) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ComponentParameter: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ComponentParameter: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Component", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Component = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field IgnoreDifferences", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -27616,55 +31213,171 @@ func (m *ComponentParameter) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF + m.IgnoreDifferences = append(m.IgnoreDifferences, ResourceIgnoreDifferences{}) + if err := m.IgnoreDifferences[len(m.IgnoreDifferences)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } - m.Value = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ComponentParameter) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ComponentParameter: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ComponentParameter: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Component", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Component = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -29184,6 +32897,133 @@ func (m *GitGenerator) Unmarshal(dAtA []byte) error { } m.PathParamPrefix = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Values", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Values == nil { + m.Values = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Values[mapkey] = mapvalue + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -30976,7 +34816,7 @@ func (m *KnownTypeField) Unmarshal(dAtA []byte) error { } return nil } -func (m *KustomizeOptions) Unmarshal(dAtA []byte) error { +func (m *KustomizeGvk) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -30999,15 +34839,15 @@ func (m *KustomizeOptions) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: KustomizeOptions: wiretype end group for non-group") + return fmt.Errorf("proto: KustomizeGvk: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: KustomizeOptions: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: KustomizeGvk: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BuildOptions", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Group", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -31035,11 +34875,11 @@ func (m *KustomizeOptions) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.BuildOptions = string(dAtA[iNdEx:postIndex]) + m.Group = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BinaryPath", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -31067,7 +34907,39 @@ func (m *KustomizeOptions) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.BinaryPath = string(dAtA[iNdEx:postIndex]) + m.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Kind = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -31090,7 +34962,7 @@ func (m *KustomizeOptions) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListGenerator) Unmarshal(dAtA []byte) error { +func (m *KustomizeOptions) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -31113,17 +34985,17 @@ func (m *ListGenerator) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListGenerator: wiretype end group for non-group") + return fmt.Errorf("proto: KustomizeOptions: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListGenerator: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: KustomizeOptions: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Elements", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BuildOptions", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -31133,31 +35005,29 @@ func (m *ListGenerator) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.Elements = append(m.Elements, v11.JSON{}) - if err := m.Elements[len(m.Elements)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.BuildOptions = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BinaryPath", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -31167,24 +35037,23 @@ func (m *ListGenerator) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Template.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.BinaryPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -31207,7 +35076,7 @@ func (m *ListGenerator) Unmarshal(dAtA []byte) error { } return nil } -func (m *ManagedNamespaceMetadata) Unmarshal(dAtA []byte) error { +func (m *KustomizePatch) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -31230,15 +35099,79 @@ func (m *ManagedNamespaceMetadata) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ManagedNamespaceMetadata: wiretype end group for non-group") + return fmt.Errorf("proto: KustomizePatch: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ManagedNamespaceMetadata: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: KustomizePatch: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Patch", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Patch = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -31265,107 +35198,16 @@ func (m *ManagedNamespaceMetadata) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Labels == nil { - m.Labels = make(map[string]string) + if m.Target == nil { + m.Target = &KustomizeSelector{} } - var mapkey string - var mapvalue string - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthGenerated - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey < 0 { - return ErrInvalidLengthGenerated - } - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var stringLenmapvalue uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapvalue |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapvalue := int(stringLenmapvalue) - if intStringLenmapvalue < 0 { - return ErrInvalidLengthGenerated - } - postStringIndexmapvalue := iNdEx + intStringLenmapvalue - if postStringIndexmapvalue < 0 { - return ErrInvalidLengthGenerated - } - if postStringIndexmapvalue > l { - return io.ErrUnexpectedEOF - } - mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) - iNdEx = postStringIndexmapvalue - } else { - iNdEx = entryPreIndex - skippy, err := skipGenerated(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } + if err := m.Target.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } - m.Labels[mapkey] = mapvalue iNdEx = postIndex - case 2: + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Annotations", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -31392,11 +35234,11 @@ func (m *ManagedNamespaceMetadata) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Annotations == nil { - m.Annotations = make(map[string]string) + if m.Options == nil { + m.Options = make(map[string]bool) } var mapkey string - var mapvalue string + var mapvalue bool for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 @@ -31445,7 +35287,7 @@ func (m *ManagedNamespaceMetadata) Unmarshal(dAtA []byte) error { mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { - var stringLenmapvalue uint64 + var mapvaluetemp int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -31455,24 +35297,12 @@ func (m *ManagedNamespaceMetadata) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= uint64(b&0x7F) << shift + mapvaluetemp |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLenmapvalue := int(stringLenmapvalue) - if intStringLenmapvalue < 0 { - return ErrInvalidLengthGenerated - } - postStringIndexmapvalue := iNdEx + intStringLenmapvalue - if postStringIndexmapvalue < 0 { - return ErrInvalidLengthGenerated - } - if postStringIndexmapvalue > l { - return io.ErrUnexpectedEOF - } - mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) - iNdEx = postStringIndexmapvalue + mapvalue = bool(mapvaluetemp != 0) } else { iNdEx = entryPreIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -31488,7 +35318,7 @@ func (m *ManagedNamespaceMetadata) Unmarshal(dAtA []byte) error { iNdEx += skippy } } - m.Annotations[mapkey] = mapvalue + m.Options[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex @@ -31511,7 +35341,7 @@ func (m *ManagedNamespaceMetadata) Unmarshal(dAtA []byte) error { } return nil } -func (m *MatrixGenerator) Unmarshal(dAtA []byte) error { +func (m *KustomizeReplica) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -31534,17 +35364,17 @@ func (m *MatrixGenerator) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MatrixGenerator: wiretype end group for non-group") + return fmt.Errorf("proto: KustomizeReplica: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MatrixGenerator: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: KustomizeReplica: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Generators", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -31554,29 +35384,27 @@ func (m *MatrixGenerator) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.Generators = append(m.Generators, ApplicationSetNestedGenerator{}) - if err := m.Generators[len(m.Generators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Count", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -31603,7 +35431,7 @@ func (m *MatrixGenerator) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Template.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Count.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -31628,7 +35456,7 @@ func (m *MatrixGenerator) Unmarshal(dAtA []byte) error { } return nil } -func (m *MergeGenerator) Unmarshal(dAtA []byte) error { +func (m *KustomizeResId) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -31651,15 +35479,15 @@ func (m *MergeGenerator) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MergeGenerator: wiretype end group for non-group") + return fmt.Errorf("proto: KustomizeResId: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MergeGenerator: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: KustomizeResId: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Generators", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field KustomizeGvk", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -31686,14 +35514,13 @@ func (m *MergeGenerator) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Generators = append(m.Generators, ApplicationSetNestedGenerator{}) - if err := m.Generators[len(m.Generators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.KustomizeGvk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field MergeKeys", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -31721,13 +35548,13 @@ func (m *MergeGenerator) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.MergeKeys = append(m.MergeKeys, string(dAtA[iNdEx:postIndex])) + m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -31737,24 +35564,23 @@ func (m *MergeGenerator) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Template.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -31777,7 +35603,7 @@ func (m *MergeGenerator) Unmarshal(dAtA []byte) error { } return nil } -func (m *NestedMatrixGenerator) Unmarshal(dAtA []byte) error { +func (m *KustomizeSelector) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -31800,15 +35626,15 @@ func (m *NestedMatrixGenerator) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: NestedMatrixGenerator: wiretype end group for non-group") + return fmt.Errorf("proto: KustomizeSelector: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: NestedMatrixGenerator: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: KustomizeSelector: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Generators", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field KustomizeResId", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -31835,66 +35661,15 @@ func (m *NestedMatrixGenerator) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Generators = append(m.Generators, ApplicationSetTerminalGenerator{}) - if err := m.Generators[len(m.Generators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.KustomizeResId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipGenerated(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *NestedMergeGenerator) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: NestedMergeGenerator: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: NestedMergeGenerator: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Generators", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AnnotationSelector", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -31904,29 +35679,27 @@ func (m *NestedMergeGenerator) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.Generators = append(m.Generators, ApplicationSetTerminalGenerator{}) - if err := m.Generators[len(m.Generators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.AnnotationSelector = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field MergeKeys", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field LabelSelector", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -31954,7 +35727,7 @@ func (m *NestedMergeGenerator) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.MergeKeys = append(m.MergeKeys, string(dAtA[iNdEx:postIndex])) + m.LabelSelector = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -31977,7 +35750,7 @@ func (m *NestedMergeGenerator) Unmarshal(dAtA []byte) error { } return nil } -func (m *Operation) Unmarshal(dAtA []byte) error { +func (m *ListGenerator) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -32000,15 +35773,15 @@ func (m *Operation) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: Operation: wiretype end group for non-group") + return fmt.Errorf("proto: ListGenerator: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: Operation: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListGenerator: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Sync", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Elements", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -32035,16 +35808,14 @@ func (m *Operation) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Sync == nil { - m.Sync = &SyncOperation{} - } - if err := m.Sync.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Elements = append(m.Elements, v11.JSON{}) + if err := m.Elements[len(m.Elements)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field InitiatedBy", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -32071,13 +35842,95 @@ func (m *Operation) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.InitiatedBy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Template.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Info", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ElementsYaml", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ElementsYaml = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ManagedNamespaceMetadata) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ManagedNamespaceMetadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ManagedNamespaceMetadata: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -32104,14 +35957,107 @@ func (m *Operation) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Info = append(m.Info, &Info{}) - if err := m.Info[len(m.Info)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } } + m.Labels[mapkey] = mapvalue iNdEx = postIndex - case 4: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Retry", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Annotations", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -32138,9 +36084,103 @@ func (m *Operation) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Retry.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + if m.Annotations == nil { + m.Annotations = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } } + m.Annotations[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex @@ -32163,7 +36203,7 @@ func (m *Operation) Unmarshal(dAtA []byte) error { } return nil } -func (m *OperationInitiator) Unmarshal(dAtA []byte) error { +func (m *MatrixGenerator) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -32186,17 +36226,17 @@ func (m *OperationInitiator) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: OperationInitiator: wiretype end group for non-group") + return fmt.Errorf("proto: MatrixGenerator: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: OperationInitiator: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MatrixGenerator: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Generators", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -32206,29 +36246,31 @@ func (m *OperationInitiator) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.Username = string(dAtA[iNdEx:postIndex]) + m.Generators = append(m.Generators, ApplicationSetNestedGenerator{}) + if err := m.Generators[len(m.Generators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Automated", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) } - var v int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -32238,12 +36280,25 @@ func (m *OperationInitiator) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - m.Automated = bool(v != 0) + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Template.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -32265,7 +36320,7 @@ func (m *OperationInitiator) Unmarshal(dAtA []byte) error { } return nil } -func (m *OperationState) Unmarshal(dAtA []byte) error { +func (m *MergeGenerator) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -32288,15 +36343,15 @@ func (m *OperationState) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: OperationState: wiretype end group for non-group") + return fmt.Errorf("proto: MergeGenerator: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: OperationState: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MergeGenerator: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Operation", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Generators", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -32323,13 +36378,14 @@ func (m *OperationState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Operation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Generators = append(m.Generators, ApplicationSetNestedGenerator{}) + if err := m.Generators[len(m.Generators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Phase", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field MergeKeys", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -32357,43 +36413,11 @@ func (m *OperationState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Phase = github_com_argoproj_gitops_engine_pkg_sync_common.OperationPhase(dAtA[iNdEx:postIndex]) + m.MergeKeys = append(m.MergeKeys, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Message = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SyncResult", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -32420,18 +36444,65 @@ func (m *OperationState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.SyncResult == nil { - m.SyncResult = &SyncOperationResult{} - } - if err := m.SyncResult.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Template.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field StartedAt", wireType) + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err } - var msglen int + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NestedMatrixGenerator) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NestedMatrixGenerator: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NestedMatrixGenerator: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Generators", wireType) + } + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -32456,13 +36527,64 @@ func (m *OperationState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.StartedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Generators = append(m.Generators, ApplicationSetTerminalGenerator{}) + if err := m.Generators[len(m.Generators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 7: + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NestedMergeGenerator) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NestedMergeGenerator: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NestedMergeGenerator: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FinishedAt", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Generators", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -32489,18 +36611,16 @@ func (m *OperationState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.FinishedAt == nil { - m.FinishedAt = &v1.Time{} - } - if err := m.FinishedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Generators = append(m.Generators, ApplicationSetTerminalGenerator{}) + if err := m.Generators[len(m.Generators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 8: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field RetryCount", wireType) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MergeKeys", wireType) } - m.RetryCount = 0 + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -32510,11 +36630,24 @@ func (m *OperationState) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.RetryCount |= int64(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MergeKeys = append(m.MergeKeys, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -32536,7 +36669,7 @@ func (m *OperationState) Unmarshal(dAtA []byte) error { } return nil } -func (m *OrphanedResourceKey) Unmarshal(dAtA []byte) error { +func (m *Operation) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -32559,17 +36692,17 @@ func (m *OrphanedResourceKey) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: OrphanedResourceKey: wiretype end group for non-group") + return fmt.Errorf("proto: Operation: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: OrphanedResourceKey: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: Operation: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Group", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Sync", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -32579,29 +36712,33 @@ func (m *OrphanedResourceKey) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.Group = string(dAtA[iNdEx:postIndex]) + if m.Sync == nil { + m.Sync = &SyncOperation{} + } + if err := m.Sync.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field InitiatedBy", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -32611,29 +36748,30 @@ func (m *OrphanedResourceKey) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.Kind = string(dAtA[iNdEx:postIndex]) + if err := m.InitiatedBy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Info", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -32643,23 +36781,58 @@ func (m *OrphanedResourceKey) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.Name = string(dAtA[iNdEx:postIndex]) + m.Info = append(m.Info, &Info{}) + if err := m.Info[len(m.Info)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Retry", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Retry.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -32682,7 +36855,7 @@ func (m *OrphanedResourceKey) Unmarshal(dAtA []byte) error { } return nil } -func (m *OrphanedResourcesMonitorSettings) Unmarshal(dAtA []byte) error { +func (m *OperationInitiator) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -32705,17 +36878,17 @@ func (m *OrphanedResourcesMonitorSettings) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: OrphanedResourcesMonitorSettings: wiretype end group for non-group") + return fmt.Errorf("proto: OperationInitiator: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: OrphanedResourcesMonitorSettings: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: OperationInitiator: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Warn", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) } - var v int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -32725,18 +36898,29 @@ func (m *OrphanedResourcesMonitorSettings) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - b := bool(v != 0) - m.Warn = &b + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Username = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Ignore", wireType) + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Automated", wireType) } - var msglen int + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -32746,26 +36930,12 @@ func (m *OrphanedResourcesMonitorSettings) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Ignore = append(m.Ignore, OrphanedResourceKey{}) - if err := m.Ignore[len(m.Ignore)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex + m.Automated = bool(v != 0) default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -32787,7 +36957,7 @@ func (m *OrphanedResourcesMonitorSettings) Unmarshal(dAtA []byte) error { } return nil } -func (m *OverrideIgnoreDiff) Unmarshal(dAtA []byte) error { +func (m *OperationState) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -32810,17 +36980,17 @@ func (m *OverrideIgnoreDiff) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: OverrideIgnoreDiff: wiretype end group for non-group") + return fmt.Errorf("proto: OperationState: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: OverrideIgnoreDiff: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: OperationState: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field JSONPointers", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Operation", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -32830,27 +37000,28 @@ func (m *OverrideIgnoreDiff) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.JSONPointers = append(m.JSONPointers, string(dAtA[iNdEx:postIndex])) + if err := m.Operation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field JQPathExpressions", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Phase", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -32878,11 +37049,11 @@ func (m *OverrideIgnoreDiff) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.JQPathExpressions = append(m.JQPathExpressions, string(dAtA[iNdEx:postIndex])) + m.Phase = github_com_argoproj_gitops_engine_pkg_sync_common.OperationPhase(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ManagedFieldsManagers", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -32910,8 +37081,132 @@ func (m *OverrideIgnoreDiff) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ManagedFieldsManagers = append(m.ManagedFieldsManagers, string(dAtA[iNdEx:postIndex])) + m.Message = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SyncResult", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SyncResult == nil { + m.SyncResult = &SyncOperationResult{} + } + if err := m.SyncResult.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StartedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.StartedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FinishedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.FinishedAt == nil { + m.FinishedAt = &v1.Time{} + } + if err := m.FinishedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RetryCount", wireType) + } + m.RetryCount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RetryCount |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -32933,7 +37228,7 @@ func (m *OverrideIgnoreDiff) Unmarshal(dAtA []byte) error { } return nil } -func (m *ProjectRole) Unmarshal(dAtA []byte) error { +func (m *OptionalArray) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -32956,15 +37251,15 @@ func (m *ProjectRole) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ProjectRole: wiretype end group for non-group") + return fmt.Errorf("proto: OptionalArray: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ProjectRole: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: OptionalArray: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Array", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -32992,13 +37287,63 @@ func (m *ProjectRole) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Name = string(dAtA[iNdEx:postIndex]) + m.Array = append(m.Array, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 2: + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *OptionalMap) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: OptionalMap: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: OptionalMap: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Map", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -33008,37 +37353,1701 @@ func (m *ProjectRole) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.Description = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Policies", wireType) + if m.Map == nil { + m.Map = make(map[string]string) } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Map[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *OrphanedResourceKey) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: OrphanedResourceKey: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: OrphanedResourceKey: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Group", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Group = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Kind = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *OrphanedResourcesMonitorSettings) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: OrphanedResourcesMonitorSettings: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: OrphanedResourcesMonitorSettings: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Warn", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + b := bool(v != 0) + m.Warn = &b + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ignore", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Ignore = append(m.Ignore, OrphanedResourceKey{}) + if err := m.Ignore[len(m.Ignore)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *OverrideIgnoreDiff) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: OverrideIgnoreDiff: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: OverrideIgnoreDiff: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field JSONPointers", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.JSONPointers = append(m.JSONPointers, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field JQPathExpressions", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.JQPathExpressions = append(m.JQPathExpressions, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ManagedFieldsManagers", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ManagedFieldsManagers = append(m.ManagedFieldsManagers, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PluginConfigMapRef) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PluginConfigMapRef: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PluginConfigMapRef: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PluginGenerator) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PluginGenerator: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PluginGenerator: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConfigMapRef", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ConfigMapRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Input.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RequeueAfterSeconds", wireType) + } + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.RequeueAfterSeconds = &v + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Template.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Values", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Values == nil { + m.Values = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Values[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PluginInput) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PluginInput: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PluginInput: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Parameters", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Parameters == nil { + m.Parameters = make(PluginParameters) + } + var mapkey string + mapvalue := &v11.JSON{} + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthGenerated + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLengthGenerated + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &v11.JSON{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Parameters[mapkey] = *mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ProjectRole) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProjectRole: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProjectRole: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Policies", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Policies = append(m.Policies, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field JWTTokens", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.JWTTokens = append(m.JWTTokens, JWTToken{}) + if err := m.JWTTokens[len(m.JWTTokens)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Groups", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Groups = append(m.Groups, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PullRequestGenerator) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PullRequestGenerator: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PullRequestGenerator: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Github", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Github == nil { + m.Github = &PullRequestGeneratorGithub{} + } + if err := m.Github.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GitLab", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.GitLab == nil { + m.GitLab = &PullRequestGeneratorGitLab{} + } + if err := m.GitLab.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Gitea", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Gitea == nil { + m.Gitea = &PullRequestGeneratorGitea{} + } + if err := m.Gitea.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BitbucketServer", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BitbucketServer == nil { + m.BitbucketServer = &PullRequestGeneratorBitbucketServer{} + } + if err := m.BitbucketServer.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Filters = append(m.Filters, PullRequestGeneratorFilter{}) + if err := m.Filters[len(m.Filters)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RequeueAfterSeconds", wireType) + } + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.RequeueAfterSeconds = &v + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Template.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bitbucket", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Bitbucket == nil { + m.Bitbucket = &PullRequestGeneratorBitbucket{} + } + if err := m.Bitbucket.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AzureDevOps", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AzureDevOps == nil { + m.AzureDevOps = &PullRequestGeneratorAzureDevOps{} + } + if err := m.AzureDevOps.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PullRequestGeneratorAzureDevOps) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PullRequestGeneratorAzureDevOps: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PullRequestGeneratorAzureDevOps: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Organization", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { @@ -33056,11 +39065,107 @@ func (m *ProjectRole) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Policies = append(m.Policies, string(dAtA[iNdEx:postIndex])) + m.Organization = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Project = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Repo", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Repo = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field JWTTokens", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field API", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.API = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenRef", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -33087,14 +39192,130 @@ func (m *ProjectRole) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.JWTTokens = append(m.JWTTokens, JWTToken{}) - if err := m.JWTTokens[len(m.JWTTokens)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if m.TokenRef == nil { + m.TokenRef = &SecretRef{} + } + if err := m.TokenRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 5: + case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Groups", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Labels = append(m.Labels, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PullRequestGeneratorBitbucket) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PullRequestGeneratorBitbucket: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PullRequestGeneratorBitbucket: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Owner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Repo", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -33122,135 +39343,13 @@ func (m *ProjectRole) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Groups = append(m.Groups, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipGenerated(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PullRequestGenerator) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: PullRequestGenerator: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PullRequestGenerator: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Github", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Github == nil { - m.Github = &PullRequestGeneratorGithub{} - } - if err := m.Github.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field GitLab", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.GitLab == nil { - m.GitLab = &PullRequestGeneratorGitLab{} - } - if err := m.GitLab.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Repo = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Gitea", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field API", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -33260,31 +39359,27 @@ func (m *PullRequestGenerator) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Gitea == nil { - m.Gitea = &PullRequestGeneratorGitea{} - } - if err := m.Gitea.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.API = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BitbucketServer", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BasicAuth", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -33311,16 +39406,16 @@ func (m *PullRequestGenerator) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.BitbucketServer == nil { - m.BitbucketServer = &PullRequestGeneratorBitbucketServer{} + if m.BasicAuth == nil { + m.BasicAuth = &BasicAuthBitbucketServer{} } - if err := m.BitbucketServer.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.BasicAuth.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BearerToken", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -33347,61 +39442,10 @@ func (m *PullRequestGenerator) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Filters = append(m.Filters, PullRequestGeneratorFilter{}) - if err := m.Filters[len(m.Filters)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field RequeueAfterSeconds", wireType) - } - var v int64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.RequeueAfterSeconds = &v - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF + if m.BearerToken == nil { + m.BearerToken = &BearerTokenBitbucketCloud{} } - if err := m.Template.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.BearerToken.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -33670,6 +39714,39 @@ func (m *PullRequestGeneratorFilter) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.BranchMatch = &s iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TargetBranchMatch", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.TargetBranchMatch = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -33884,6 +39961,26 @@ func (m *PullRequestGeneratorGitLab) Unmarshal(dAtA []byte) error { } m.PullRequestState = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Insecure", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Insecure = bool(v != 0) default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -34939,6 +41036,26 @@ func (m *RepoCreds) Unmarshal(dAtA []byte) error { } m.Proxy = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 20: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ForceHttpBasicAuth", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ForceHttpBasicAuth = bool(v != 0) default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -35693,6 +41810,26 @@ func (m *Repository) Unmarshal(dAtA []byte) error { } m.GCPServiceAccountKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 22: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ForceHttpBasicAuth", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ForceHttpBasicAuth = bool(v != 0) default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -35926,7 +42063,241 @@ func (m *RepositoryCertificate) Unmarshal(dAtA []byte) error { } return nil } -func (m *RepositoryCertificateList) Unmarshal(dAtA []byte) error { +func (m *RepositoryCertificateList) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RepositoryCertificateList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RepositoryCertificateList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Items = append(m.Items, RepositoryCertificate{}) + if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RepositoryList) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RepositoryList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RepositoryList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Items = append(m.Items, &Repository{}) + if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceAction) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -35949,17 +42320,17 @@ func (m *RepositoryCertificateList) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: RepositoryCertificateList: wiretype end group for non-group") + return fmt.Errorf("proto: ResourceAction: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: RepositoryCertificateList: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ResourceAction: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -35969,112 +42340,27 @@ func (m *RepositoryCertificateList) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Items = append(m.Items, RepositoryCertificate{}) - if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipGenerated(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *RepositoryList) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RepositoryList: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RepositoryList: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -36101,15 +42387,16 @@ func (m *RepositoryList) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Params = append(m.Params, ResourceActionParam{}) + if err := m.Params[len(m.Params)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Disabled", wireType) } - var msglen int + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -36119,79 +42406,15 @@ func (m *RepositoryList) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Items = append(m.Items, &Repository{}) - if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipGenerated(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ResourceAction) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ResourceAction: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ResourceAction: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + m.Disabled = bool(v != 0) + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field IconClass", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -36219,13 +42442,13 @@ func (m *ResourceAction) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Name = string(dAtA[iNdEx:postIndex]) + m.IconClass = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DisplayName", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -36235,46 +42458,24 @@ func (m *ResourceAction) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthGenerated } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthGenerated } if postIndex > l { return io.ErrUnexpectedEOF } - m.Params = append(m.Params, ResourceActionParam{}) - if err := m.Params[len(m.Params)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.DisplayName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Disabled", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Disabled = bool(v != 0) default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -38295,6 +44496,39 @@ func (m *ResourceOverride) Unmarshal(dAtA []byte) error { } } m.UseOpenLibs = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IgnoreResourceUpdates", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.IgnoreResourceUpdates.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -39618,6 +45852,39 @@ func (m *RevisionHistory) Unmarshal(dAtA []byte) error { } m.Revisions = append(m.Revisions, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InitiatedBy", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.InitiatedBy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -40159,33 +46426,377 @@ func (m *SCMProviderGenerator) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.CloneProtocol = string(dAtA[iNdEx:postIndex]) + m.CloneProtocol = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RequeueAfterSeconds", wireType) + } + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.RequeueAfterSeconds = &v + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Template.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Values", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Values == nil { + m.Values = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Values[mapkey] = mapvalue + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AWSCodeCommit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AWSCodeCommit == nil { + m.AWSCodeCommit = &SCMProviderGeneratorAWSCodeCommit{} + } + if err := m.AWSCodeCommit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SCMProviderGeneratorAWSCodeCommit) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SCMProviderGeneratorAWSCodeCommit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SCMProviderGeneratorAWSCodeCommit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TagFilters", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TagFilters = append(m.TagFilters, &TagFilter{}) + if err := m.TagFilters[len(m.TagFilters)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Role = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Region", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Region = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 9: + case 4: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field RequeueAfterSeconds", wireType) - } - var v int64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.RequeueAfterSeconds = &v - case 10: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AllBranches", wireType) } - var msglen int + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -40195,25 +46806,12 @@ func (m *SCMProviderGenerator) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.Template.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex + m.AllBranches = bool(v != 0) default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -41551,6 +48149,79 @@ func (m *SCMProviderGeneratorGitlab) Unmarshal(dAtA []byte) error { } } m.AllBranches = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Insecure", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Insecure = bool(v != 0) + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IncludeSharedProjects", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + b := bool(v != 0) + m.IncludeSharedProjects = &b + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Topic", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Topic = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -42498,6 +49169,42 @@ func (m *SyncOperationResult) Unmarshal(dAtA []byte) error { } m.Revisions = append(m.Revisions, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ManagedNamespaceMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ManagedNamespaceMetadata == nil { + m.ManagedNamespaceMetadata = &ManagedNamespaceMetadata{} + } + if err := m.ManagedNamespaceMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -43771,6 +50478,120 @@ func (m *TLSClientConfig) Unmarshal(dAtA []byte) error { } return nil } +func (m *TagFilter) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TagFilter: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TagFilter: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipGenerated(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/pkg/apis/application/v1alpha1/generated.proto b/pkg/apis/application/v1alpha1/generated.proto index c7f3a766548e3..7a296f1e467fe 100644 --- a/pkg/apis/application/v1alpha1/generated.proto +++ b/pkg/apis/application/v1alpha1/generated.proto @@ -22,6 +22,9 @@ message AWSAuthConfig { // RoleARN contains optional role ARN. If set then AWS IAM Authenticator assume a role to perform cluster operations instead of the default AWS credential provider chain. optional string roleARN = 2; + + // Profile contains optional role ARN. If set then AWS IAM Authenticator uses the profile to perform cluster operations instead of the default AWS credential provider chain. + optional string profile = 3; } // AppProject provides a logical grouping of applications, providing controls for: @@ -116,7 +119,7 @@ message Application { optional Operation operation = 4; } -// ApplicationCondition contains details about an application condition, which is usally an error or warning +// ApplicationCondition contains details about an application condition, which is usually an error or warning message ApplicationCondition { // Type is an application condition type optional string type = 1; @@ -130,14 +133,14 @@ message ApplicationCondition { // ApplicationDestination holds information about the application's destination message ApplicationDestination { - // Server specifies the URL of the target cluster and must be set to the Kubernetes control plane API + // Server specifies the URL of the target cluster's Kubernetes control plane API. This must be set if Name is not set. optional string server = 1; // Namespace specifies the target namespace for the application's resources. // The namespace will only be set for namespace-scoped resources that have not set a value for .metadata.namespace optional string namespace = 2; - // Name is an alternate way of specifying the target cluster by its symbolic name + // Name is an alternate way of specifying the target cluster by its symbolic name. This must be set if Server is not set. optional string name = 3; } @@ -157,6 +160,12 @@ message ApplicationMatchExpression { repeated string values = 3; } +message ApplicationPreservedFields { + repeated string annotations = 1; + + repeated string labels = 2; +} + // ApplicationSet is a set of Application resources // +genclient // +genclient:noStatus @@ -183,7 +192,10 @@ message ApplicationSetApplicationStatus { optional string message = 3; // Status contains the AppSet's perceived status of the managed Application resource: (Waiting, Pending, Progressing, Healthy) - optional string status = 5; + optional string status = 4; + + // Step tracks which step this Application should be updated in + optional string step = 5; } // ApplicationSetCondition contains details about an applicationset condition, which is usally an error or warning @@ -224,6 +236,8 @@ message ApplicationSetGenerator { // Selector allows to post-filter all generator. optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 9; + + optional PluginGenerator plugin = 10; } // ApplicationSetList contains a list of ApplicationSet @@ -258,6 +272,21 @@ message ApplicationSetNestedGenerator { // Selector allows to post-filter all generator. optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 9; + + optional PluginGenerator plugin = 10; +} + +// ApplicationSetResourceIgnoreDifferences configures how the ApplicationSet controller will ignore differences in live +// applications when applying changes from generated applications. +message ApplicationSetResourceIgnoreDifferences { + // Name is the name of the application to ignore differences for. If not specified, the rule applies to all applications. + optional string name = 1; + + // JSONPointers is a list of JSON pointers to fields to ignore differences for. + repeated string jsonPointers = 2; + + // JQPathExpressions is a list of JQ path expressions to fields to ignore differences for. + repeated string jqPathExpressions = 3; } message ApplicationSetRolloutStep { @@ -281,6 +310,17 @@ message ApplicationSetSpec { optional ApplicationSetSyncPolicy syncPolicy = 4; optional ApplicationSetStrategy strategy = 5; + + optional ApplicationPreservedFields preservedFields = 6; + + repeated string goTemplateOptions = 7; + + // ApplyNestedSelectors enables selectors defined within the generators of two level-nested matrix or merge generators + optional bool applyNestedSelectors = 8; + + repeated ApplicationSetResourceIgnoreDifferences ignoreApplicationDifferences = 9; + + optional string templatePatch = 10; } // ApplicationSetStatus defines the observed state of ApplicationSet @@ -304,6 +344,11 @@ message ApplicationSetStrategy { message ApplicationSetSyncPolicy { // PreserveResourcesOnDeletion will preserve resources on deletion. If PreserveResourcesOnDeletion is set to true, these Applications will not be deleted. optional bool preserveResourcesOnDeletion = 1; + + // ApplicationsSync represents the policy applied on the generated applications. Possible values are create-only, create-update, create-delete, sync + // +kubebuilder:validation:Optional + // +kubebuilder:validation:Enum=create-only;create-update;create-delete;sync + optional string applicationsSync = 2; } // ApplicationSetTemplate represents argocd ApplicationSpec @@ -343,6 +388,11 @@ message ApplicationSetTerminalGenerator { optional DuckTypeGenerator clusterDecisionResource = 5; optional PullRequestGenerator pullRequest = 6; + + optional PluginGenerator plugin = 7; + + // Selector allows to post-filter all generator. + optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 8; } // ApplicationSource contains all required information about the source of an application @@ -403,7 +453,8 @@ message ApplicationSourceHelm { // ReleaseName is the Helm release name to use. If omitted it will use the application name optional string releaseName = 3; - // Values specifies Helm values to be passed to helm template, typically defined as a block + // Values specifies Helm values to be passed to helm template, typically defined as a block. ValuesObject takes precedence over Values, so use one or the other. + // +patchStrategy=replace optional string values = 4; // FileParameters are file parameters to the helm template @@ -420,6 +471,10 @@ message ApplicationSourceHelm { // SkipCrds skips custom resource definition installation step (Helm's --skip-crds) optional bool skipCrds = 9; + + // ValuesObject specifies Helm values to be passed to helm template, defined as a map. This takes precedence over Values. + // +kubebuilder:pruning:PreserveUnknownFields + optional k8s.io.apimachinery.pkg.runtime.RawExtension valuesObject = 10; } // ApplicationSourceJsonnet holds options specific to applications of type Jsonnet @@ -459,6 +514,24 @@ message ApplicationSourceKustomize { // ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize apps optional bool forceCommonAnnotations = 8; + + // Namespace sets the namespace that Kustomize adds to all resources + optional string namespace = 9; + + // CommonAnnotationsEnvsubst specifies whether to apply env variables substitution for annotation values + optional bool commonAnnotationsEnvsubst = 10; + + // Replicas is a list of Kustomize Replicas override specifications + repeated KustomizeReplica replicas = 11; + + // Patches is a list of Kustomize patches + repeated KustomizePatch patches = 12; + + // Components specifies a list of kustomize components to add to the kustomization before building + repeated string components = 13; + + // LabelWithoutSelector specifies whether to apply common labels to resource selectors or not + optional bool labelWithoutSelector = 14; } // ApplicationSourcePlugin holds options specific to config management plugins @@ -478,10 +551,10 @@ message ApplicationSourcePluginParameter { optional string string = 5; // Map is the value of a map type parameter. - map map = 3; + optional OptionalMap map = 3; // Array is the value of an array type parameter. - repeated string array = 4; + optional OptionalArray array = 4; } // ApplicationSpec represents desired application state. Contains link to repository with application definition and additional parameters link definition revision. @@ -554,6 +627,9 @@ message ApplicationStatus { // SourceTypes specifies the type of the sources included in the application repeated string sourceTypes = 12; + + // ControllerNamespace indicates the namespace in which the application controller is located + optional string controllerNamespace = 13; } // ApplicationSummary contains information about URLs and container images used by an application @@ -611,6 +687,23 @@ message BasicAuthBitbucketServer { optional SecretRef passwordRef = 2; } +// BearerTokenBitbucketCloud defines the Bearer token for BitBucket AppToken auth. +message BearerTokenBitbucketCloud { + // Password (or personal access token) reference. + optional SecretRef tokenRef = 1; +} + +// ChartDetails contains helm chart metadata for a specific version +message ChartDetails { + optional string description = 1; + + // The URL of this projects home page, e.g. "http://example.com" + optional string home = 2; + + // List of maintainer details, name and email, e.g. ["John Doe "] + repeated string maintainers = 3; +} + // Cluster is the definition of a cluster resource message Cluster { // Server is the API server URL of the Kubernetes cluster @@ -745,6 +838,9 @@ message ComparedTo { // Sources is a reference to the application's multiple sources used for comparison repeated ApplicationSource sources = 3; + + // IgnoreDifferences is a reference to the application's ignored differences used for comparison + repeated ResourceIgnoreDifferences ignoreDifferences = 4; } // ComponentParameter contains information about component parameter value @@ -851,6 +947,9 @@ message GitGenerator { optional ApplicationSetTemplate template = 6; optional string pathParamPrefix = 7; + + // Values contains key/value pairs which are passed directly as parameters to the template + map values = 8; } // GnuPGPublicKey is a representation of a GnuPG public key @@ -985,6 +1084,14 @@ message KnownTypeField { optional string type = 2; } +message KustomizeGvk { + optional string group = 1; + + optional string version = 2; + + optional string kind = 3; +} + // KustomizeOptions are options for kustomize to use when building manifests message KustomizeOptions { // BuildOptions is a string of build parameters to use when calling `kustomize build` @@ -994,11 +1101,48 @@ message KustomizeOptions { optional string binaryPath = 2; } +message KustomizePatch { + optional string path = 1; + + optional string patch = 2; + + optional KustomizeSelector target = 3; + + map options = 4; +} + +message KustomizeReplica { + // Name of Deployment or StatefulSet + optional string name = 1; + + // Number of replicas + optional k8s.io.apimachinery.pkg.util.intstr.IntOrString count = 2; +} + +message KustomizeResId { + optional KustomizeGvk gvk = 1; + + optional string name = 2; + + optional string namespace = 3; +} + +message KustomizeSelector { + optional KustomizeResId resId = 1; + + optional string annotationSelector = 2; + + optional string labelSelector = 3; +} + // ListGenerator include items info message ListGenerator { + // +kubebuilder:validation:Optional repeated k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1.JSON elements = 1; optional ApplicationSetTemplate template = 2; + + optional string elementsYaml = 3; } message ManagedNamespaceMetadata { @@ -1105,6 +1249,18 @@ message OperationState { optional int64 retryCount = 8; } +message OptionalArray { + // Array is the value of an array type parameter. + // +optional + repeated string array = 1; +} + +message OptionalMap { + // Map is the value of a map type parameter. + // +optional + map map = 1; +} + // OrphanedResourceKey is a reference to a resource to be ignored from message OrphanedResourceKey { optional string group = 1; @@ -1137,6 +1293,33 @@ message OverrideIgnoreDiff { repeated string managedFieldsManagers = 3; } +message PluginConfigMapRef { + // Name of the ConfigMap + optional string name = 1; +} + +// PluginGenerator defines connection info specific to Plugin. +message PluginGenerator { + optional PluginConfigMapRef configMapRef = 1; + + optional PluginInput input = 2; + + // RequeueAfterSeconds determines how long the ApplicationSet controller will wait before reconciling the ApplicationSet again. + optional int64 requeueAfterSeconds = 3; + + optional ApplicationSetTemplate template = 4; + + // Values contains key/value pairs which are passed directly as parameters to the template. These values will not be + // sent as parameters to the plugin. + map values = 5; +} + +message PluginInput { + // Parameters contains the information to pass to the plugin. It is a map. The keys must be strings, and the + // values can be any type. + map parameters = 1; +} + // ProjectRole represents a role that has access to a project message ProjectRole { // Name is a name for this role @@ -1173,9 +1356,53 @@ message PullRequestGenerator { optional int64 requeueAfterSeconds = 6; optional ApplicationSetTemplate template = 7; + + optional PullRequestGeneratorBitbucket bitbucket = 8; + + // Additional provider to use and config for it. + optional PullRequestGeneratorAzureDevOps azuredevops = 9; +} + +// PullRequestGeneratorAzureDevOps defines connection info specific to AzureDevOps. +message PullRequestGeneratorAzureDevOps { + // Azure DevOps org to scan. Required. + optional string organization = 1; + + // Azure DevOps project name to scan. Required. + optional string project = 2; + + // Azure DevOps repo name to scan. Required. + optional string repo = 3; + + // The Azure DevOps API URL to talk to. If blank, use https://dev.azure.com/. + optional string api = 4; + + // Authentication token reference. + optional SecretRef tokenRef = 5; + + // Labels is used to filter the PRs that you want to target + repeated string labels = 6; +} + +// PullRequestGeneratorBitbucket defines connection info specific to Bitbucket. +message PullRequestGeneratorBitbucket { + // Workspace to scan. Required. + optional string owner = 1; + + // Repo name to scan. Required. + optional string repo = 2; + + // The Bitbucket REST API URL to talk to. If blank, uses https://api.bitbucket.org/2.0. + optional string api = 3; + + // Credentials for Basic auth + optional BasicAuthBitbucketServer basicAuth = 4; + + // Credentials for AppToken (Bearer auth) + optional BearerTokenBitbucketCloud bearerToken = 5; } -// PullRequestGenerator defines connection info specific to BitbucketServer. +// PullRequestGeneratorBitbucketServer defines connection info specific to BitbucketServer. message PullRequestGeneratorBitbucketServer { // Project to scan. Required. optional string project = 1; @@ -1195,6 +1422,8 @@ message PullRequestGeneratorBitbucketServer { // pass for a pull request to be included. message PullRequestGeneratorFilter { optional string branchMatch = 1; + + optional string targetBranchMatch = 2; } // PullRequestGeneratorGitLab defines connection info specific to GitLab. @@ -1213,9 +1442,12 @@ message PullRequestGeneratorGitLab { // PullRequestState is an additional MRs filter to get only those with a certain state. Default: "" (all states) optional string pullRequestState = 5; + + // Skips validating the SCM provider's TLS certificate - useful for self-signed certificates.; default: false + optional bool insecure = 6; } -// PullRequestGenerator defines connection info specific to Gitea. +// PullRequestGeneratorGitea defines connection info specific to Gitea. message PullRequestGeneratorGitea { // Gitea org or user to scan. Required. optional string owner = 1; @@ -1305,6 +1537,9 @@ message RepoCreds { // Proxy specifies the HTTP/HTTPS proxy used to access repos at the repo server optional string proxy = 19; + + // ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections + optional bool forceHttpBasicAuth = 20; } // RepositoryList is a collection of Repositories. @@ -1379,6 +1614,9 @@ message Repository { // GCPServiceAccountKey specifies the service account key in JSON format to be used for getting credentials to Google Cloud Source repos optional string gcpServiceAccountKey = 21; + + // ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections + optional bool forceHttpBasicAuth = 22; } // A RepositoryCertificate is either SSH known hosts entry or TLS certificate @@ -1422,6 +1660,10 @@ message ResourceAction { repeated ResourceActionParam params = 2; optional bool disabled = 3; + + optional string iconClass = 4; + + optional string displayName = 5; } // TODO: describe this type @@ -1551,6 +1793,8 @@ message ResourceOverride { optional OverrideIgnoreDiff ignoreDifferences = 2; + optional OverrideIgnoreDiff ignoreResourceUpdates = 6; + repeated KnownTypeField knownTypeFields = 4; } @@ -1658,6 +1902,9 @@ message RevisionHistory { // Revisions holds the revision of each source in sources field the sync was performed against repeated string revisions = 9; + + // InitiatedBy contains information about who initiated the operations + optional OperationInitiator initiatedBy = 10; } // RevisionMetadata contains metadata for a specific revision in a Git repository @@ -1707,6 +1954,28 @@ message SCMProviderGenerator { optional int64 requeueAfterSeconds = 9; optional ApplicationSetTemplate template = 10; + + // Values contains key/value pairs which are passed directly as parameters to the template + map values = 11; + + optional SCMProviderGeneratorAWSCodeCommit awsCodeCommit = 12; +} + +// SCMProviderGeneratorAWSCodeCommit defines connection info specific to AWS CodeCommit. +message SCMProviderGeneratorAWSCodeCommit { + // TagFilters provides the tag filter(s) for repo discovery + repeated TagFilter tagFilters = 1; + + // Role provides the AWS IAM role to assume, for cross-account repo discovery + // if not provided, AppSet controller will use its pod/node identity to discover. + optional string role = 2; + + // Region provides the AWS region to discover repos. + // if not provided, AppSet controller will infer the current region from environment. + optional string region = 3; + + // Scan all branches instead of just the default branch. + optional bool allBranches = 4; } // SCMProviderGeneratorAzureDevOps defines connection info specific to Azure DevOps. @@ -1829,6 +2098,15 @@ message SCMProviderGeneratorGitlab { // Scan all branches instead of just the default branch. optional bool allBranches = 5; + + // Skips validating the SCM provider's TLS certificate - useful for self-signed certificates.; default: false + optional bool insecure = 6; + + // When recursing through subgroups, also include shared Projects (true) or scan only the subgroups under same path (false). Defaults to "true" + optional bool includeSharedProjects = 7; + + // Filter repos list based on Gitlab Topic. + optional string topic = 8; } // Utility struct for a reference to a secret key. @@ -1908,6 +2186,9 @@ message SyncOperationResult { // Revisions holds the revision this sync operation was performed for respective indexed source in sources field repeated string revisions = 5; + + // ManagedNamespaceMetadata contains the current sync state of managed namespace metadata + optional ManagedNamespaceMetadata managedNamespaceMetadata = 6; } // SyncPolicy controls when a sync will be performed in response to updates in git @@ -1930,7 +2211,7 @@ message SyncPolicyAutomated { // Prune specifies whether to delete resources from the cluster that are not found in the sources anymore as part of automated sync (default: false) optional bool prune = 1; - // SelfHeal specifes whether to revert resources back to their desired state upon modification in the cluster (default: false) + // SelfHeal specifies whether to revert resources back to their desired state upon modification in the cluster (default: false) optional bool selfHeal = 2; // AllowEmpty allows apps have zero live resources (default: false) @@ -2027,3 +2308,9 @@ message TLSClientConfig { optional bytes caData = 5; } +message TagFilter { + optional string key = 1; + + optional string value = 2; +} + diff --git a/pkg/apis/application/v1alpha1/openapi_generated.go b/pkg/apis/application/v1alpha1/openapi_generated.go index 17fbea6cf9e11..32eb8a725f353 100644 --- a/pkg/apis/application/v1alpha1/openapi_generated.go +++ b/pkg/apis/application/v1alpha1/openapi_generated.go @@ -14,141 +14,159 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.AWSAuthConfig": schema_pkg_apis_application_v1alpha1_AWSAuthConfig(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.AppProject": schema_pkg_apis_application_v1alpha1_AppProject(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.AppProjectList": schema_pkg_apis_application_v1alpha1_AppProjectList(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.AppProjectSpec": schema_pkg_apis_application_v1alpha1_AppProjectSpec(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.AppProjectStatus": schema_pkg_apis_application_v1alpha1_AppProjectStatus(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Application": schema_pkg_apis_application_v1alpha1_Application(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationCondition": schema_pkg_apis_application_v1alpha1_ApplicationCondition(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationDestination": schema_pkg_apis_application_v1alpha1_ApplicationDestination(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationList": schema_pkg_apis_application_v1alpha1_ApplicationList(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationMatchExpression": schema_pkg_apis_application_v1alpha1_ApplicationMatchExpression(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSet": schema_pkg_apis_application_v1alpha1_ApplicationSet(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetApplicationStatus": schema_pkg_apis_application_v1alpha1_ApplicationSetApplicationStatus(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetCondition": schema_pkg_apis_application_v1alpha1_ApplicationSetCondition(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetGenerator": schema_pkg_apis_application_v1alpha1_ApplicationSetGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetList": schema_pkg_apis_application_v1alpha1_ApplicationSetList(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetNestedGenerator": schema_pkg_apis_application_v1alpha1_ApplicationSetNestedGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetRolloutStep": schema_pkg_apis_application_v1alpha1_ApplicationSetRolloutStep(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetRolloutStrategy": schema_pkg_apis_application_v1alpha1_ApplicationSetRolloutStrategy(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetSpec": schema_pkg_apis_application_v1alpha1_ApplicationSetSpec(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetStatus": schema_pkg_apis_application_v1alpha1_ApplicationSetStatus(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetStrategy": schema_pkg_apis_application_v1alpha1_ApplicationSetStrategy(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetSyncPolicy": schema_pkg_apis_application_v1alpha1_ApplicationSetSyncPolicy(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate": schema_pkg_apis_application_v1alpha1_ApplicationSetTemplate(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplateMeta": schema_pkg_apis_application_v1alpha1_ApplicationSetTemplateMeta(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTerminalGenerator": schema_pkg_apis_application_v1alpha1_ApplicationSetTerminalGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSource": schema_pkg_apis_application_v1alpha1_ApplicationSource(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSourceDirectory": schema_pkg_apis_application_v1alpha1_ApplicationSourceDirectory(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSourceHelm": schema_pkg_apis_application_v1alpha1_ApplicationSourceHelm(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSourceJsonnet": schema_pkg_apis_application_v1alpha1_ApplicationSourceJsonnet(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSourceKustomize": schema_pkg_apis_application_v1alpha1_ApplicationSourceKustomize(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSourcePlugin": schema_pkg_apis_application_v1alpha1_ApplicationSourcePlugin(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSourcePluginParameter": schema_pkg_apis_application_v1alpha1_ApplicationSourcePluginParameter(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSpec": schema_pkg_apis_application_v1alpha1_ApplicationSpec(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationStatus": schema_pkg_apis_application_v1alpha1_ApplicationStatus(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSummary": schema_pkg_apis_application_v1alpha1_ApplicationSummary(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationTree": schema_pkg_apis_application_v1alpha1_ApplicationTree(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationWatchEvent": schema_pkg_apis_application_v1alpha1_ApplicationWatchEvent(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Backoff": schema_pkg_apis_application_v1alpha1_Backoff(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BasicAuthBitbucketServer": schema_pkg_apis_application_v1alpha1_BasicAuthBitbucketServer(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Cluster": schema_pkg_apis_application_v1alpha1_Cluster(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterCacheInfo": schema_pkg_apis_application_v1alpha1_ClusterCacheInfo(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterConfig": schema_pkg_apis_application_v1alpha1_ClusterConfig(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterGenerator": schema_pkg_apis_application_v1alpha1_ClusterGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterInfo": schema_pkg_apis_application_v1alpha1_ClusterInfo(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterList": schema_pkg_apis_application_v1alpha1_ClusterList(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Command": schema_pkg_apis_application_v1alpha1_Command(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ComparedTo": schema_pkg_apis_application_v1alpha1_ComparedTo(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ComponentParameter": schema_pkg_apis_application_v1alpha1_ComponentParameter(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConfigManagementPlugin": schema_pkg_apis_application_v1alpha1_ConfigManagementPlugin(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConnectionState": schema_pkg_apis_application_v1alpha1_ConnectionState(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.DuckTypeGenerator": schema_pkg_apis_application_v1alpha1_DuckTypeGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.EnvEntry": schema_pkg_apis_application_v1alpha1_EnvEntry(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ExecProviderConfig": schema_pkg_apis_application_v1alpha1_ExecProviderConfig(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitDirectoryGeneratorItem": schema_pkg_apis_application_v1alpha1_GitDirectoryGeneratorItem(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitFileGeneratorItem": schema_pkg_apis_application_v1alpha1_GitFileGeneratorItem(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitGenerator": schema_pkg_apis_application_v1alpha1_GitGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GnuPGPublicKey": schema_pkg_apis_application_v1alpha1_GnuPGPublicKey(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GnuPGPublicKeyList": schema_pkg_apis_application_v1alpha1_GnuPGPublicKeyList(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HealthStatus": schema_pkg_apis_application_v1alpha1_HealthStatus(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmFileParameter": schema_pkg_apis_application_v1alpha1_HelmFileParameter(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmOptions": schema_pkg_apis_application_v1alpha1_HelmOptions(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmParameter": schema_pkg_apis_application_v1alpha1_HelmParameter(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HostInfo": schema_pkg_apis_application_v1alpha1_HostInfo(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HostResourceInfo": schema_pkg_apis_application_v1alpha1_HostResourceInfo(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Info": schema_pkg_apis_application_v1alpha1_Info(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.InfoItem": schema_pkg_apis_application_v1alpha1_InfoItem(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.JWTToken": schema_pkg_apis_application_v1alpha1_JWTToken(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.JWTTokens": schema_pkg_apis_application_v1alpha1_JWTTokens(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.JsonnetVar": schema_pkg_apis_application_v1alpha1_JsonnetVar(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KnownTypeField": schema_pkg_apis_application_v1alpha1_KnownTypeField(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizeOptions": schema_pkg_apis_application_v1alpha1_KustomizeOptions(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ListGenerator": schema_pkg_apis_application_v1alpha1_ListGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ManagedNamespaceMetadata": schema_pkg_apis_application_v1alpha1_ManagedNamespaceMetadata(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.MatrixGenerator": schema_pkg_apis_application_v1alpha1_MatrixGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.MergeGenerator": schema_pkg_apis_application_v1alpha1_MergeGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.NestedMatrixGenerator": schema_pkg_apis_application_v1alpha1_NestedMatrixGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.NestedMergeGenerator": schema_pkg_apis_application_v1alpha1_NestedMergeGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Operation": schema_pkg_apis_application_v1alpha1_Operation(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OperationInitiator": schema_pkg_apis_application_v1alpha1_OperationInitiator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OperationState": schema_pkg_apis_application_v1alpha1_OperationState(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OrphanedResourceKey": schema_pkg_apis_application_v1alpha1_OrphanedResourceKey(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OrphanedResourcesMonitorSettings": schema_pkg_apis_application_v1alpha1_OrphanedResourcesMonitorSettings(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OverrideIgnoreDiff": schema_pkg_apis_application_v1alpha1_OverrideIgnoreDiff(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ProjectRole": schema_pkg_apis_application_v1alpha1_ProjectRole(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGenerator": schema_pkg_apis_application_v1alpha1_PullRequestGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorBitbucketServer": schema_pkg_apis_application_v1alpha1_PullRequestGeneratorBitbucketServer(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorFilter": schema_pkg_apis_application_v1alpha1_PullRequestGeneratorFilter(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGitLab": schema_pkg_apis_application_v1alpha1_PullRequestGeneratorGitLab(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGitea": schema_pkg_apis_application_v1alpha1_PullRequestGeneratorGitea(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGithub": schema_pkg_apis_application_v1alpha1_PullRequestGeneratorGithub(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RefTarget": schema_pkg_apis_application_v1alpha1_RefTarget(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RepoCreds": schema_pkg_apis_application_v1alpha1_RepoCreds(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RepoCredsList": schema_pkg_apis_application_v1alpha1_RepoCredsList(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Repository": schema_pkg_apis_application_v1alpha1_Repository(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RepositoryCertificate": schema_pkg_apis_application_v1alpha1_RepositoryCertificate(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RepositoryCertificateList": schema_pkg_apis_application_v1alpha1_RepositoryCertificateList(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RepositoryList": schema_pkg_apis_application_v1alpha1_RepositoryList(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceAction": schema_pkg_apis_application_v1alpha1_ResourceAction(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceActionDefinition": schema_pkg_apis_application_v1alpha1_ResourceActionDefinition(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceActionParam": schema_pkg_apis_application_v1alpha1_ResourceActionParam(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceActions": schema_pkg_apis_application_v1alpha1_ResourceActions(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceDiff": schema_pkg_apis_application_v1alpha1_ResourceDiff(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceIgnoreDifferences": schema_pkg_apis_application_v1alpha1_ResourceIgnoreDifferences(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceNetworkingInfo": schema_pkg_apis_application_v1alpha1_ResourceNetworkingInfo(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceNode": schema_pkg_apis_application_v1alpha1_ResourceNode(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceOverride": schema_pkg_apis_application_v1alpha1_ResourceOverride(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceRef": schema_pkg_apis_application_v1alpha1_ResourceRef(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceResult": schema_pkg_apis_application_v1alpha1_ResourceResult(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceStatus": schema_pkg_apis_application_v1alpha1_ResourceStatus(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RetryStrategy": schema_pkg_apis_application_v1alpha1_RetryStrategy(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RevisionHistory": schema_pkg_apis_application_v1alpha1_RevisionHistory(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RevisionMetadata": schema_pkg_apis_application_v1alpha1_RevisionMetadata(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGenerator": schema_pkg_apis_application_v1alpha1_SCMProviderGenerator(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorAzureDevOps": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorAzureDevOps(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorBitbucket": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorBitbucket(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorBitbucketServer": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorBitbucketServer(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorFilter": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorFilter(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorGitea": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorGitea(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorGithub": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorGithub(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorGitlab": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorGitlab(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SecretRef": schema_pkg_apis_application_v1alpha1_SecretRef(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SignatureKey": schema_pkg_apis_application_v1alpha1_SignatureKey(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncOperation": schema_pkg_apis_application_v1alpha1_SyncOperation(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncOperationResource": schema_pkg_apis_application_v1alpha1_SyncOperationResource(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncOperationResult": schema_pkg_apis_application_v1alpha1_SyncOperationResult(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncPolicy": schema_pkg_apis_application_v1alpha1_SyncPolicy(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncPolicyAutomated": schema_pkg_apis_application_v1alpha1_SyncPolicyAutomated(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncStatus": schema_pkg_apis_application_v1alpha1_SyncStatus(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncStrategy": schema_pkg_apis_application_v1alpha1_SyncStrategy(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncStrategyApply": schema_pkg_apis_application_v1alpha1_SyncStrategyApply(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncStrategyHook": schema_pkg_apis_application_v1alpha1_SyncStrategyHook(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncWindow": schema_pkg_apis_application_v1alpha1_SyncWindow(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.TLSClientConfig": schema_pkg_apis_application_v1alpha1_TLSClientConfig(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.objectMeta": schema_pkg_apis_application_v1alpha1_objectMeta(ref), - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.rawResourceOverride": schema_pkg_apis_application_v1alpha1_rawResourceOverride(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.AWSAuthConfig": schema_pkg_apis_application_v1alpha1_AWSAuthConfig(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.AppProject": schema_pkg_apis_application_v1alpha1_AppProject(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.AppProjectList": schema_pkg_apis_application_v1alpha1_AppProjectList(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.AppProjectSpec": schema_pkg_apis_application_v1alpha1_AppProjectSpec(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.AppProjectStatus": schema_pkg_apis_application_v1alpha1_AppProjectStatus(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Application": schema_pkg_apis_application_v1alpha1_Application(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationCondition": schema_pkg_apis_application_v1alpha1_ApplicationCondition(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationDestination": schema_pkg_apis_application_v1alpha1_ApplicationDestination(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationList": schema_pkg_apis_application_v1alpha1_ApplicationList(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationMatchExpression": schema_pkg_apis_application_v1alpha1_ApplicationMatchExpression(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationPreservedFields": schema_pkg_apis_application_v1alpha1_ApplicationPreservedFields(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSet": schema_pkg_apis_application_v1alpha1_ApplicationSet(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetApplicationStatus": schema_pkg_apis_application_v1alpha1_ApplicationSetApplicationStatus(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetCondition": schema_pkg_apis_application_v1alpha1_ApplicationSetCondition(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetGenerator": schema_pkg_apis_application_v1alpha1_ApplicationSetGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetList": schema_pkg_apis_application_v1alpha1_ApplicationSetList(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetNestedGenerator": schema_pkg_apis_application_v1alpha1_ApplicationSetNestedGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetResourceIgnoreDifferences": schema_pkg_apis_application_v1alpha1_ApplicationSetResourceIgnoreDifferences(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetRolloutStep": schema_pkg_apis_application_v1alpha1_ApplicationSetRolloutStep(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetRolloutStrategy": schema_pkg_apis_application_v1alpha1_ApplicationSetRolloutStrategy(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetSpec": schema_pkg_apis_application_v1alpha1_ApplicationSetSpec(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetStatus": schema_pkg_apis_application_v1alpha1_ApplicationSetStatus(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetStrategy": schema_pkg_apis_application_v1alpha1_ApplicationSetStrategy(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetSyncPolicy": schema_pkg_apis_application_v1alpha1_ApplicationSetSyncPolicy(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate": schema_pkg_apis_application_v1alpha1_ApplicationSetTemplate(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplateMeta": schema_pkg_apis_application_v1alpha1_ApplicationSetTemplateMeta(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTerminalGenerator": schema_pkg_apis_application_v1alpha1_ApplicationSetTerminalGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSource": schema_pkg_apis_application_v1alpha1_ApplicationSource(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSourceDirectory": schema_pkg_apis_application_v1alpha1_ApplicationSourceDirectory(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSourceHelm": schema_pkg_apis_application_v1alpha1_ApplicationSourceHelm(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSourceJsonnet": schema_pkg_apis_application_v1alpha1_ApplicationSourceJsonnet(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSourceKustomize": schema_pkg_apis_application_v1alpha1_ApplicationSourceKustomize(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSourcePlugin": schema_pkg_apis_application_v1alpha1_ApplicationSourcePlugin(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSourcePluginParameter": schema_pkg_apis_application_v1alpha1_ApplicationSourcePluginParameter(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSpec": schema_pkg_apis_application_v1alpha1_ApplicationSpec(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationStatus": schema_pkg_apis_application_v1alpha1_ApplicationStatus(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSummary": schema_pkg_apis_application_v1alpha1_ApplicationSummary(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationTree": schema_pkg_apis_application_v1alpha1_ApplicationTree(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationWatchEvent": schema_pkg_apis_application_v1alpha1_ApplicationWatchEvent(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Backoff": schema_pkg_apis_application_v1alpha1_Backoff(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BasicAuthBitbucketServer": schema_pkg_apis_application_v1alpha1_BasicAuthBitbucketServer(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BearerTokenBitbucketCloud": schema_pkg_apis_application_v1alpha1_BearerTokenBitbucketCloud(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ChartDetails": schema_pkg_apis_application_v1alpha1_ChartDetails(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Cluster": schema_pkg_apis_application_v1alpha1_Cluster(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterCacheInfo": schema_pkg_apis_application_v1alpha1_ClusterCacheInfo(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterConfig": schema_pkg_apis_application_v1alpha1_ClusterConfig(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterGenerator": schema_pkg_apis_application_v1alpha1_ClusterGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterInfo": schema_pkg_apis_application_v1alpha1_ClusterInfo(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterList": schema_pkg_apis_application_v1alpha1_ClusterList(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Command": schema_pkg_apis_application_v1alpha1_Command(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ComparedTo": schema_pkg_apis_application_v1alpha1_ComparedTo(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ComponentParameter": schema_pkg_apis_application_v1alpha1_ComponentParameter(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConfigManagementPlugin": schema_pkg_apis_application_v1alpha1_ConfigManagementPlugin(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConnectionState": schema_pkg_apis_application_v1alpha1_ConnectionState(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.DuckTypeGenerator": schema_pkg_apis_application_v1alpha1_DuckTypeGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.EnvEntry": schema_pkg_apis_application_v1alpha1_EnvEntry(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ExecProviderConfig": schema_pkg_apis_application_v1alpha1_ExecProviderConfig(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitDirectoryGeneratorItem": schema_pkg_apis_application_v1alpha1_GitDirectoryGeneratorItem(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitFileGeneratorItem": schema_pkg_apis_application_v1alpha1_GitFileGeneratorItem(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitGenerator": schema_pkg_apis_application_v1alpha1_GitGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GnuPGPublicKey": schema_pkg_apis_application_v1alpha1_GnuPGPublicKey(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GnuPGPublicKeyList": schema_pkg_apis_application_v1alpha1_GnuPGPublicKeyList(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HealthStatus": schema_pkg_apis_application_v1alpha1_HealthStatus(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmFileParameter": schema_pkg_apis_application_v1alpha1_HelmFileParameter(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmOptions": schema_pkg_apis_application_v1alpha1_HelmOptions(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmParameter": schema_pkg_apis_application_v1alpha1_HelmParameter(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HostInfo": schema_pkg_apis_application_v1alpha1_HostInfo(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HostResourceInfo": schema_pkg_apis_application_v1alpha1_HostResourceInfo(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Info": schema_pkg_apis_application_v1alpha1_Info(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.InfoItem": schema_pkg_apis_application_v1alpha1_InfoItem(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.JWTToken": schema_pkg_apis_application_v1alpha1_JWTToken(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.JWTTokens": schema_pkg_apis_application_v1alpha1_JWTTokens(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.JsonnetVar": schema_pkg_apis_application_v1alpha1_JsonnetVar(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KnownTypeField": schema_pkg_apis_application_v1alpha1_KnownTypeField(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizeGvk": schema_pkg_apis_application_v1alpha1_KustomizeGvk(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizeOptions": schema_pkg_apis_application_v1alpha1_KustomizeOptions(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizePatch": schema_pkg_apis_application_v1alpha1_KustomizePatch(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizeReplica": schema_pkg_apis_application_v1alpha1_KustomizeReplica(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizeResId": schema_pkg_apis_application_v1alpha1_KustomizeResId(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizeSelector": schema_pkg_apis_application_v1alpha1_KustomizeSelector(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ListGenerator": schema_pkg_apis_application_v1alpha1_ListGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ManagedNamespaceMetadata": schema_pkg_apis_application_v1alpha1_ManagedNamespaceMetadata(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.MatrixGenerator": schema_pkg_apis_application_v1alpha1_MatrixGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.MergeGenerator": schema_pkg_apis_application_v1alpha1_MergeGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.NestedMatrixGenerator": schema_pkg_apis_application_v1alpha1_NestedMatrixGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.NestedMergeGenerator": schema_pkg_apis_application_v1alpha1_NestedMergeGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Operation": schema_pkg_apis_application_v1alpha1_Operation(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OperationInitiator": schema_pkg_apis_application_v1alpha1_OperationInitiator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OperationState": schema_pkg_apis_application_v1alpha1_OperationState(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OptionalArray": schema_pkg_apis_application_v1alpha1_OptionalArray(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OptionalMap": schema_pkg_apis_application_v1alpha1_OptionalMap(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OrphanedResourceKey": schema_pkg_apis_application_v1alpha1_OrphanedResourceKey(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OrphanedResourcesMonitorSettings": schema_pkg_apis_application_v1alpha1_OrphanedResourcesMonitorSettings(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OverrideIgnoreDiff": schema_pkg_apis_application_v1alpha1_OverrideIgnoreDiff(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginConfigMapRef": schema_pkg_apis_application_v1alpha1_PluginConfigMapRef(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginGenerator": schema_pkg_apis_application_v1alpha1_PluginGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginInput": schema_pkg_apis_application_v1alpha1_PluginInput(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ProjectRole": schema_pkg_apis_application_v1alpha1_ProjectRole(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGenerator": schema_pkg_apis_application_v1alpha1_PullRequestGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorAzureDevOps": schema_pkg_apis_application_v1alpha1_PullRequestGeneratorAzureDevOps(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorBitbucket": schema_pkg_apis_application_v1alpha1_PullRequestGeneratorBitbucket(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorBitbucketServer": schema_pkg_apis_application_v1alpha1_PullRequestGeneratorBitbucketServer(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorFilter": schema_pkg_apis_application_v1alpha1_PullRequestGeneratorFilter(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGitLab": schema_pkg_apis_application_v1alpha1_PullRequestGeneratorGitLab(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGitea": schema_pkg_apis_application_v1alpha1_PullRequestGeneratorGitea(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGithub": schema_pkg_apis_application_v1alpha1_PullRequestGeneratorGithub(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RefTarget": schema_pkg_apis_application_v1alpha1_RefTarget(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RepoCreds": schema_pkg_apis_application_v1alpha1_RepoCreds(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RepoCredsList": schema_pkg_apis_application_v1alpha1_RepoCredsList(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.Repository": schema_pkg_apis_application_v1alpha1_Repository(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RepositoryCertificate": schema_pkg_apis_application_v1alpha1_RepositoryCertificate(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RepositoryCertificateList": schema_pkg_apis_application_v1alpha1_RepositoryCertificateList(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RepositoryList": schema_pkg_apis_application_v1alpha1_RepositoryList(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceAction": schema_pkg_apis_application_v1alpha1_ResourceAction(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceActionDefinition": schema_pkg_apis_application_v1alpha1_ResourceActionDefinition(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceActionParam": schema_pkg_apis_application_v1alpha1_ResourceActionParam(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceActions": schema_pkg_apis_application_v1alpha1_ResourceActions(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceDiff": schema_pkg_apis_application_v1alpha1_ResourceDiff(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceIgnoreDifferences": schema_pkg_apis_application_v1alpha1_ResourceIgnoreDifferences(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceNetworkingInfo": schema_pkg_apis_application_v1alpha1_ResourceNetworkingInfo(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceNode": schema_pkg_apis_application_v1alpha1_ResourceNode(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceOverride": schema_pkg_apis_application_v1alpha1_ResourceOverride(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceRef": schema_pkg_apis_application_v1alpha1_ResourceRef(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceResult": schema_pkg_apis_application_v1alpha1_ResourceResult(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceStatus": schema_pkg_apis_application_v1alpha1_ResourceStatus(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RetryStrategy": schema_pkg_apis_application_v1alpha1_RetryStrategy(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RevisionHistory": schema_pkg_apis_application_v1alpha1_RevisionHistory(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.RevisionMetadata": schema_pkg_apis_application_v1alpha1_RevisionMetadata(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGenerator": schema_pkg_apis_application_v1alpha1_SCMProviderGenerator(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorAWSCodeCommit": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorAWSCodeCommit(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorAzureDevOps": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorAzureDevOps(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorBitbucket": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorBitbucket(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorBitbucketServer": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorBitbucketServer(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorFilter": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorFilter(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorGitea": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorGitea(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorGithub": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorGithub(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorGitlab": schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorGitlab(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SecretRef": schema_pkg_apis_application_v1alpha1_SecretRef(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SignatureKey": schema_pkg_apis_application_v1alpha1_SignatureKey(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncOperation": schema_pkg_apis_application_v1alpha1_SyncOperation(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncOperationResource": schema_pkg_apis_application_v1alpha1_SyncOperationResource(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncOperationResult": schema_pkg_apis_application_v1alpha1_SyncOperationResult(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncPolicy": schema_pkg_apis_application_v1alpha1_SyncPolicy(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncPolicyAutomated": schema_pkg_apis_application_v1alpha1_SyncPolicyAutomated(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncStatus": schema_pkg_apis_application_v1alpha1_SyncStatus(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncStrategy": schema_pkg_apis_application_v1alpha1_SyncStrategy(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncStrategyApply": schema_pkg_apis_application_v1alpha1_SyncStrategyApply(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncStrategyHook": schema_pkg_apis_application_v1alpha1_SyncStrategyHook(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SyncWindow": schema_pkg_apis_application_v1alpha1_SyncWindow(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.TLSClientConfig": schema_pkg_apis_application_v1alpha1_TLSClientConfig(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.TagFilter": schema_pkg_apis_application_v1alpha1_TagFilter(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.objectMeta": schema_pkg_apis_application_v1alpha1_objectMeta(ref), + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.rawResourceOverride": schema_pkg_apis_application_v1alpha1_rawResourceOverride(ref), } } @@ -173,6 +191,13 @@ func schema_pkg_apis_application_v1alpha1_AWSAuthConfig(ref common.ReferenceCall Format: "", }, }, + "profile": { + SchemaProps: spec.SchemaProps{ + Description: "Profile contains optional role ARN. If set then AWS IAM Authenticator uses the profile to perform cluster operations instead of the default AWS credential provider chain.", + Type: []string{"string"}, + Format: "", + }, + }, }, }, }, @@ -539,7 +564,7 @@ func schema_pkg_apis_application_v1alpha1_ApplicationCondition(ref common.Refere return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "ApplicationCondition contains details about an application condition, which is usally an error or warning", + Description: "ApplicationCondition contains details about an application condition, which is usually an error or warning", Type: []string{"object"}, Properties: map[string]spec.Schema{ "type": { @@ -582,7 +607,7 @@ func schema_pkg_apis_application_v1alpha1_ApplicationDestination(ref common.Refe Properties: map[string]spec.Schema{ "server": { SchemaProps: spec.SchemaProps{ - Description: "Server specifies the URL of the target cluster and must be set to the Kubernetes control plane API", + Description: "Server specifies the URL of the target cluster's Kubernetes control plane API. This must be set if Name is not set.", Type: []string{"string"}, Format: "", }, @@ -596,7 +621,7 @@ func schema_pkg_apis_application_v1alpha1_ApplicationDestination(ref common.Refe }, "name": { SchemaProps: spec.SchemaProps{ - Description: "Name is an alternate way of specifying the target cluster by its symbolic name", + Description: "Name is an alternate way of specifying the target cluster by its symbolic name. This must be set if Server is not set.", Type: []string{"string"}, Format: "", }, @@ -694,6 +719,46 @@ func schema_pkg_apis_application_v1alpha1_ApplicationMatchExpression(ref common. } } +func schema_pkg_apis_application_v1alpha1_ApplicationPreservedFields(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "annotations": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "labels": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + func schema_pkg_apis_application_v1alpha1_ApplicationSet(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -779,8 +844,16 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSetApplicationStatus(ref co Format: "", }, }, + "step": { + SchemaProps: spec.SchemaProps{ + Description: "Step tracks which step this Application should be updated in", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, }, - Required: []string{"application", "message", "status"}, + Required: []string{"application", "message", "status", "step"}, }, }, Dependencies: []string{ @@ -895,11 +968,16 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSetGenerator(ref common.Ref Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), }, }, + "plugin": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginGenerator"), + }, + }, }, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.DuckTypeGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ListGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.MatrixGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.MergeGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGenerator", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.DuckTypeGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ListGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.MatrixGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.MergeGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGenerator", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, } } @@ -1007,11 +1085,66 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSetNestedGenerator(ref comm Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), }, }, + "plugin": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginGenerator"), + }, + }, }, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.DuckTypeGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ListGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGenerator", "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.DuckTypeGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ListGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGenerator", "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + } +} + +func schema_pkg_apis_application_v1alpha1_ApplicationSetResourceIgnoreDifferences(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ApplicationSetResourceIgnoreDifferences configures how the ApplicationSet controller will ignore differences in live applications when applying changes from generated applications.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the application to ignore differences for. If not specified, the rule applies to all applications.", + Type: []string{"string"}, + Format: "", + }, + }, + "jsonPointers": { + SchemaProps: spec.SchemaProps{ + Description: "JSONPointers is a list of JSON pointers to fields to ignore differences for.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "jqPathExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "JQPathExpressions is a list of JQ path expressions to fields to ignore differences for.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, } } @@ -1116,12 +1249,57 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSetSpec(ref common.Referenc Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetStrategy"), }, }, + "preservedFields": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationPreservedFields"), + }, + }, + "goTemplateOptions": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "applyNestedSelectors": { + SchemaProps: spec.SchemaProps{ + Description: "ApplyNestedSelectors enables selectors defined within the generators of two level-nested matrix or merge generators", + Type: []string{"boolean"}, + Format: "", + }, + }, + "ignoreApplicationDifferences": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetResourceIgnoreDifferences"), + }, + }, + }, + }, + }, + "templatePatch": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, }, Required: []string{"generators", "template"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetStrategy", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetSyncPolicy", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationPreservedFields", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetResourceIgnoreDifferences", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetStrategy", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetSyncPolicy", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"}, } } @@ -1207,6 +1385,13 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSetSyncPolicy(ref common.Re Format: "", }, }, + "applicationsSync": { + SchemaProps: spec.SchemaProps{ + Description: "ApplicationsSync represents the policy applied on the generated applications. Possible values are create-only, create-update, create-delete, sync", + Type: []string{"string"}, + Format: "", + }, + }, }, }, }, @@ -1347,11 +1532,22 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSetTerminalGenerator(ref co Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGenerator"), }, }, + "plugin": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginGenerator"), + }, + }, + "selector": { + SchemaProps: spec.SchemaProps{ + Description: "Selector allows to post-filter all generator.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), + }, + }, }, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.DuckTypeGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ListGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGenerator"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ClusterGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.DuckTypeGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ListGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGenerator", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, } } @@ -1518,8 +1714,13 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSourceHelm(ref common.Refer }, }, "values": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-strategy": "replace", + }, + }, SchemaProps: spec.SchemaProps{ - Description: "Values specifies Helm values to be passed to helm template, typically defined as a block", + Description: "Values specifies Helm values to be passed to helm template, typically defined as a block. ValuesObject takes precedence over Values, so use one or the other.", Type: []string{"string"}, Format: "", }, @@ -1566,11 +1767,17 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSourceHelm(ref common.Refer Format: "", }, }, + "valuesObject": { + SchemaProps: spec.SchemaProps{ + Description: "ValuesObject specifies Helm values to be passed to helm template, defined as a map. This takes precedence over Values.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, }, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmFileParameter", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmParameter"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmFileParameter", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmParameter", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, } } @@ -1721,9 +1928,75 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSourceKustomize(ref common. Format: "", }, }, + "namespace": { + SchemaProps: spec.SchemaProps{ + Description: "Namespace sets the namespace that Kustomize adds to all resources", + Type: []string{"string"}, + Format: "", + }, + }, + "commonAnnotationsEnvsubst": { + SchemaProps: spec.SchemaProps{ + Description: "CommonAnnotationsEnvsubst specifies whether to apply env variables substitution for annotation values", + Type: []string{"boolean"}, + Format: "", + }, + }, + "replicas": { + SchemaProps: spec.SchemaProps{ + Description: "Replicas is a list of Kustomize Replicas override specifications", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizeReplica"), + }, + }, + }, + }, + }, + "patches": { + SchemaProps: spec.SchemaProps{ + Description: "Patches is a list of Kustomize patches", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizePatch"), + }, + }, + }, + }, + }, + "components": { + SchemaProps: spec.SchemaProps{ + Description: "Components specifies a list of kustomize components to add to the kustomization before building", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "labelWithoutSelector": { + SchemaProps: spec.SchemaProps{ + Description: "LabelWithoutSelector specifies whether to apply common labels to resource selectors or not", + Type: []string{"boolean"}, + Format: "", + }, + }, }, }, }, + Dependencies: []string{ + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizePatch", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizeReplica"}, } } @@ -1793,37 +2066,6 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSourcePluginParameter(ref c Format: "", }, }, - "map": { - SchemaProps: spec.SchemaProps{ - Description: "Map is the value of a map type parameter.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "array": { - SchemaProps: spec.SchemaProps{ - Description: "Array is the value of an array type parameter.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, }, }, }, @@ -2039,6 +2281,13 @@ func schema_pkg_apis_application_v1alpha1_ApplicationStatus(ref common.Reference }, }, }, + "controllerNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "ControllerNamespace indicates the namespace in which the application controller is located", + Type: []string{"string"}, + Format: "", + }, + }, }, }, }, @@ -2241,19 +2490,82 @@ func schema_pkg_apis_application_v1alpha1_BasicAuthBitbucketServer(ref common.Re } } -func schema_pkg_apis_application_v1alpha1_Cluster(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_application_v1alpha1_BearerTokenBitbucketCloud(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Cluster is the definition of a cluster resource", + Description: "BearerTokenBitbucketCloud defines the Bearer token for BitBucket AppToken auth.", Type: []string{"object"}, Properties: map[string]spec.Schema{ - "server": { + "tokenRef": { SchemaProps: spec.SchemaProps{ - Description: "Server is the API server URL of the Kubernetes cluster", - Default: "", - Type: []string{"string"}, - Format: "", + Description: "Password (or personal access token) reference.", + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SecretRef"), + }, + }, + }, + Required: []string{"tokenRef"}, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SecretRef"}, + } +} + +func schema_pkg_apis_application_v1alpha1_ChartDetails(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ChartDetails contains helm chart metadata for a specific version", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "description": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "home": { + SchemaProps: spec.SchemaProps{ + Description: "The URL of this projects home page, e.g. \"http://example.com\"", + Type: []string{"string"}, + Format: "", + }, + }, + "maintainers": { + SchemaProps: spec.SchemaProps{ + Description: "List of maintainer details, name and email, e.g. [\"John Doe \"]", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_application_v1alpha1_Cluster(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Cluster is the definition of a cluster resource", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "server": { + SchemaProps: spec.SchemaProps{ + Description: "Server is the API server URL of the Kubernetes cluster", + Default: "", + Type: []string{"string"}, + Format: "", }, }, "name": { @@ -2680,12 +2992,26 @@ func schema_pkg_apis_application_v1alpha1_ComparedTo(ref common.ReferenceCallbac }, }, }, + "ignoreDifferences": { + SchemaProps: spec.SchemaProps{ + Description: "IgnoreDifferences is a reference to the application's ignored differences used for comparison", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceIgnoreDifferences"), + }, + }, + }, + }, + }, }, Required: []string{"destination"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationDestination", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSource"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationDestination", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSource", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceIgnoreDifferences"}, } } @@ -3070,6 +3396,22 @@ func schema_pkg_apis_application_v1alpha1_GitGenerator(ref common.ReferenceCallb Format: "", }, }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "Values contains key/value pairs which are passed directly as parameters to the template", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, Required: []string{"repoURL", "revision"}, }, @@ -3537,6 +3879,36 @@ func schema_pkg_apis_application_v1alpha1_KnownTypeField(ref common.ReferenceCal } } +func schema_pkg_apis_application_v1alpha1_KustomizeGvk(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_pkg_apis_application_v1alpha1_KustomizeOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -3567,71 +3939,38 @@ func schema_pkg_apis_application_v1alpha1_KustomizeOptions(ref common.ReferenceC } } -func schema_pkg_apis_application_v1alpha1_ListGenerator(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_application_v1alpha1_KustomizePatch(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "ListGenerator include items info", - Type: []string{"object"}, + Type: []string{"object"}, Properties: map[string]spec.Schema{ - "elements": { + "path": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON"), - }, - }, - }, + Type: []string{"string"}, + Format: "", }, }, - "template": { + "patch": { SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"), + Type: []string{"string"}, + Format: "", }, }, - }, - Required: []string{"elements"}, - }, - }, - Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate", "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON"}, - } -} - -func schema_pkg_apis_application_v1alpha1_ManagedNamespaceMetadata(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "labels": { + "target": { SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizeSelector"), }, }, - "annotations": { + "options": { SchemaProps: spec.SchemaProps{ Type: []string{"object"}, AdditionalProperties: &spec.SchemaOrBool{ Allows: true, Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, + Default: false, + Type: []string{"boolean"}, Format: "", }, }, @@ -3641,105 +3980,316 @@ func schema_pkg_apis_application_v1alpha1_ManagedNamespaceMetadata(ref common.Re }, }, }, + Dependencies: []string{ + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.KustomizeSelector"}, } } -func schema_pkg_apis_application_v1alpha1_MatrixGenerator(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_application_v1alpha1_KustomizeReplica(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "MatrixGenerator generates the cartesian product of two sets of parameters. The parameters are defined by two nested generators.", - Type: []string{"object"}, + Type: []string{"object"}, Properties: map[string]spec.Schema{ - "generators": { + "name": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetNestedGenerator"), - }, - }, - }, + Description: "Name of Deployment or StatefulSet", + Default: "", + Type: []string{"string"}, + Format: "", }, }, - "template": { + "count": { SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"), + Description: "Number of replicas", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"), }, }, }, - Required: []string{"generators"}, + Required: []string{"name", "count"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetNestedGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"}, + "k8s.io/apimachinery/pkg/util/intstr.IntOrString"}, } } -func schema_pkg_apis_application_v1alpha1_MergeGenerator(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_application_v1alpha1_KustomizeResId(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "MergeGenerator merges the output of two or more generators. Where the values for all specified merge keys are equal between two sets of generated parameters, the parameter sets will be merged with the parameters from the latter generator taking precedence. Parameter sets with merge keys not present in the base generator's params will be ignored. For example, if the first generator produced [{a: '1', b: '2'}, {c: '1', d: '1'}] and the second generator produced [{'a': 'override'}], the united parameters for merge keys = ['a'] would be [{a: 'override', b: '1'}, {c: '1', d: '1'}].\n\nMergeGenerator supports template overriding. If a MergeGenerator is one of multiple top-level generators, its template will be merged with the top-level generator before the parameters are applied.", - Type: []string{"object"}, + Type: []string{"object"}, Properties: map[string]spec.Schema{ - "generators": { + "group": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetNestedGenerator"), - }, - }, - }, + Type: []string{"string"}, + Format: "", }, }, - "mergeKeys": { + "version": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, + Type: []string{"string"}, + Format: "", }, }, - "template": { + "kind": { SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"), + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "namespace": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", }, }, }, - Required: []string{"generators", "mergeKeys"}, }, }, - Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetNestedGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"}, } } -func schema_pkg_apis_application_v1alpha1_NestedMatrixGenerator(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_application_v1alpha1_KustomizeSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "NestedMatrixGenerator is a MatrixGenerator nested under another combination-type generator (MatrixGenerator or MergeGenerator). NestedMatrixGenerator does not have an override template, because template overriding has no meaning within the constituent generators of combination-type generators.\n\nNOTE: Nested matrix generator is not included directly in the CRD struct, instead it is included as a generic 'apiextensionsv1.JSON' object, and then marshalled into a NestedMatrixGenerator when processed.", - Type: []string{"object"}, + Type: []string{"object"}, Properties: map[string]spec.Schema{ - "generators": { + "group": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "namespace": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "annotationSelector": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "labelSelector": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_application_v1alpha1_ListGenerator(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ListGenerator include items info", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "elements": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON"), + }, + }, + }, + }, + }, + "template": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"), + }, + }, + "elementsYaml": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"elements"}, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate", "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON"}, + } +} + +func schema_pkg_apis_application_v1alpha1_ManagedNamespaceMetadata(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "labels": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "annotations": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_application_v1alpha1_MatrixGenerator(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "MatrixGenerator generates the cartesian product of two sets of parameters. The parameters are defined by two nested generators.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "generators": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetNestedGenerator"), + }, + }, + }, + }, + }, + "template": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"), + }, + }, + }, + Required: []string{"generators"}, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetNestedGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"}, + } +} + +func schema_pkg_apis_application_v1alpha1_MergeGenerator(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "MergeGenerator merges the output of two or more generators. Where the values for all specified merge keys are equal between two sets of generated parameters, the parameter sets will be merged with the parameters from the latter generator taking precedence. Parameter sets with merge keys not present in the base generator's params will be ignored. For example, if the first generator produced [{a: '1', b: '2'}, {c: '1', d: '1'}] and the second generator produced [{'a': 'override'}], the united parameters for merge keys = ['a'] would be [{a: 'override', b: '1'}, {c: '1', d: '1'}].\n\nMergeGenerator supports template overriding. If a MergeGenerator is one of multiple top-level generators, its template will be merged with the top-level generator before the parameters are applied.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "generators": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetNestedGenerator"), + }, + }, + }, + }, + }, + "mergeKeys": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "template": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"), + }, + }, + }, + Required: []string{"generators", "mergeKeys"}, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetNestedGenerator", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"}, + } +} + +func schema_pkg_apis_application_v1alpha1_NestedMatrixGenerator(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NestedMatrixGenerator is a MatrixGenerator nested under another combination-type generator (MatrixGenerator or MergeGenerator). NestedMatrixGenerator does not have an override template, because template overriding has no meaning within the constituent generators of combination-type generators.\n\nNOTE: Nested matrix generator is not included directly in the CRD struct, instead it is included as a generic 'apiextensionsv1.JSON' object, and then marshalled into a NestedMatrixGenerator when processed.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "generators": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTerminalGenerator"), @@ -3939,29 +4489,25 @@ func schema_pkg_apis_application_v1alpha1_OperationState(ref common.ReferenceCal } } -func schema_pkg_apis_application_v1alpha1_OrphanedResourceKey(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_application_v1alpha1_OptionalArray(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "OrphanedResourceKey is a reference to a resource to be ignored from", - Type: []string{"object"}, + Type: []string{"object"}, Properties: map[string]spec.Schema{ - "group": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "kind": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "name": { + "array": { SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", + Description: "Array is the value of an array type parameter.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, }, }, }, @@ -3970,11 +4516,70 @@ func schema_pkg_apis_application_v1alpha1_OrphanedResourceKey(ref common.Referen } } -func schema_pkg_apis_application_v1alpha1_OrphanedResourcesMonitorSettings(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_application_v1alpha1_OptionalMap(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "OrphanedResourcesMonitorSettings holds settings of orphaned resources monitoring", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "map": { + SchemaProps: spec.SchemaProps{ + Description: "Map is the value of a map type parameter.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_application_v1alpha1_OrphanedResourceKey(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OrphanedResourceKey is a reference to a resource to be ignored from", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_application_v1alpha1_OrphanedResourcesMonitorSettings(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OrphanedResourcesMonitorSettings holds settings of orphaned resources monitoring", Type: []string{"object"}, Properties: map[string]spec.Schema{ "warn": { @@ -4065,60 +4670,309 @@ func schema_pkg_apis_application_v1alpha1_OverrideIgnoreDiff(ref common.Referenc } } -func schema_pkg_apis_application_v1alpha1_ProjectRole(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_application_v1alpha1_PluginConfigMapRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the ConfigMap", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + } +} + +func schema_pkg_apis_application_v1alpha1_PluginGenerator(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PluginGenerator defines connection info specific to Plugin.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "configMapRef": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginConfigMapRef"), + }, + }, + "input": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginInput"), + }, + }, + "requeueAfterSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "RequeueAfterSeconds determines how long the ApplicationSet controller will wait before reconciling the ApplicationSet again.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "template": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"), + }, + }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "Values contains key/value pairs which are passed directly as parameters to the template. These values will not be sent as parameters to the plugin.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"configMapRef"}, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginConfigMapRef", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PluginInput"}, + } +} + +func schema_pkg_apis_application_v1alpha1_PluginInput(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "parameters": { + SchemaProps: spec.SchemaProps{ + Description: "Parameters contains the information to pass to the plugin. It is a map. The keys must be strings, and the values can be any type.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON"}, + } +} + +func schema_pkg_apis_application_v1alpha1_ProjectRole(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ProjectRole represents a role that has access to a project", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is a name for this role", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a description of the role", + Type: []string{"string"}, + Format: "", + }, + }, + "policies": { + SchemaProps: spec.SchemaProps{ + Description: "Policies Stores a list of casbin formatted strings that define access policies for the role in the project", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "jwtTokens": { + SchemaProps: spec.SchemaProps{ + Description: "JWTTokens are a list of generated JWT tokens bound to this role", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.JWTToken"), + }, + }, + }, + }, + }, + "groups": { + SchemaProps: spec.SchemaProps{ + Description: "Groups are a list of OIDC group claims bound to this role", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.JWTToken"}, + } +} + +func schema_pkg_apis_application_v1alpha1_PullRequestGenerator(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PullRequestGenerator defines a generator that scrapes a PullRequest API to find candidate pull requests.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "github": { + SchemaProps: spec.SchemaProps{ + Description: "Which provider to use and config for it.", + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGithub"), + }, + }, + "gitlab": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGitLab"), + }, + }, + "gitea": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGitea"), + }, + }, + "bitbucketServer": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorBitbucketServer"), + }, + }, + "filters": { + SchemaProps: spec.SchemaProps{ + Description: "Filters for which pull requests should be considered.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorFilter"), + }, + }, + }, + }, + }, + "requeueAfterSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Standard parameters.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "template": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"), + }, + }, + "bitbucket": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorBitbucket"), + }, + }, + "azuredevops": { + SchemaProps: spec.SchemaProps{ + Description: "Additional provider to use and config for it.", + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorAzureDevOps"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorAzureDevOps", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorBitbucket", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorBitbucketServer", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorFilter", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGitLab", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGitea", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGithub"}, + } +} + +func schema_pkg_apis_application_v1alpha1_PullRequestGeneratorAzureDevOps(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "ProjectRole represents a role that has access to a project", + Description: "PullRequestGeneratorAzureDevOps defines connection info specific to AzureDevOps.", Type: []string{"object"}, Properties: map[string]spec.Schema{ - "name": { + "organization": { SchemaProps: spec.SchemaProps{ - Description: "Name is a name for this role", + Description: "Azure DevOps org to scan. Required.", Default: "", Type: []string{"string"}, Format: "", }, }, - "description": { + "project": { SchemaProps: spec.SchemaProps{ - Description: "Description is a description of the role", + Description: "Azure DevOps project name to scan. Required.", + Default: "", Type: []string{"string"}, Format: "", }, }, - "policies": { + "repo": { SchemaProps: spec.SchemaProps{ - Description: "Policies Stores a list of casbin formatted strings that define access policies for the role in the project", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, + Description: "Azure DevOps repo name to scan. Required.", + Default: "", + Type: []string{"string"}, + Format: "", }, }, - "jwtTokens": { + "api": { SchemaProps: spec.SchemaProps{ - Description: "JWTTokens are a list of generated JWT tokens bound to this role", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.JWTToken"), - }, - }, - }, + Description: "The Azure DevOps API URL to talk to. If blank, use https://dev.azure.com/.", + Type: []string{"string"}, + Format: "", }, }, - "groups": { + "tokenRef": { SchemaProps: spec.SchemaProps{ - Description: "Groups are a list of OIDC group claims bound to this role", + Description: "Authentication token reference.", + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SecretRef"), + }, + }, + "labels": { + SchemaProps: spec.SchemaProps{ + Description: "Labels is used to filter the PRs that you want to target", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -4132,74 +4986,62 @@ func schema_pkg_apis_application_v1alpha1_ProjectRole(ref common.ReferenceCallba }, }, }, - Required: []string{"name"}, + Required: []string{"organization", "project", "repo"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.JWTToken"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SecretRef"}, } } -func schema_pkg_apis_application_v1alpha1_PullRequestGenerator(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_application_v1alpha1_PullRequestGeneratorBitbucket(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "PullRequestGenerator defines a generator that scrapes a PullRequest API to find candidate pull requests.", + Description: "PullRequestGeneratorBitbucket defines connection info specific to Bitbucket.", Type: []string{"object"}, Properties: map[string]spec.Schema{ - "github": { - SchemaProps: spec.SchemaProps{ - Description: "Which provider to use and config for it.", - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGithub"), - }, - }, - "gitlab": { - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGitLab"), - }, - }, - "gitea": { + "owner": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGitea"), + Description: "Workspace to scan. Required.", + Default: "", + Type: []string{"string"}, + Format: "", }, }, - "bitbucketServer": { + "repo": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorBitbucketServer"), + Description: "Repo name to scan. Required.", + Default: "", + Type: []string{"string"}, + Format: "", }, }, - "filters": { + "api": { SchemaProps: spec.SchemaProps{ - Description: "Filters for which pull requests should be considered.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorFilter"), - }, - }, - }, + Description: "The Bitbucket REST API URL to talk to. If blank, uses https://api.bitbucket.org/2.0.", + Type: []string{"string"}, + Format: "", }, }, - "requeueAfterSeconds": { + "basicAuth": { SchemaProps: spec.SchemaProps{ - Description: "Standard parameters.", - Type: []string{"integer"}, - Format: "int64", + Description: "Credentials for Basic auth", + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BasicAuthBitbucketServer"), }, }, - "template": { + "bearerToken": { SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"), + Description: "Credentials for AppToken (Bearer auth)", + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BearerTokenBitbucketCloud"), }, }, }, + Required: []string{"owner", "repo"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorBitbucketServer", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorFilter", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGitLab", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGitea", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.PullRequestGeneratorGithub"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BasicAuthBitbucketServer", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.BearerTokenBitbucketCloud"}, } } @@ -4207,7 +5049,7 @@ func schema_pkg_apis_application_v1alpha1_PullRequestGeneratorBitbucketServer(re return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "PullRequestGenerator defines connection info specific to BitbucketServer.", + Description: "PullRequestGeneratorBitbucketServer defines connection info specific to BitbucketServer.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "project": { @@ -4262,6 +5104,12 @@ func schema_pkg_apis_application_v1alpha1_PullRequestGeneratorFilter(ref common. Format: "", }, }, + "targetBranchMatch": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, }, }, }, @@ -4318,6 +5166,13 @@ func schema_pkg_apis_application_v1alpha1_PullRequestGeneratorGitLab(ref common. Format: "", }, }, + "insecure": { + SchemaProps: spec.SchemaProps{ + Description: "Skips validating the SCM provider's TLS certificate - useful for self-signed certificates.; default: false", + Type: []string{"boolean"}, + Format: "", + }, + }, }, Required: []string{"project"}, }, @@ -4331,7 +5186,7 @@ func schema_pkg_apis_application_v1alpha1_PullRequestGeneratorGitea(ref common.R return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "PullRequestGenerator defines connection info specific to Gitea.", + Description: "PullRequestGeneratorGitea defines connection info specific to Gitea.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "owner": { @@ -4588,6 +5443,13 @@ func schema_pkg_apis_application_v1alpha1_RepoCreds(ref common.ReferenceCallback Format: "", }, }, + "forceHttpBasicAuth": { + SchemaProps: spec.SchemaProps{ + Description: "ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections", + Type: []string{"boolean"}, + Format: "", + }, + }, }, Required: []string{"url"}, }, @@ -4785,6 +5647,13 @@ func schema_pkg_apis_application_v1alpha1_Repository(ref common.ReferenceCallbac Format: "", }, }, + "forceHttpBasicAuth": { + SchemaProps: spec.SchemaProps{ + Description: "ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections", + Type: []string{"boolean"}, + Format: "", + }, + }, }, Required: []string{"repo"}, }, @@ -4948,6 +5817,18 @@ func schema_pkg_apis_application_v1alpha1_ResourceAction(ref common.ReferenceCal Format: "", }, }, + "iconClass": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "displayName": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, }, }, }, @@ -5456,6 +6337,12 @@ func schema_pkg_apis_application_v1alpha1_ResourceOverride(ref common.ReferenceC Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OverrideIgnoreDiff"), }, }, + "IgnoreResourceUpdates": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OverrideIgnoreDiff"), + }, + }, "KnownTypeFields": { SchemaProps: spec.SchemaProps{ Type: []string{"array"}, @@ -5470,7 +6357,7 @@ func schema_pkg_apis_application_v1alpha1_ResourceOverride(ref common.ReferenceC }, }, }, - Required: []string{"HealthLua", "UseOpenLibs", "Actions", "IgnoreDifferences", "KnownTypeFields"}, + Required: []string{"HealthLua", "UseOpenLibs", "Actions", "IgnoreDifferences", "IgnoreResourceUpdates", "KnownTypeFields"}, }, }, Dependencies: []string{ @@ -5789,12 +6676,19 @@ func schema_pkg_apis_application_v1alpha1_RevisionHistory(ref common.ReferenceCa }, }, }, + "initiatedBy": { + SchemaProps: spec.SchemaProps{ + Description: "InitiatedBy contains information about who initiated the operations", + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OperationInitiator"), + }, + }, }, Required: []string{"deployedAt", "id"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSource", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSource", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OperationInitiator", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, } } @@ -5929,11 +6823,81 @@ func schema_pkg_apis_application_v1alpha1_SCMProviderGenerator(ref common.Refere Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate"), }, }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "Values contains key/value pairs which are passed directly as parameters to the template", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "awsCodeCommit": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorAWSCodeCommit"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorAWSCodeCommit", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorAzureDevOps", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorBitbucket", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorBitbucketServer", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorFilter", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorGitea", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorGithub", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorGitlab"}, + } +} + +func schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorAWSCodeCommit(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SCMProviderGeneratorAWSCodeCommit defines connection info specific to AWS CodeCommit.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "tagFilters": { + SchemaProps: spec.SchemaProps{ + Description: "TagFilters provides the tag filter(s) for repo discovery", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.TagFilter"), + }, + }, + }, + }, + }, + "role": { + SchemaProps: spec.SchemaProps{ + Description: "Role provides the AWS IAM role to assume, for cross-account repo discovery if not provided, AppSet controller will use its pod/node identity to discover.", + Type: []string{"string"}, + Format: "", + }, + }, + "region": { + SchemaProps: spec.SchemaProps{ + Description: "Region provides the AWS region to discover repos. if not provided, AppSet controller will infer the current region from environment.", + Type: []string{"string"}, + Format: "", + }, + }, + "allBranches": { + SchemaProps: spec.SchemaProps{ + Description: "Scan all branches instead of just the default branch.", + Type: []string{"boolean"}, + Format: "", + }, + }, }, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSetTemplate", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorAzureDevOps", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorBitbucket", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorBitbucketServer", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorFilter", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorGitea", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorGithub", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.SCMProviderGeneratorGitlab"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.TagFilter"}, } } @@ -6288,6 +7252,27 @@ func schema_pkg_apis_application_v1alpha1_SCMProviderGeneratorGitlab(ref common. Format: "", }, }, + "insecure": { + SchemaProps: spec.SchemaProps{ + Description: "Skips validating the SCM provider's TLS certificate - useful for self-signed certificates.; default: false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "includeSharedProjects": { + SchemaProps: spec.SchemaProps{ + Description: "When recursing through subgroups, also include shared Projects (true) or scan only the subgroups under same path (false). Defaults to \"true\"", + Type: []string{"boolean"}, + Format: "", + }, + }, + "topic": { + SchemaProps: spec.SchemaProps{ + Description: "Filter repos list based on Gitlab Topic.", + Type: []string{"string"}, + Format: "", + }, + }, }, Required: []string{"group"}, }, @@ -6572,12 +7557,18 @@ func schema_pkg_apis_application_v1alpha1_SyncOperationResult(ref common.Referen }, }, }, + "managedNamespaceMetadata": { + SchemaProps: spec.SchemaProps{ + Description: "ManagedNamespaceMetadata contains the current sync state of managed namespace metadata", + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ManagedNamespaceMetadata"), + }, + }, }, Required: []string{"revision"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSource", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceResult"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSource", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ManagedNamespaceMetadata", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ResourceResult"}, } } @@ -6645,7 +7636,7 @@ func schema_pkg_apis_application_v1alpha1_SyncPolicyAutomated(ref common.Referen }, "selfHeal": { SchemaProps: spec.SchemaProps{ - Description: "SelfHeal specifes whether to revert resources back to their desired state upon modification in the cluster (default: false)", + Description: "SelfHeal specifies whether to revert resources back to their desired state upon modification in the cluster (default: false)", Type: []string{"boolean"}, Format: "", }, @@ -6926,6 +7917,32 @@ func schema_pkg_apis_application_v1alpha1_TLSClientConfig(ref common.ReferenceCa } } +func schema_pkg_apis_application_v1alpha1_TagFilter(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"key"}, + }, + }, + } +} + func schema_pkg_apis_application_v1alpha1_objectMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -6976,6 +7993,12 @@ func schema_pkg_apis_application_v1alpha1_rawResourceOverride(ref common.Referen Format: "", }, }, + "ignoreResourceUpdates": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, "knownTypeFields": { SchemaProps: spec.SchemaProps{ Type: []string{"array"}, diff --git a/pkg/apis/application/v1alpha1/repository_types.go b/pkg/apis/application/v1alpha1/repository_types.go index 2104f2b141434..3a557813d87c6 100644 --- a/pkg/apis/application/v1alpha1/repository_types.go +++ b/pkg/apis/application/v1alpha1/repository_types.go @@ -1,6 +1,7 @@ package v1alpha1 import ( + "fmt" "net/url" "github.com/argoproj/argo-cd/v2/util/cert" @@ -41,6 +42,8 @@ type RepoCreds struct { GCPServiceAccountKey string `json:"gcpServiceAccountKey,omitempty" protobuf:"bytes,13,opt,name=gcpServiceAccountKey"` // Proxy specifies the HTTP/HTTPS proxy used to access repos at the repo server Proxy string `json:"proxy,omitempty" protobuf:"bytes,19,opt,name=proxy"` + // ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections + ForceHttpBasicAuth bool `json:"forceHttpBasicAuth,omitempty" protobuf:"bytes,20,opt,name=forceHttpBasicAuth"` } // Repository is a repository holding application configurations @@ -88,6 +91,8 @@ type Repository struct { Project string `json:"project,omitempty" protobuf:"bytes,20,opt,name=project"` // GCPServiceAccountKey specifies the service account key in JSON format to be used for getting credentials to Google Cloud Source repos GCPServiceAccountKey string `json:"gcpServiceAccountKey,omitempty" protobuf:"bytes,21,opt,name=gcpServiceAccountKey"` + // ForceHttpBasicAuth specifies whether Argo CD should attempt to force basic auth for HTTP connections + ForceHttpBasicAuth bool `json:"forceHttpBasicAuth,omitempty" protobuf:"bytes,22,opt,name=forceHttpBasicAuth"` } // IsInsecure returns true if the repository has been configured to skip server verification @@ -138,6 +143,7 @@ func (repo *Repository) CopyCredentialsFromRepo(source *Repository) { if repo.GCPServiceAccountKey == "" { repo.GCPServiceAccountKey = source.GCPServiceAccountKey } + repo.ForceHttpBasicAuth = source.ForceHttpBasicAuth } } @@ -177,6 +183,7 @@ func (repo *Repository) CopyCredentialsFrom(source *RepoCreds) { if repo.Proxy == "" { repo.Proxy = source.Proxy } + repo.ForceHttpBasicAuth = source.ForceHttpBasicAuth } } @@ -186,16 +193,16 @@ func (repo *Repository) GetGitCreds(store git.CredsStore) git.Creds { return git.NopCreds{} } if repo.Password != "" { - return git.NewHTTPSCreds(repo.Username, repo.Password, repo.TLSClientCertData, repo.TLSClientCertKey, repo.IsInsecure(), repo.Proxy, store) + return git.NewHTTPSCreds(repo.Username, repo.Password, repo.TLSClientCertData, repo.TLSClientCertKey, repo.IsInsecure(), repo.Proxy, store, repo.ForceHttpBasicAuth) } if repo.SSHPrivateKey != "" { - return git.NewSSHCreds(repo.SSHPrivateKey, getCAPath(repo.Repo), repo.IsInsecure(), store) + return git.NewSSHCreds(repo.SSHPrivateKey, getCAPath(repo.Repo), repo.IsInsecure(), store, repo.Proxy) } if repo.GithubAppPrivateKey != "" && repo.GithubAppId != 0 && repo.GithubAppInstallationId != 0 { return git.NewGitHubAppCreds(repo.GithubAppId, repo.GithubAppInstallationId, repo.GithubAppPrivateKey, repo.GitHubAppEnterpriseBaseURL, repo.Repo, repo.TLSClientCertData, repo.TLSClientCertKey, repo.IsInsecure(), repo.Proxy, store) } if repo.GCPServiceAccountKey != "" { - return git.NewGoogleCloudCreds(repo.GCPServiceAccountKey) + return git.NewGoogleCloudCreds(repo.GCPServiceAccountKey, store) } return git.NopCreds{} } @@ -259,6 +266,14 @@ func (m *Repository) CopySettingsFrom(source *Repository) { } } +// StringForLogging gets a string representation of the Repository which is safe to log or return to the user. +func (m *Repository) StringForLogging() string { + if m == nil { + return "" + } + return fmt.Sprintf("&Repository{Repo: %q, Type: %q, Name: %q, Project: %q}", m.Repo, m.Type, m.Name, m.Project) +} + // Repositories defines a list of Repository configurations type Repositories []*Repository diff --git a/pkg/apis/application/v1alpha1/types.go b/pkg/apis/application/v1alpha1/types.go index 409489b14bb67..abd2735710e72 100644 --- a/pkg/apis/application/v1alpha1/types.go +++ b/pkg/apis/application/v1alpha1/types.go @@ -18,7 +18,6 @@ import ( "github.com/argoproj/gitops-engine/pkg/health" synccommon "github.com/argoproj/gitops-engine/pkg/sync/common" - "github.com/ghodss/yaml" "github.com/robfig/cron/v3" log "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" @@ -26,16 +25,21 @@ import ( v1 "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/apimachinery/pkg/util/intstr" utilnet "k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/util/collections" + "github.com/argoproj/argo-cd/v2/util/env" "github.com/argoproj/argo-cd/v2/util/helm" + utilhttp "github.com/argoproj/argo-cd/v2/util/http" "github.com/argoproj/argo-cd/v2/util/security" ) @@ -67,7 +71,7 @@ type ApplicationSpec struct { // SyncPolicy controls when and how a sync will be performed SyncPolicy *SyncPolicy `json:"syncPolicy,omitempty" protobuf:"bytes,4,name=syncPolicy"` // IgnoreDifferences is a list of resources and their fields which should be ignored during comparison - IgnoreDifferences []ResourceIgnoreDifferences `json:"ignoreDifferences,omitempty" protobuf:"bytes,5,name=ignoreDifferences"` + IgnoreDifferences IgnoreDifferences `json:"ignoreDifferences,omitempty" protobuf:"bytes,5,name=ignoreDifferences"` // Info contains a list of information (URLs, email addresses, and plain text) that relates to the application Info []Info `json:"info,omitempty" protobuf:"bytes,6,name=info"` // RevisionHistoryLimit limits the number of items kept in the application's revision history, which is used for informational purposes as well as for rollbacks to previous versions. @@ -81,6 +85,12 @@ type ApplicationSpec struct { Sources ApplicationSources `json:"sources,omitempty" protobuf:"bytes,8,opt,name=sources"` } +type IgnoreDifferences []ResourceIgnoreDifferences + +func (id IgnoreDifferences) Equals(other IgnoreDifferences) bool { + return reflect.DeepEqual(id, other) +} + type TrackingMethod string // ResourceIgnoreDifferences contains resource filter and list of json paths which should be ignored during comparison with live state. @@ -183,6 +193,18 @@ type ApplicationSource struct { // ApplicationSources contains list of required information about the sources of an application type ApplicationSources []ApplicationSource +func (s ApplicationSources) Equals(other ApplicationSources) bool { + if len(s) != len(other) { + return false + } + for i := range s { + if !s[i].Equals(&other[i]) { + return false + } + } + return true +} + func (a *ApplicationSpec) GetSource() ApplicationSource { // if Application has multiple sources, return the first source in sources if a.HasMultipleSources() { @@ -208,9 +230,12 @@ func (a *ApplicationSpec) HasMultipleSources() bool { return a.Sources != nil && len(a.Sources) > 0 } -func (a *ApplicationSpec) GetSourcePtr() *ApplicationSource { +func (a *ApplicationSpec) GetSourcePtr(index int) *ApplicationSource { // if Application has multiple sources, return the first source in sources if a.HasMultipleSources() { + if index > 0 { + return &a.Sources[index-1] + } return &a.Sources[0] } return a.Source @@ -285,8 +310,9 @@ type ApplicationSourceHelm struct { Parameters []HelmParameter `json:"parameters,omitempty" protobuf:"bytes,2,opt,name=parameters"` // ReleaseName is the Helm release name to use. If omitted it will use the application name ReleaseName string `json:"releaseName,omitempty" protobuf:"bytes,3,opt,name=releaseName"` - // Values specifies Helm values to be passed to helm template, typically defined as a block - Values string `json:"values,omitempty" protobuf:"bytes,4,opt,name=values"` + // Values specifies Helm values to be passed to helm template, typically defined as a block. ValuesObject takes precedence over Values, so use one or the other. + // +patchStrategy=replace + Values string `json:"values,omitempty" patchStrategy:"replace" protobuf:"bytes,4,opt,name=values"` // FileParameters are file parameters to the helm template FileParameters []HelmFileParameter `json:"fileParameters,omitempty" protobuf:"bytes,5,opt,name=fileParameters"` // Version is the Helm version to use for templating ("3") @@ -297,6 +323,9 @@ type ApplicationSourceHelm struct { IgnoreMissingValueFiles bool `json:"ignoreMissingValueFiles,omitempty" protobuf:"bytes,8,opt,name=ignoreMissingValueFiles"` // SkipCrds skips custom resource definition installation step (Helm's --skip-crds) SkipCrds bool `json:"skipCrds,omitempty" protobuf:"bytes,9,opt,name=skipCrds"` + // ValuesObject specifies Helm values to be passed to helm template, defined as a map. This takes precedence over Values. + // +kubebuilder:pruning:PreserveUnknownFields + ValuesObject *runtime.RawExtension `json:"valuesObject,omitempty" protobuf:"bytes,10,opt,name=valuesObject"` } // HelmParameter is a parameter that's passed to helm template during manifest generation @@ -378,7 +407,7 @@ func (in *ApplicationSourceHelm) AddFileParameter(p HelmFileParameter) { // IsZero Returns true if the Helm options in an application source are considered zero func (h *ApplicationSourceHelm) IsZero() bool { - return h == nil || (h.Version == "") && (h.ReleaseName == "") && len(h.ValueFiles) == 0 && len(h.Parameters) == 0 && len(h.FileParameters) == 0 && h.Values == "" && !h.PassCredentials && !h.IgnoreMissingValueFiles && !h.SkipCrds + return h == nil || (h.Version == "") && (h.ReleaseName == "") && len(h.ValueFiles) == 0 && len(h.Parameters) == 0 && len(h.FileParameters) == 0 && h.ValuesIsEmpty() && !h.PassCredentials && !h.IgnoreMissingValueFiles && !h.SkipCrds } // KustomizeImage represents a Kustomize image definition in the format [old_image_name=]: @@ -433,14 +462,109 @@ type ApplicationSourceKustomize struct { ForceCommonLabels bool `json:"forceCommonLabels,omitempty" protobuf:"bytes,7,opt,name=forceCommonLabels"` // ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize apps ForceCommonAnnotations bool `json:"forceCommonAnnotations,omitempty" protobuf:"bytes,8,opt,name=forceCommonAnnotations"` + // Namespace sets the namespace that Kustomize adds to all resources + Namespace string `json:"namespace,omitempty" protobuf:"bytes,9,opt,name=namespace"` + // CommonAnnotationsEnvsubst specifies whether to apply env variables substitution for annotation values + CommonAnnotationsEnvsubst bool `json:"commonAnnotationsEnvsubst,omitempty" protobuf:"bytes,10,opt,name=commonAnnotationsEnvsubst"` + // Replicas is a list of Kustomize Replicas override specifications + Replicas KustomizeReplicas `json:"replicas,omitempty" protobuf:"bytes,11,opt,name=replicas"` + // Patches is a list of Kustomize patches + Patches KustomizePatches `json:"patches,omitempty" protobuf:"bytes,12,opt,name=patches"` + // Components specifies a list of kustomize components to add to the kustomization before building + Components []string `json:"components,omitempty" protobuf:"bytes,13,rep,name=components"` + //LabelWithoutSelector specifies whether to apply common labels to resource selectors or not + LabelWithoutSelector bool `json:"labelWithoutSelector,omitempty" protobuf:"bytes,14,opt,name=labelWithoutSelector"` +} + +type KustomizeReplica struct { + // Name of Deployment or StatefulSet + Name string `json:"name" protobuf:"bytes,1,name=name"` + // Number of replicas + Count intstr.IntOrString `json:"count" protobuf:"bytes,2,name=count"` +} + +type KustomizeReplicas []KustomizeReplica + +// GetIntCount returns Count converted to int. +// If parsing error occurs, returns 0 and error. +func (kr KustomizeReplica) GetIntCount() (int, error) { + if kr.Count.Type == intstr.String { + if count, err := strconv.Atoi(kr.Count.StrVal); err != nil { + return 0, fmt.Errorf("expected integer value for count. Received: %s", kr.Count.StrVal) + } else { + return count, nil + } + } else { + return kr.Count.IntValue(), nil + } +} + +// NewKustomizeReplica parses a string in format name=count into a KustomizeReplica object and returns it +func NewKustomizeReplica(text string) (*KustomizeReplica, error) { + parts := strings.SplitN(text, "=", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("expected parameter of the form: name=count. Received: %s", text) + } + + kr := &KustomizeReplica{ + Name: parts[0], + Count: intstr.Parse(parts[1]), + } + + if _, err := kr.GetIntCount(); err != nil { + return nil, err + } + + return kr, nil +} + +type KustomizePatches []KustomizePatch + +type KustomizePatch struct { + Path string `json:"path,omitempty" yaml:"path,omitempty" protobuf:"bytes,1,opt,name=path"` + Patch string `json:"patch,omitempty" yaml:"patch,omitempty" protobuf:"bytes,2,opt,name=patch"` + Target *KustomizeSelector `json:"target,omitempty" yaml:"target,omitempty" protobuf:"bytes,3,opt,name=target"` + Options map[string]bool `json:"options,omitempty" yaml:"options,omitempty" protobuf:"bytes,4,opt,name=options"` +} + +// Copied from: https://github.com/kubernetes-sigs/kustomize/blob/cd7ba1744eadb793ab7cd056a76ee8a5ca725db9/api/types/patch.go +func (p *KustomizePatch) Equals(o KustomizePatch) bool { + targetEqual := (p.Target == o.Target) || + (p.Target != nil && o.Target != nil && *p.Target == *o.Target) + return p.Path == o.Path && + p.Patch == o.Patch && + targetEqual && + reflect.DeepEqual(p.Options, o.Options) +} + +type KustomizeSelector struct { + KustomizeResId `json:",inline,omitempty" yaml:",inline,omitempty" protobuf:"bytes,1,opt,name=resId"` + AnnotationSelector string `json:"annotationSelector,omitempty" yaml:"annotationSelector,omitempty" protobuf:"bytes,2,opt,name=annotationSelector"` + LabelSelector string `json:"labelSelector,omitempty" yaml:"labelSelector,omitempty" protobuf:"bytes,3,opt,name=labelSelector"` +} + +type KustomizeResId struct { + KustomizeGvk `json:",inline,omitempty" yaml:",inline,omitempty" protobuf:"bytes,1,opt,name=gvk"` + Name string `json:"name,omitempty" yaml:"name,omitempty" protobuf:"bytes,2,opt,name=name"` + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"` +} + +type KustomizeGvk struct { + Group string `json:"group,omitempty" yaml:"group,omitempty" protobuf:"bytes,1,opt,name=group"` + Version string `json:"version,omitempty" yaml:"version,omitempty" protobuf:"bytes,2,opt,name=version"` + Kind string `json:"kind,omitempty" yaml:"kind,omitempty" protobuf:"bytes,3,opt,name=kind"` } // AllowsConcurrentProcessing returns true if multiple processes can run Kustomize builds on the same source at the same time func (k *ApplicationSourceKustomize) AllowsConcurrentProcessing() bool { return len(k.Images) == 0 && len(k.CommonLabels) == 0 && + len(k.CommonAnnotations) == 0 && k.NamePrefix == "" && - k.NameSuffix == "" + k.Namespace == "" && + k.NameSuffix == "" && + len(k.Patches) == 0 && + len(k.Components) == 0 } // IsZero returns true when the Kustomize options are considered empty @@ -449,9 +573,13 @@ func (k *ApplicationSourceKustomize) IsZero() bool { k.NamePrefix == "" && k.NameSuffix == "" && k.Version == "" && + k.Namespace == "" && len(k.Images) == 0 && + len(k.Replicas) == 0 && len(k.CommonLabels) == 0 && - len(k.CommonAnnotations) == 0 + len(k.CommonAnnotations) == 0 && + len(k.Patches) == 0 && + len(k.Components) == 0 } // MergeImage merges a new Kustomize image identifier in to a list of images @@ -464,6 +592,26 @@ func (k *ApplicationSourceKustomize) MergeImage(image KustomizeImage) { } } +// MergeReplicas merges a new Kustomize replica identifier in to a list of replicas +func (k *ApplicationSourceKustomize) MergeReplica(replica KustomizeReplica) { + i := k.Replicas.FindByName(replica.Name) + if i >= 0 { + k.Replicas[i] = replica + } else { + k.Replicas = append(k.Replicas, replica) + } +} + +// Find returns a positive integer representing the index in the list of replicas +func (rs KustomizeReplicas) FindByName(name string) int { + for i, r := range rs { + if r.Name == name { + return i + } + } + return -1 +} + // JsonnetVar represents a variable to be passed to jsonnet during manifest generation type JsonnetVar struct { Name string `json:"name" protobuf:"bytes,1,opt,name=name"` @@ -513,19 +661,155 @@ func (d *ApplicationSourceDirectory) IsZero() bool { return d == nil || !d.Recurse && d.Jsonnet.IsZero() } +type OptionalMap struct { + // Map is the value of a map type parameter. + // +optional + Map map[string]string `json:"map" protobuf:"bytes,1,rep,name=map"` + // We need the explicit +optional so that kube-builder generates the CRD without marking this as required. +} + +// Equals returns true if the two OptionalMap objects are equal. We can't use reflect.DeepEqual because it will return +// false if one of the maps is nil and the other is an empty map. This is because the JSON unmarshaller will set the +// map to nil if it is empty, but the protobuf unmarshaller will set it to an empty map. +func (o *OptionalMap) Equals(other *OptionalMap) bool { + if o == nil && other == nil { + return true + } + if o == nil || other == nil { + return false + } + if len(o.Map) != len(other.Map) { + return false + } + if o.Map == nil && other.Map == nil { + return true + } + // The next two blocks are critical. Depending on whether the struct was populated from JSON or protobufs, the map + // field will be either nil or an empty map. They mean the same thing: the map is empty. + if o.Map == nil && len(other.Map) == 0 { + return true + } + if other.Map == nil && len(o.Map) == 0 { + return true + } + return reflect.DeepEqual(o.Map, other.Map) +} + +type OptionalArray struct { + // Array is the value of an array type parameter. + // +optional + Array []string `json:"array" protobuf:"bytes,1,rep,name=array"` + // We need the explicit +optional so that kube-builder generates the CRD without marking this as required. +} + +// Equals returns true if the two OptionalArray objects are equal. We can't use reflect.DeepEqual because it will return +// false if one of the arrays is nil and the other is an empty array. This is because the JSON unmarshaller will set the +// array to nil if it is empty, but the protobuf unmarshaller will set it to an empty array. +func (o *OptionalArray) Equals(other *OptionalArray) bool { + if o == nil && other == nil { + return true + } + if o == nil || other == nil { + return false + } + if len(o.Array) != len(other.Array) { + return false + } + if o.Array == nil && other.Array == nil { + return true + } + // The next two blocks are critical. Depending on whether the struct was populated from JSON or protobufs, the array + // field will be either nil or an empty array. They mean the same thing: the array is empty. + if o.Array == nil && len(other.Array) == 0 { + return true + } + if other.Array == nil && len(o.Array) == 0 { + return true + } + return reflect.DeepEqual(o.Array, other.Array) +} + type ApplicationSourcePluginParameter struct { + // We use pointers to structs because go-to-protobuf represents pointers to arrays/maps as repeated fields. + // These repeated fields have no way to represent "present but empty." So we would have no way to distinguish + // {name: parameters, array: []} from {name: parameter} + // By wrapping the array/map in a struct, we can use a pointer to the struct to represent "present but empty." + // Name is the name identifying a parameter. Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"` // String_ is the value of a string type parameter. String_ *string `json:"string,omitempty" protobuf:"bytes,5,opt,name=string"` // Map is the value of a map type parameter. - Map map[string]string `json:"map,omitempty" protobuf:"bytes,3,rep,name=map"` + *OptionalMap `json:",omitempty" protobuf:"bytes,3,rep,name=map"` // Array is the value of an array type parameter. - Array []string `json:"array,omitempty" protobuf:"bytes,4,rep,name=array"` + *OptionalArray `json:",omitempty" protobuf:"bytes,4,rep,name=array"` +} + +func (p ApplicationSourcePluginParameter) Equals(other ApplicationSourcePluginParameter) bool { + if p.Name != other.Name { + return false + } + if !reflect.DeepEqual(p.String_, other.String_) { + return false + } + return p.OptionalMap.Equals(other.OptionalMap) && p.OptionalArray.Equals(other.OptionalArray) +} + +// MarshalJSON is a custom JSON marshaller for ApplicationSourcePluginParameter. We need this custom marshaler because, +// when ApplicationSourcePluginParameter is unmarshaled, either from JSON or protobufs, the fields inside OptionalMap and +// OptionalArray are not set. The default JSON marshaler marshals these as "null." But really what we want to represent +// is an empty map or array. +// +// There are efforts to change things upstream, but nothing has been merged yet. See https://github.com/golang/go/issues/37711 +func (p ApplicationSourcePluginParameter) MarshalJSON() ([]byte, error) { + out := map[string]interface{}{} + out["name"] = p.Name + if p.String_ != nil { + out["string"] = p.String_ + } + if p.OptionalMap != nil { + if p.OptionalMap.Map == nil { + // Nil is not the same as a nil map. Nil means the field was not set, while a nil map means the field was set to an empty map. + // Either way, we want to marshal it as "{}". + out["map"] = map[string]string{} + } else { + out["map"] = p.OptionalMap.Map + } + } + if p.OptionalArray != nil { + if p.OptionalArray.Array == nil { + // Nil is not the same as a nil array. Nil means the field was not set, while a nil array means the field was set to an empty array. + // Either way, we want to marshal it as "[]". + out["array"] = []string{} + } else { + out["array"] = p.OptionalArray.Array + } + } + bytes, err := json.Marshal(out) + if err != nil { + return nil, err + } + return bytes, nil } type ApplicationSourcePluginParameters []ApplicationSourcePluginParameter +func (p ApplicationSourcePluginParameters) Equals(other ApplicationSourcePluginParameters) bool { + if len(p) != len(other) { + return false + } + for i := range p { + if !p[i].Equals(other[i]) { + return false + } + } + return true +} + +func (p ApplicationSourcePluginParameters) IsZero() bool { + return len(p) == 0 +} + // Environ builds a list of environment variables to represent parameters sent to a plugin from the Application // manifest. Parameters are represented as one large stringified JSON array (under `ARGOCD_APP_PARAMETERS`). They're // also represented as individual environment variables, each variable's key being an escaped version of the parameter's @@ -544,13 +828,13 @@ func (p ApplicationSourcePluginParameters) Environ() ([]string, error) { if param.String_ != nil { env = append(env, fmt.Sprintf("%s=%s", envBaseName, *param.String_)) } - if param.Map != nil { - for key, value := range param.Map { + if param.OptionalMap != nil { + for key, value := range param.OptionalMap.Map { env = append(env, fmt.Sprintf("%s_%s=%s", envBaseName, escaped(key), value)) } } - if param.Array != nil { - for i, value := range param.Array { + if param.OptionalArray != nil { + for i, value := range param.OptionalArray.Array { env = append(env, fmt.Sprintf("%s_%d=%s", envBaseName, i, value)) } } @@ -572,9 +856,28 @@ type ApplicationSourcePlugin struct { Parameters ApplicationSourcePluginParameters `json:"parameters,omitempty" protobuf:"bytes,3,opt,name=parameters"` } +func (c *ApplicationSourcePlugin) Equals(other *ApplicationSourcePlugin) bool { + if c == nil && other == nil { + return true + } + if c == nil || other == nil { + return false + } + if !c.Parameters.Equals(other.Parameters) { + return false + } + // DeepEqual works fine for fields besides Parameters. Since we already know that Parameters are equal, we can + // set them to nil and then do a DeepEqual. + leftCopy := c.DeepCopy() + rightCopy := other.DeepCopy() + leftCopy.Parameters = nil + rightCopy.Parameters = nil + return reflect.DeepEqual(leftCopy, rightCopy) +} + // IsZero returns true if the ApplicationSourcePlugin is considered empty func (c *ApplicationSourcePlugin) IsZero() bool { - return c == nil || c.Name == "" && c.Env.IsZero() + return c == nil || c.Name == "" && c.Env.IsZero() && c.Parameters.IsZero() } // AddEnvEntry merges an EnvEntry into a list of entries. If an entry with the same name already exists, @@ -607,12 +910,12 @@ func (c *ApplicationSourcePlugin) RemoveEnvEntry(key string) error { // ApplicationDestination holds information about the application's destination type ApplicationDestination struct { - // Server specifies the URL of the target cluster and must be set to the Kubernetes control plane API + // Server specifies the URL of the target cluster's Kubernetes control plane API. This must be set if Name is not set. Server string `json:"server,omitempty" protobuf:"bytes,1,opt,name=server"` // Namespace specifies the target namespace for the application's resources. // The namespace will only be set for namespace-scoped resources that have not set a value for .metadata.namespace Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,opt,name=namespace"` - // Name is an alternate way of specifying the target cluster by its symbolic name + // Name is an alternate way of specifying the target cluster by its symbolic name. This must be set if Server is not set. Name string `json:"name,omitempty" protobuf:"bytes,3,opt,name=name"` // nolint:govet @@ -653,6 +956,37 @@ type ApplicationStatus struct { ResourceHealthSource ResourceHealthLocation `json:"resourceHealthSource,omitempty" protobuf:"bytes,11,opt,name=resourceHealthSource"` // SourceTypes specifies the type of the sources included in the application SourceTypes []ApplicationSourceType `json:"sourceTypes,omitempty" protobuf:"bytes,12,opt,name=sourceTypes"` + // ControllerNamespace indicates the namespace in which the application controller is located + ControllerNamespace string `json:"controllerNamespace,omitempty" protobuf:"bytes,13,opt,name=controllerNamespace"` +} + +// GetRevisions will return the current revision associated with the Application. +// If app has multisources, it will return all corresponding revisions preserving +// order from the app.spec.sources. If app has only one source, it will return a +// single revision in the list. +func (a *ApplicationStatus) GetRevisions() []string { + revisions := []string{} + if len(a.Sync.Revisions) > 0 { + revisions = a.Sync.Revisions + } else if a.Sync.Revision != "" { + revisions = append(revisions, a.Sync.Revision) + } + return revisions +} + +// BuildComparedToStatus will build a ComparedTo object based on the current +// Application state. +func (app *Application) BuildComparedToStatus() ComparedTo { + ct := ComparedTo{ + Destination: app.Spec.Destination, + IgnoreDifferences: app.Spec.IgnoreDifferences, + } + if app.Spec.HasMultipleSources() { + ct.Sources = app.Spec.Sources + } else { + ct.Source = app.Spec.GetSource() + } + return ct } // JWTTokens represents a list of JWT tokens @@ -839,11 +1173,12 @@ type SyncPolicy struct { Retry *RetryStrategy `json:"retry,omitempty" protobuf:"bytes,3,opt,name=retry"` // ManagedNamespaceMetadata controls metadata in the given namespace (if CreateNamespace=true) ManagedNamespaceMetadata *ManagedNamespaceMetadata `json:"managedNamespaceMetadata,omitempty" protobuf:"bytes,4,opt,name=managedNamespaceMetadata"` + // If you add a field here, be sure to update IsZero. } // IsZero returns true if the sync policy is empty func (p *SyncPolicy) IsZero() bool { - return p == nil || (p.Automated == nil && len(p.SyncOptions) == 0 && p.Retry == nil) + return p == nil || (p.Automated == nil && len(p.SyncOptions) == 0 && p.Retry == nil && p.ManagedNamespaceMetadata == nil) } // RetryStrategy contains information about the strategy to apply when a sync failed @@ -913,7 +1248,7 @@ type Backoff struct { type SyncPolicyAutomated struct { // Prune specifies whether to delete resources from the cluster that are not found in the sources anymore as part of automated sync (default: false) Prune bool `json:"prune,omitempty" protobuf:"bytes,1,opt,name=prune"` - // SelfHeal specifes whether to revert resources back to their desired state upon modification in the cluster (default: false) + // SelfHeal specifies whether to revert resources back to their desired state upon modification in the cluster (default: false) SelfHeal bool `json:"selfHeal,omitempty" protobuf:"bytes,2,opt,name=selfHeal"` // AllowEmpty allows apps have zero live resources (default: false) AllowEmpty bool `json:"allowEmpty,omitempty" protobuf:"bytes,3,opt,name=allowEmpty"` @@ -973,6 +1308,15 @@ type RevisionMetadata struct { SignatureInfo string `json:"signatureInfo,omitempty" protobuf:"bytes,5,opt,name=signatureInfo"` } +// ChartDetails contains helm chart metadata for a specific version +type ChartDetails struct { + Description string `json:"description,omitempty" protobuf:"bytes,1,opt,name=description"` + // The URL of this projects home page, e.g. "http://example.com" + Home string `json:"home,omitempty" protobuf:"bytes,2,opt,name=home"` + // List of maintainer details, name and email, e.g. ["John Doe "] + Maintainers []string `json:"maintainers,omitempty" protobuf:"bytes,3,opt,name=maintainers"` +} + // SyncOperationResult represent result of sync operation type SyncOperationResult struct { // Resources contains a list of sync result items for each individual resource in a sync operation @@ -985,6 +1329,8 @@ type SyncOperationResult struct { Sources ApplicationSources `json:"sources,omitempty" protobuf:"bytes,4,opt,name=sources"` // Revisions holds the revision this sync operation was performed for respective indexed source in sources field Revisions []string `json:"revisions,omitempty" protobuf:"bytes,5,opt,name=revisions"` + // ManagedNamespaceMetadata contains the current sync state of managed namespace metadata + ManagedNamespaceMetadata *ManagedNamespaceMetadata `json:"managedNamespaceMetadata,omitempty" protobuf:"bytes,6,opt,name=managedNamespaceMetadata"` } // ResourceResult holds the operation result details of a specific resource @@ -1060,6 +1406,8 @@ type RevisionHistory struct { Sources ApplicationSources `json:"sources,omitempty" protobuf:"bytes,8,opt,name=sources"` // Revisions holds the revision of each source in sources field the sync was performed against Revisions []string `json:"revisions,omitempty" protobuf:"bytes,9,opt,name=revisions"` + // InitiatedBy contains information about who initiated the operations + InitiatedBy OperationInitiator `json:"initiatedBy,omitempty" protobuf:"bytes,10,opt,name=initiatedBy"` } // ApplicationWatchEvent contains information about application change. @@ -1129,7 +1477,7 @@ const ( ApplicationConditionOrphanedResourceWarning = "OrphanedResourceWarning" ) -// ApplicationCondition contains details about an application condition, which is usally an error or warning +// ApplicationCondition contains details about an application condition, which is usually an error or warning type ApplicationCondition struct { // Type is an application condition type Type ApplicationConditionType `json:"type" protobuf:"bytes,1,opt,name=type"` @@ -1147,6 +1495,8 @@ type ComparedTo struct { Destination ApplicationDestination `json:"destination" protobuf:"bytes,2,opt,name=destination"` // Sources is a reference to the application's multiple sources used for comparison Sources ApplicationSources `json:"sources,omitempty" protobuf:"bytes,3,opt,name=sources"` + // IgnoreDifferences is a reference to the application's ignored differences used for comparison + IgnoreDifferences IgnoreDifferences `json:"ignoreDifferences,omitempty" protobuf:"bytes,4,opt,name=ignoreDifferences"` } // SyncStatus contains information about the currently observed live and desired states of an application @@ -1511,6 +1861,9 @@ type AWSAuthConfig struct { // RoleARN contains optional role ARN. If set then AWS IAM Authenticator assume a role to perform cluster operations instead of the default AWS credential provider chain. RoleARN string `json:"roleARN,omitempty" protobuf:"bytes,2,opt,name=roleARN"` + + // Profile contains optional role ARN. If set then AWS IAM Authenticator uses the profile to perform cluster operations instead of the default AWS credential provider chain. + Profile string `json:"profile,omitempty" protobuf:"bytes,3,opt,name=profile"` } // ExecProviderConfig is config used to call an external command to perform cluster authentication @@ -1584,9 +1937,9 @@ type KnownTypeField struct { // OverrideIgnoreDiff contains configurations about how fields should be ignored during diffs between // the desired state and live state type OverrideIgnoreDiff struct { - //JSONPointers is a JSON path list following the format defined in RFC4627 (https://datatracker.ietf.org/doc/html/rfc6902#section-3) + // JSONPointers is a JSON path list following the format defined in RFC4627 (https://datatracker.ietf.org/doc/html/rfc6902#section-3) JSONPointers []string `json:"jsonPointers" protobuf:"bytes,1,rep,name=jSONPointers"` - //JQPathExpressions is a JQ path list that will be evaludated during the diff process + // JQPathExpressions is a JQ path list that will be evaludated during the diff process JQPathExpressions []string `json:"jqPathExpressions" protobuf:"bytes,2,opt,name=jqPathExpressions"` // ManagedFieldsManagers is a list of trusted managers. Fields mutated by those managers will take precedence over the // desired state defined in the SCM and won't be displayed in diffs @@ -1594,21 +1947,23 @@ type OverrideIgnoreDiff struct { } type rawResourceOverride struct { - HealthLua string `json:"health.lua,omitempty"` - UseOpenLibs bool `json:"health.lua.useOpenLibs,omitempty"` - Actions string `json:"actions,omitempty"` - IgnoreDifferences string `json:"ignoreDifferences,omitempty"` - KnownTypeFields []KnownTypeField `json:"knownTypeFields,omitempty"` + HealthLua string `json:"health.lua,omitempty"` + UseOpenLibs bool `json:"health.lua.useOpenLibs,omitempty"` + Actions string `json:"actions,omitempty"` + IgnoreDifferences string `json:"ignoreDifferences,omitempty"` + IgnoreResourceUpdates string `json:"ignoreResourceUpdates,omitempty"` + KnownTypeFields []KnownTypeField `json:"knownTypeFields,omitempty"` } // ResourceOverride holds configuration to customize resource diffing and health assessment // TODO: describe the members of this type type ResourceOverride struct { - HealthLua string `protobuf:"bytes,1,opt,name=healthLua"` - UseOpenLibs bool `protobuf:"bytes,5,opt,name=useOpenLibs"` - Actions string `protobuf:"bytes,3,opt,name=actions"` - IgnoreDifferences OverrideIgnoreDiff `protobuf:"bytes,2,opt,name=ignoreDifferences"` - KnownTypeFields []KnownTypeField `protobuf:"bytes,4,opt,name=knownTypeFields"` + HealthLua string `protobuf:"bytes,1,opt,name=healthLua"` + UseOpenLibs bool `protobuf:"bytes,5,opt,name=useOpenLibs"` + Actions string `protobuf:"bytes,3,opt,name=actions"` + IgnoreDifferences OverrideIgnoreDiff `protobuf:"bytes,2,opt,name=ignoreDifferences"` + IgnoreResourceUpdates OverrideIgnoreDiff `protobuf:"bytes,6,opt,name=ignoreResourceUpdates"` + KnownTypeFields []KnownTypeField `protobuf:"bytes,4,opt,name=knownTypeFields"` } // TODO: describe this method @@ -1621,7 +1976,15 @@ func (s *ResourceOverride) UnmarshalJSON(data []byte) error { s.HealthLua = raw.HealthLua s.UseOpenLibs = raw.UseOpenLibs s.Actions = raw.Actions - return yaml.Unmarshal([]byte(raw.IgnoreDifferences), &s.IgnoreDifferences) + err := yaml.Unmarshal([]byte(raw.IgnoreDifferences), &s.IgnoreDifferences) + if err != nil { + return err + } + err = yaml.Unmarshal([]byte(raw.IgnoreResourceUpdates), &s.IgnoreResourceUpdates) + if err != nil { + return err + } + return nil } // TODO: describe this method @@ -1630,7 +1993,11 @@ func (s ResourceOverride) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - raw := &rawResourceOverride{s.HealthLua, s.UseOpenLibs, s.Actions, string(ignoreDifferencesData), s.KnownTypeFields} + ignoreResourceUpdatesData, err := yaml.Marshal(s.IgnoreResourceUpdates) + if err != nil { + return nil, err + } + raw := &rawResourceOverride{s.HealthLua, s.UseOpenLibs, s.Actions, string(ignoreDifferencesData), string(ignoreResourceUpdatesData), s.KnownTypeFields} return json.Marshal(raw) } @@ -1661,9 +2028,11 @@ type ResourceActionDefinition struct { // TODO: describe this type // TODO: describe members of this type type ResourceAction struct { - Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"` - Params []ResourceActionParam `json:"params,omitempty" protobuf:"bytes,2,rep,name=params"` - Disabled bool `json:"disabled,omitempty" protobuf:"varint,3,opt,name=disabled"` + Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"` + Params []ResourceActionParam `json:"params,omitempty" protobuf:"bytes,2,rep,name=params"` + Disabled bool `json:"disabled,omitempty" protobuf:"varint,3,opt,name=disabled"` + IconClass string `json:"iconClass,omitempty" protobuf:"bytes,4,opt,name=iconClass"` + DisplayName string `json:"displayName,omitempty" protobuf:"bytes,5,opt,name=displayName"` } // TODO: describe this type @@ -1860,7 +2229,7 @@ type SyncWindow struct { Clusters []string `json:"clusters,omitempty" protobuf:"bytes,6,opt,name=clusters"` // ManualSync enables manual syncs when they would otherwise be blocked ManualSync bool `json:"manualSync,omitempty" protobuf:"bytes,7,opt,name=manualSync"` - //TimeZone of the sync that will be applied to the schedule + // TimeZone of the sync that will be applied to the schedule TimeZone string `json:"timeZone,omitempty" protobuf:"bytes,8,opt,name=timeZone"` } @@ -2056,6 +2425,10 @@ func (w *SyncWindows) CanSync(isManual bool) bool { } } + if active.hasAllow() { + return true + } + inactiveAllows := w.InactiveAllows() if inactiveAllows.HasWindows() { if isManual && inactiveAllows.manualEnabled() { @@ -2288,6 +2661,18 @@ func (app *Application) IsRefreshRequested() (RefreshType, bool) { return refreshType, true } +func (app *Application) HasPostDeleteFinalizer(stage ...string) bool { + return getFinalizerIndex(app.ObjectMeta, strings.Join(append([]string{PostDeleteFinalizerName}, stage...), "/")) > -1 +} + +func (app *Application) SetPostDeleteFinalizer(stage ...string) { + setFinalizer(&app.ObjectMeta, strings.Join(append([]string{PostDeleteFinalizerName}, stage...), "/"), true) +} + +func (app *Application) UnSetPostDeleteFinalizer(stage ...string) { + setFinalizer(&app.ObjectMeta, strings.Join(append([]string{PostDeleteFinalizerName}, stage...), "/"), false) +} + // SetCascadedDeletion will enable cascaded deletion by setting the propagation policy finalizer func (app *Application) SetCascadedDeletion(finalizer string) { setFinalizer(&app.ObjectMeta, finalizer, true) @@ -2330,6 +2715,13 @@ func (app *Application) GetPropagationPolicy() string { return "" } +// HasChangedManagedNamespaceMetadata checks whether app.Spec.SyncPolicy.ManagedNamespaceMetadata differs from the +// managed namespace metadata which has been stored app.Status.OperationState.SyncResult. If they differ a refresh should +// be triggered. +func (app *Application) HasChangedManagedNamespaceMetadata() bool { + return app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.ManagedNamespaceMetadata != nil && app.Status.OperationState != nil && app.Status.OperationState.SyncResult != nil && !reflect.DeepEqual(app.Spec.SyncPolicy.ManagedNamespaceMetadata, app.Status.OperationState.SyncResult.ManagedNamespaceMetadata) +} + // IsFinalizerPresent checks if the app has a given finalizer func (app *Application) IsFinalizerPresent(finalizer string) bool { return getFinalizerIndex(app.ObjectMeta, finalizer) > -1 @@ -2401,8 +2793,23 @@ func (condition *ApplicationCondition) IsError() bool { } // Equals compares two instances of ApplicationSource and return true if instances are equal. -func (source *ApplicationSource) Equals(other ApplicationSource) bool { - return reflect.DeepEqual(*source, other) +func (source *ApplicationSource) Equals(other *ApplicationSource) bool { + if source == nil && other == nil { + return true + } + if source == nil || other == nil { + return false + } + if !source.Plugin.Equals(other.Plugin) { + return false + } + // reflect.DeepEqual works fine for the other fields. Since the plugin fields are equal, set them to null so they're + // not considered in the DeepEqual comparison. + sourceCopy := source.DeepCopy() + otherCopy := other.DeepCopy() + sourceCopy.Plugin = nil + otherCopy.Plugin = nil + return reflect.DeepEqual(sourceCopy, otherCopy) } // ExplicitType returns the type (e.g. Helm, Kustomize, etc) of the application. If either none or multiple types are defined, returns an error. @@ -2540,6 +2947,12 @@ func SetK8SConfigDefaults(config *rest.Config) error { config.Timeout = K8sServerSideTimeout config.Transport = tr + maxRetries := env.ParseInt64FromEnv(utilhttp.EnvRetryMax, 0, 1, math.MaxInt64) + if maxRetries > 0 { + backoffDurationMS := env.ParseInt64FromEnv(utilhttp.EnvRetryBaseBackoff, 100, 1, math.MaxInt64) + backoffDuration := time.Duration(backoffDurationMS) * time.Millisecond + config.WrapTransport = utilhttp.WithRetry(maxRetries, backoffDuration) + } return nil } @@ -2547,12 +2960,17 @@ func SetK8SConfigDefaults(config *rest.Config) error { func (c *Cluster) RawRestConfig() *rest.Config { var config *rest.Config var err error - if c.Server == KubernetesInternalAPIServerAddr && os.Getenv(EnvVarFakeInClusterConfig) == "true" { + if c.Server == KubernetesInternalAPIServerAddr && env.ParseBoolFromEnv(EnvVarFakeInClusterConfig, false) { conf, exists := os.LookupEnv("KUBECONFIG") if exists { config, err = clientcmd.BuildConfigFromFlags("", conf) } else { - config, err = clientcmd.BuildConfigFromFlags("", filepath.Join(os.Getenv("HOME"), ".kube", "config")) + var homeDir string + homeDir, err = os.UserHomeDir() + if err != nil { + homeDir = "" + } + config, err = clientcmd.BuildConfigFromFlags("", filepath.Join(homeDir, ".kube", "config")) } } else if c.Server == KubernetesInternalAPIServerAddr && c.Config.Username == "" && c.Config.Password == "" && c.Config.BearerToken == "" { config, err = rest.InClusterConfig() @@ -2577,6 +2995,9 @@ func (c *Cluster) RawRestConfig() *rest.Config { if c.Config.AWSAuthConfig.RoleARN != "" { args = append(args, "--role-arn", c.Config.AWSAuthConfig.RoleARN) } + if c.Config.AWSAuthConfig.Profile != "" { + args = append(args, "--profile", c.Config.AWSAuthConfig.Profile) + } config = &rest.Config{ Host: c.Server, TLSClientConfig: tlsClientConfig, @@ -2716,5 +3137,5 @@ func (a *Application) QualifiedName() string { // RBACName returns the full qualified RBAC resource name for the application // in a backwards-compatible way. func (a *Application) RBACName(defaultNS string) string { - return security.AppRBACName(defaultNS, a.Spec.GetProject(), a.Namespace, a.Name) + return security.RBACName(defaultNS, a.Spec.GetProject(), a.Namespace, a.Name) } diff --git a/pkg/apis/application/v1alpha1/types_test.go b/pkg/apis/application/v1alpha1/types_test.go index 17f6982581db3..2374f5fb503e6 100644 --- a/pkg/apis/application/v1alpha1/types_test.go +++ b/pkg/apis/application/v1alpha1/types_test.go @@ -3,7 +3,7 @@ package v1alpha1 import ( "encoding/json" "errors" - fmt "fmt" + "fmt" "os" "path" "reflect" @@ -11,14 +11,16 @@ import ( "testing" "time" - argocdcommon "github.com/argoproj/argo-cd/v2/common" "github.com/stretchr/testify/require" "k8s.io/utils/pointer" + argocdcommon "github.com/argoproj/argo-cd/v2/common" + "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/intstr" ) func TestAppProject_IsSourcePermitted(t *testing.T) { @@ -368,7 +370,7 @@ func TestAppProject_IsDestinationPermitted_PermitOnlyProjectScopedClusters(t *te projDest: []ApplicationDestination{{ Server: "https://my-cluster.123.com", Namespace: "default", }}, - appDest: ApplicationDestination{Server: "https://some-other-cluster.com", Namespace: "default"}, + appDest: ApplicationDestination{Server: "https://some-other-cluster.example.com", Namespace: "default"}, clusters: []*Cluster{{ Server: "https://my-cluster.123.com", }}, @@ -644,7 +646,7 @@ func TestAppProject_ValidateDestinations(t *testing.T) { err = p.ValidateProject() assert.NoError(t, err) - //no duplicates allowed + // no duplicates allowed p.Spec.Destinations = []ApplicationDestination{validDestination, validDestination} err = p.ValidateProject() assert.Error(t, err) @@ -863,9 +865,9 @@ func TestAppSourceEquality(t *testing.T) { }, } right := left.DeepCopy() - assert.True(t, left.Equals(*right)) + assert.True(t, left.Equals(right)) right.Directory.Recurse = false - assert.False(t, left.Equals(*right)) + assert.False(t, left.Equals(right)) } func TestAppDestinationEquality(t *testing.T) { @@ -1315,6 +1317,96 @@ func TestNewHelmParameter(t *testing.T) { }) } +func TestNewKustomizeReplica(t *testing.T) { + t.Run("Valid", func(t *testing.T) { + r, err := NewKustomizeReplica("my-deployment=2") + assert.NoError(t, err) + assert.Equal(t, &KustomizeReplica{Name: "my-deployment", Count: intstr.Parse("2")}, r) + }) + t.Run("InvalidFormat", func(t *testing.T) { + _, err := NewKustomizeReplica("garbage") + assert.EqualError(t, err, "expected parameter of the form: name=count. Received: garbage") + }) + t.Run("InvalidCount", func(t *testing.T) { + _, err := NewKustomizeReplica("my-deployment=garbage") + assert.EqualError(t, err, "expected integer value for count. Received: garbage") + }) +} + +func TestKustomizeReplica_GetIntCount(t *testing.T) { + t.Run("String which can be converted to integer", func(t *testing.T) { + kr := KustomizeReplica{ + Name: "test", + Count: intstr.FromString("2"), + } + count, err := kr.GetIntCount() + assert.NoError(t, err) + assert.Equal(t, 2, count) + }) + t.Run("String which cannot be converted to integer", func(t *testing.T) { + kr := KustomizeReplica{ + Name: "test", + Count: intstr.FromString("garbage"), + } + count, err := kr.GetIntCount() + assert.EqualError(t, err, "expected integer value for count. Received: garbage") + assert.Equal(t, 0, count) + }) + t.Run("Integer", func(t *testing.T) { + kr := KustomizeReplica{ + Name: "test", + Count: intstr.FromInt(2), + } + count, err := kr.GetIntCount() + assert.NoError(t, err) + assert.Equal(t, 2, count) + }) +} + +func TestApplicationSourceKustomize_MergeReplica(t *testing.T) { + r1 := KustomizeReplica{ + Name: "my-deployment", + Count: intstr.FromInt(2), + } + r2 := KustomizeReplica{ + Name: "my-deployment", + Count: intstr.FromInt(4), + } + t.Run("Add", func(t *testing.T) { + k := ApplicationSourceKustomize{Replicas: KustomizeReplicas{}} + k.MergeReplica(r1) + assert.Equal(t, KustomizeReplicas{r1}, k.Replicas) + }) + t.Run("Replace", func(t *testing.T) { + k := ApplicationSourceKustomize{Replicas: KustomizeReplicas{r1}} + k.MergeReplica(r2) + assert.Equal(t, 1, len(k.Replicas)) + assert.Equal(t, k.Replicas[0].Name, r2.Name) + assert.Equal(t, k.Replicas[0].Count, r2.Count) + }) +} +func TestApplicationSourceKustomize_FindByName(t *testing.T) { + r1 := KustomizeReplica{ + Name: "my-deployment", + Count: intstr.FromInt(2), + } + r2 := KustomizeReplica{ + Name: "my-statefulset", + Count: intstr.FromInt(4), + } + Replicas := KustomizeReplicas{r1, r2} + t.Run("Found", func(t *testing.T) { + i1 := Replicas.FindByName("my-deployment") + i2 := Replicas.FindByName("my-statefulset") + assert.Equal(t, 0, i1) + assert.Equal(t, 1, i2) + }) + t.Run("Not Found", func(t *testing.T) { + i := Replicas.FindByName("not-found") + assert.Equal(t, -1, i) + }) +} + func TestApplicationSourceHelm_IsZero(t *testing.T) { tests := []struct { name string @@ -1346,6 +1438,7 @@ func TestApplicationSourceKustomize_IsZero(t *testing.T) { {"NamePrefix", &ApplicationSourceKustomize{NamePrefix: "foo"}, false}, {"NameSuffix", &ApplicationSourceKustomize{NameSuffix: "foo"}, false}, {"Images", &ApplicationSourceKustomize{Images: []KustomizeImage{""}}, false}, + {"Replicas", &ApplicationSourceKustomize{Replicas: []KustomizeReplica{{Name: "", Count: intstr.FromInt(0)}}}, false}, {"CommonLabels", &ApplicationSourceKustomize{CommonLabels: map[string]string{"": ""}}, false}, {"CommonAnnotations", &ApplicationSourceKustomize{CommonAnnotations: map[string]string{"": ""}}, false}, } @@ -2133,6 +2226,20 @@ func TestSyncWindows_CanSync(t *testing.T) { // then assert.False(t, canSync) }) + t.Run("will allow auto sync with active-allow and inactive-allow", func(t *testing.T) { + // given + t.Parallel() + proj := newProjectBuilder(). + withActiveAllowWindow(false). + withInactiveAllowWindow(false). + build() + + // when + canSync := proj.Spec.SyncWindows.CanSync(false) + + // then + assert.True(t, canSync) + }) t.Run("will deny manual sync with active-deny", func(t *testing.T) { // given t.Parallel() @@ -2859,7 +2966,7 @@ func TestRetryStrategy_NextRetryAtCustomBackoff(t *testing.T) { retry := RetryStrategy{ Backoff: &Backoff{ Duration: "2s", - Factor: pointer.Int64Ptr(3), + Factor: pointer.Int64(3), MaxDuration: "1m", }, } @@ -2880,11 +2987,31 @@ func TestRetryStrategy_NextRetryAtCustomBackoff(t *testing.T) { } func TestSourceAllowsConcurrentProcessing_KustomizeParams(t *testing.T) { - src := ApplicationSource{Path: ".", Kustomize: &ApplicationSourceKustomize{ - NameSuffix: "test", - }} + t.Run("Has NameSuffix", func(t *testing.T) { + src := ApplicationSource{Path: ".", Kustomize: &ApplicationSourceKustomize{ + NameSuffix: "test", + }} - assert.False(t, src.AllowsConcurrentProcessing()) + assert.False(t, src.AllowsConcurrentProcessing()) + }) + + t.Run("Has CommonAnnotations", func(t *testing.T) { + src := ApplicationSource{Path: ".", Kustomize: &ApplicationSourceKustomize{ + CommonAnnotations: map[string]string{"foo": "bar"}, + }} + + assert.False(t, src.AllowsConcurrentProcessing()) + }) + + t.Run("Has Patches", func(t *testing.T) { + src := ApplicationSource{Path: ".", Kustomize: &ApplicationSourceKustomize{ + Patches: KustomizePatches{{ + Path: "test", + }}, + }} + + assert.False(t, src.AllowsConcurrentProcessing()) + }) } func TestUnSetCascadedDeletion(t *testing.T) { @@ -2948,10 +3075,10 @@ func TestOrphanedResourcesMonitorSettings_IsWarn(t *testing.T) { settings := OrphanedResourcesMonitorSettings{} assert.False(t, settings.IsWarn()) - settings.Warn = pointer.BoolPtr(false) + settings.Warn = pointer.Bool(false) assert.False(t, settings.IsWarn()) - settings.Warn = pointer.BoolPtr(true) + settings.Warn = pointer.Bool(true) assert.True(t, settings.IsWarn()) } @@ -3039,7 +3166,7 @@ func TestGetCAPath(t *testing.T) { if err != nil { panic(err) } - os.Setenv(argocdcommon.EnvVarTLSDataPath, temppath) + t.Setenv(argocdcommon.EnvVarTLSDataPath, temppath) validcert := []string{ "https://foo.example.com", "oci://foo.example.com", @@ -3261,8 +3388,8 @@ func TestApplicationSourcePluginParameters_Environ_string(t *testing.T) { func TestApplicationSourcePluginParameters_Environ_array(t *testing.T) { params := ApplicationSourcePluginParameters{ { - Name: "dependencies", - Array: []string{"redis", "minio"}, + Name: "dependencies", + OptionalArray: &OptionalArray{Array: []string{"redis", "minio"}}, }, } environ, err := params.Environ() @@ -3279,9 +3406,11 @@ func TestApplicationSourcePluginParameters_Environ_map(t *testing.T) { params := ApplicationSourcePluginParameters{ { Name: "helm-parameters", - Map: map[string]string{ - "image.repo": "quay.io/argoproj/argo-cd", - "image.tag": "v2.4.0", + OptionalMap: &OptionalMap{ + Map: map[string]string{ + "image.repo": "quay.io/argoproj/argo-cd", + "image.tag": "v2.4.0", + }, }, }, } @@ -3302,10 +3431,14 @@ func TestApplicationSourcePluginParameters_Environ_all(t *testing.T) { { Name: "some-name", String_: pointer.String("1.2.3"), - Array: []string{"redis", "minio"}, - Map: map[string]string{ - "image.repo": "quay.io/argoproj/argo-cd", - "image.tag": "v2.4.0", + OptionalArray: &OptionalArray{ + Array: []string{"redis", "minio"}, + }, + OptionalMap: &OptionalMap{ + Map: map[string]string{ + "image.repo": "quay.io/argoproj/argo-cd", + "image.tag": "v2.4.0", + }, }, }, } @@ -3401,3 +3534,89 @@ func TestGetSources(t *testing.T) { }) } } + +func TestOptionalArrayEquality(t *testing.T) { + // Demonstrate that the JSON unmarshalling of an empty array parameter is an OptionalArray with the array field set + // to an empty array. + presentButEmpty := `{"array":[]}` + param := ApplicationSourcePluginParameter{} + err := json.Unmarshal([]byte(presentButEmpty), ¶m) + require.NoError(t, err) + jsonPresentButEmpty := param.OptionalArray + require.Equal(t, &OptionalArray{Array: []string{}}, jsonPresentButEmpty) + + // We won't simulate the protobuf unmarshalling of an empty array parameter. By experimentation, this is how it's + // unmarshalled. + protobufPresentButEmpty := &OptionalArray{Array: nil} + + tests := []struct { + name string + a *OptionalArray + b *OptionalArray + expected bool + }{ + {"nil and nil", nil, nil, true}, + {"nil and empty", nil, jsonPresentButEmpty, false}, + {"nil and empty-containing-nil", nil, protobufPresentButEmpty, false}, + {"empty-containing-empty and nil", jsonPresentButEmpty, nil, false}, + {"empty-containing-nil and nil", protobufPresentButEmpty, nil, false}, + {"empty-containing-empty and empty-containing-empty", jsonPresentButEmpty, jsonPresentButEmpty, true}, + {"empty-containing-empty and empty-containing-nil", jsonPresentButEmpty, protobufPresentButEmpty, true}, + {"empty-containing-nil and empty-containing-empty", protobufPresentButEmpty, jsonPresentButEmpty, true}, + {"empty-containing-nil and empty-containing-nil", protobufPresentButEmpty, protobufPresentButEmpty, true}, + {"empty-containing-empty and non-empty", jsonPresentButEmpty, &OptionalArray{Array: []string{"a"}}, false}, + {"non-empty and empty-containing-nil", &OptionalArray{Array: []string{"a"}}, jsonPresentButEmpty, false}, + {"non-empty and non-empty", &OptionalArray{Array: []string{"a"}}, &OptionalArray{Array: []string{"a"}}, true}, + {"non-empty and non-empty different", &OptionalArray{Array: []string{"a"}}, &OptionalArray{Array: []string{"b"}}, false}, + } + for _, testCase := range tests { + testCopy := testCase + t.Run(testCopy.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, testCopy.expected, testCopy.a.Equals(testCopy.b)) + }) + } +} + +func TestOptionalMapEquality(t *testing.T) { + // Demonstrate that the JSON unmarshalling of an empty map parameter is an OptionalMap with the map field set + // to an empty map. + presentButEmpty := `{"map":{}}` + param := ApplicationSourcePluginParameter{} + err := json.Unmarshal([]byte(presentButEmpty), ¶m) + require.NoError(t, err) + jsonPresentButEmpty := param.OptionalMap + require.Equal(t, &OptionalMap{Map: map[string]string{}}, jsonPresentButEmpty) + + // We won't simulate the protobuf unmarshalling of an empty map parameter. By experimentation, this is how it's + // unmarshalled. + protobufPresentButEmpty := &OptionalMap{Map: nil} + + tests := []struct { + name string + a *OptionalMap + b *OptionalMap + expected bool + }{ + {"nil and nil", nil, nil, true}, + {"nil and empty-containing-empty", nil, jsonPresentButEmpty, false}, + {"nil and empty-containing-nil", nil, protobufPresentButEmpty, false}, + {"empty-containing-empty and nil", jsonPresentButEmpty, nil, false}, + {"empty-containing-nil and nil", protobufPresentButEmpty, nil, false}, + {"empty-containing-empty and empty-containing-empty", jsonPresentButEmpty, jsonPresentButEmpty, true}, + {"empty-containing-empty and empty-containing-nil", jsonPresentButEmpty, protobufPresentButEmpty, true}, + {"empty-containing-empty and non-empty", jsonPresentButEmpty, &OptionalMap{Map: map[string]string{"a": "b"}}, false}, + {"empty-containing-nil and empty-containing-empty", protobufPresentButEmpty, jsonPresentButEmpty, true}, + {"empty-containing-nil and empty-containing-nil", protobufPresentButEmpty, protobufPresentButEmpty, true}, + {"non-empty and empty-containing-empty", &OptionalMap{Map: map[string]string{"a": "b"}}, jsonPresentButEmpty, false}, + {"non-empty and non-empty", &OptionalMap{Map: map[string]string{"a": "b"}}, &OptionalMap{Map: map[string]string{"a": "b"}}, true}, + {"non-empty and non-empty different", &OptionalMap{Map: map[string]string{"a": "b"}}, &OptionalMap{Map: map[string]string{"a": "c"}}, false}, + } + for _, testCase := range tests { + testCopy := testCase + t.Run(testCopy.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, testCopy.expected, testCopy.a.Equals(testCopy.b)) + }) + } +} diff --git a/pkg/apis/application/v1alpha1/values.go b/pkg/apis/application/v1alpha1/values.go new file mode 100644 index 0000000000000..942e2a651cf71 --- /dev/null +++ b/pkg/apis/application/v1alpha1/values.go @@ -0,0 +1,61 @@ +package v1alpha1 + +import ( + "encoding/json" + "fmt" + reflect "reflect" + "strings" + + runtime "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/yaml" +) + +// Set the ValuesObject property to the json representation of the yaml contained in value +// Remove Values property if present +func (h *ApplicationSourceHelm) SetValuesString(value string) error { + if value == "" { + h.ValuesObject = nil + h.Values = "" + } else { + data, err := yaml.YAMLToJSON([]byte(value)) + if err != nil { + return fmt.Errorf("failed converting yaml to json: %v", err) + } + var v interface{} + if err := json.Unmarshal(data, &v); err != nil { + return fmt.Errorf("failed to unmarshal json: %v", err) + } + switch v.(type) { + case string: + case map[string]interface{}: + default: + return fmt.Errorf("invalid type %q", reflect.TypeOf(v)) + } + h.ValuesObject = &runtime.RawExtension{Raw: data} + h.Values = "" + } + return nil +} + +func (h *ApplicationSourceHelm) ValuesYAML() []byte { + if h.ValuesObject == nil || h.ValuesObject.Raw == nil { + return []byte(h.Values) + } + b, err := yaml.JSONToYAML(h.ValuesObject.Raw) + if err != nil { + // This should be impossible, because rawValue isn't set directly. + return []byte{} + } + return b +} + +func (h *ApplicationSourceHelm) ValuesIsEmpty() bool { + return len(h.ValuesYAML()) == 0 +} + +func (h *ApplicationSourceHelm) ValuesString() string { + if h.ValuesObject == nil || h.ValuesObject.Raw == nil { + return h.Values + } + return strings.TrimSuffix(string(h.ValuesYAML()), "\n") +} diff --git a/pkg/apis/application/v1alpha1/values_test.go b/pkg/apis/application/v1alpha1/values_test.go new file mode 100644 index 0000000000000..f21f17168a2e8 --- /dev/null +++ b/pkg/apis/application/v1alpha1/values_test.go @@ -0,0 +1,81 @@ +package v1alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValues_SetString(t *testing.T) { + testCases := []struct { + name string + inputValue string + expectError bool + expectValue string + }{ + { + name: "an empty string should not throw an error", + inputValue: `""`, + expectValue: "\"\"", + }, + { + name: "a string with contents should not throw an error", + inputValue: `"hello"`, + expectValue: "hello", + }, + { + name: "an array should throw an error", + inputValue: "[]", + expectError: true, + }, + { + name: "a number should throw an error", + inputValue: "42", + expectError: true, + }, + { + name: "a boolean should throw an error", + inputValue: "false", + expectError: true, + }, + { + name: "null should throw an error", + inputValue: "null", + expectError: true, + }, + { + name: "an empty object should not throw an error", + inputValue: "{}", + expectValue: "{}", + }, + { + name: "an object with contents should not throw an error", + inputValue: `{"some": "inputValue"}`, + expectValue: "some: inputValue", + }, + { + name: "a complex object should not throw an error", + inputValue: `{"a": {"nested": "object"}, "an": ["array"], "bool": true, "number": 1, "some": "string"}`, + expectValue: "a:\n nested: object\nan:\n- array\nbool: true\nnumber: 1\nsome: string", + }, + } + + for _, testCase := range testCases { + var err error + t.Run(testCase.name, func(t *testing.T) { + source := &ApplicationSourceHelm{} + err = source.SetValuesString(testCase.inputValue) + + if !testCase.expectError { + assert.Equal(t, testCase.expectValue, source.ValuesString()) + data, err := source.ValuesObject.MarshalJSON() + assert.NoError(t, err) + err = source.ValuesObject.UnmarshalJSON(data) + assert.NoError(t, err) + assert.Equal(t, testCase.expectValue, source.ValuesString()) + } else { + assert.Error(t, err) + } + }) + } +} diff --git a/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go index 15cc07f3c19f3..8c851067a6be3 100644 --- a/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go @@ -315,6 +315,32 @@ func (in *ApplicationMatchExpression) DeepCopy() *ApplicationMatchExpression { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationPreservedFields) DeepCopyInto(out *ApplicationPreservedFields) { + *out = *in + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationPreservedFields. +func (in *ApplicationPreservedFields) DeepCopy() *ApplicationPreservedFields { + if in == nil { + return nil + } + out := new(ApplicationPreservedFields) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ApplicationSet) DeepCopyInto(out *ApplicationSet) { *out = *in @@ -431,6 +457,11 @@ func (in *ApplicationSetGenerator) DeepCopyInto(out *ApplicationSetGenerator) { *out = new(v1.LabelSelector) (*in).DeepCopyInto(*out) } + if in.Plugin != nil { + in, out := &in.Plugin, &out.Plugin + *out = new(PluginGenerator) + (*in).DeepCopyInto(*out) + } return } @@ -444,6 +475,28 @@ func (in *ApplicationSetGenerator) DeepCopy() *ApplicationSetGenerator { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in ApplicationSetIgnoreDifferences) DeepCopyInto(out *ApplicationSetIgnoreDifferences) { + { + in := &in + *out = make(ApplicationSetIgnoreDifferences, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSetIgnoreDifferences. +func (in ApplicationSetIgnoreDifferences) DeepCopy() ApplicationSetIgnoreDifferences { + if in == nil { + return nil + } + out := new(ApplicationSetIgnoreDifferences) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ApplicationSetList) DeepCopyInto(out *ApplicationSetList) { *out = *in @@ -525,6 +578,11 @@ func (in *ApplicationSetNestedGenerator) DeepCopyInto(out *ApplicationSetNestedG *out = new(v1.LabelSelector) (*in).DeepCopyInto(*out) } + if in.Plugin != nil { + in, out := &in.Plugin, &out.Plugin + *out = new(PluginGenerator) + (*in).DeepCopyInto(*out) + } return } @@ -560,6 +618,32 @@ func (in ApplicationSetNestedGenerators) DeepCopy() ApplicationSetNestedGenerato return *out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationSetResourceIgnoreDifferences) DeepCopyInto(out *ApplicationSetResourceIgnoreDifferences) { + *out = *in + if in.JSONPointers != nil { + in, out := &in.JSONPointers, &out.JSONPointers + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.JQPathExpressions != nil { + in, out := &in.JQPathExpressions, &out.JQPathExpressions + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSetResourceIgnoreDifferences. +func (in *ApplicationSetResourceIgnoreDifferences) DeepCopy() *ApplicationSetResourceIgnoreDifferences { + if in == nil { + return nil + } + out := new(ApplicationSetResourceIgnoreDifferences) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ApplicationSetRolloutStep) DeepCopyInto(out *ApplicationSetRolloutStep) { *out = *in @@ -625,13 +709,35 @@ func (in *ApplicationSetSpec) DeepCopyInto(out *ApplicationSetSpec) { if in.SyncPolicy != nil { in, out := &in.SyncPolicy, &out.SyncPolicy *out = new(ApplicationSetSyncPolicy) - **out = **in + (*in).DeepCopyInto(*out) } if in.Strategy != nil { in, out := &in.Strategy, &out.Strategy *out = new(ApplicationSetStrategy) (*in).DeepCopyInto(*out) } + if in.PreservedFields != nil { + in, out := &in.PreservedFields, &out.PreservedFields + *out = new(ApplicationPreservedFields) + (*in).DeepCopyInto(*out) + } + if in.GoTemplateOptions != nil { + in, out := &in.GoTemplateOptions, &out.GoTemplateOptions + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.IgnoreApplicationDifferences != nil { + in, out := &in.IgnoreApplicationDifferences, &out.IgnoreApplicationDifferences + *out = make(ApplicationSetIgnoreDifferences, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.TemplatePatch != nil { + in, out := &in.TemplatePatch, &out.TemplatePatch + *out = new(string) + **out = **in + } return } @@ -699,6 +805,11 @@ func (in *ApplicationSetStrategy) DeepCopy() *ApplicationSetStrategy { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ApplicationSetSyncPolicy) DeepCopyInto(out *ApplicationSetSyncPolicy) { *out = *in + if in.ApplicationsSync != nil { + in, out := &in.ApplicationsSync, &out.ApplicationsSync + *out = new(ApplicationsSyncPolicy) + **out = **in + } return } @@ -798,6 +909,16 @@ func (in *ApplicationSetTerminalGenerator) DeepCopyInto(out *ApplicationSetTermi *out = new(PullRequestGenerator) (*in).DeepCopyInto(*out) } + if in.Plugin != nil { + in, out := &in.Plugin, &out.Plugin + *out = new(PluginGenerator) + (*in).DeepCopyInto(*out) + } + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } return } @@ -904,6 +1025,11 @@ func (in *ApplicationSourceHelm) DeepCopyInto(out *ApplicationSourceHelm) { *out = make([]HelmFileParameter, len(*in)) copy(*out, *in) } + if in.ValuesObject != nil { + in, out := &in.ValuesObject, &out.ValuesObject + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) + } return } @@ -970,6 +1096,23 @@ func (in *ApplicationSourceKustomize) DeepCopyInto(out *ApplicationSourceKustomi (*out)[key] = val } } + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = make(KustomizeReplicas, len(*in)) + copy(*out, *in) + } + if in.Patches != nil { + in, out := &in.Patches, &out.Patches + *out = make(KustomizePatches, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Components != nil { + in, out := &in.Components, &out.Components + *out = make([]string, len(*in)) + copy(*out, *in) + } return } @@ -1025,17 +1168,15 @@ func (in *ApplicationSourcePluginParameter) DeepCopyInto(out *ApplicationSourceP *out = new(string) **out = **in } - if in.Map != nil { - in, out := &in.Map, &out.Map - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } + if in.OptionalMap != nil { + in, out := &in.OptionalMap, &out.OptionalMap + *out = new(OptionalMap) + (*in).DeepCopyInto(*out) } - if in.Array != nil { - in, out := &in.Array, &out.Array - *out = make([]string, len(*in)) - copy(*out, *in) + if in.OptionalArray != nil { + in, out := &in.OptionalArray, &out.OptionalArray + *out = new(OptionalArray) + (*in).DeepCopyInto(*out) } return } @@ -1110,7 +1251,7 @@ func (in *ApplicationSpec) DeepCopyInto(out *ApplicationSpec) { } if in.IgnoreDifferences != nil { in, out := &in.IgnoreDifferences, &out.IgnoreDifferences - *out = make([]ResourceIgnoreDifferences, len(*in)) + *out = make(IgnoreDifferences, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1325,6 +1466,48 @@ func (in *BasicAuthBitbucketServer) DeepCopy() *BasicAuthBitbucketServer { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BearerTokenBitbucketCloud) DeepCopyInto(out *BearerTokenBitbucketCloud) { + *out = *in + if in.TokenRef != nil { + in, out := &in.TokenRef, &out.TokenRef + *out = new(SecretRef) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BearerTokenBitbucketCloud. +func (in *BearerTokenBitbucketCloud) DeepCopy() *BearerTokenBitbucketCloud { + if in == nil { + return nil + } + out := new(BearerTokenBitbucketCloud) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ChartDetails) DeepCopyInto(out *ChartDetails) { + *out = *in + if in.Maintainers != nil { + in, out := &in.Maintainers, &out.Maintainers + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChartDetails. +func (in *ChartDetails) DeepCopy() *ChartDetails { + if in == nil { + return nil + } + out := new(ChartDetails) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Cluster) DeepCopyInto(out *Cluster) { *out = *in @@ -1529,6 +1712,13 @@ func (in *ComparedTo) DeepCopyInto(out *ComparedTo) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.IgnoreDifferences != nil { + in, out := &in.IgnoreDifferences, &out.IgnoreDifferences + *out = make(IgnoreDifferences, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } @@ -1751,6 +1941,13 @@ func (in *GitGenerator) DeepCopyInto(out *GitGenerator) { **out = **in } in.Template.DeepCopyInto(&out.Template) + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } return } @@ -1909,6 +2106,28 @@ func (in *HostResourceInfo) DeepCopy() *HostResourceInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in IgnoreDifferences) DeepCopyInto(out *IgnoreDifferences) { + { + in := &in + *out = make(IgnoreDifferences, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IgnoreDifferences. +func (in IgnoreDifferences) DeepCopy() IgnoreDifferences { + if in == nil { + return nil + } + out := new(IgnoreDifferences) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Info) DeepCopyInto(out *Info) { *out = *in @@ -2010,6 +2229,22 @@ func (in *KnownTypeField) DeepCopy() *KnownTypeField { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KustomizeGvk) DeepCopyInto(out *KustomizeGvk) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizeGvk. +func (in *KustomizeGvk) DeepCopy() *KustomizeGvk { + if in == nil { + return nil + } + out := new(KustomizeGvk) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in KustomizeImages) DeepCopyInto(out *KustomizeImages) { { @@ -2046,6 +2281,127 @@ func (in *KustomizeOptions) DeepCopy() *KustomizeOptions { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KustomizePatch) DeepCopyInto(out *KustomizePatch) { + *out = *in + if in.Target != nil { + in, out := &in.Target, &out.Target + *out = new(KustomizeSelector) + **out = **in + } + if in.Options != nil { + in, out := &in.Options, &out.Options + *out = make(map[string]bool, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizePatch. +func (in *KustomizePatch) DeepCopy() *KustomizePatch { + if in == nil { + return nil + } + out := new(KustomizePatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in KustomizePatches) DeepCopyInto(out *KustomizePatches) { + { + in := &in + *out = make(KustomizePatches, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizePatches. +func (in KustomizePatches) DeepCopy() KustomizePatches { + if in == nil { + return nil + } + out := new(KustomizePatches) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KustomizeReplica) DeepCopyInto(out *KustomizeReplica) { + *out = *in + out.Count = in.Count + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizeReplica. +func (in *KustomizeReplica) DeepCopy() *KustomizeReplica { + if in == nil { + return nil + } + out := new(KustomizeReplica) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in KustomizeReplicas) DeepCopyInto(out *KustomizeReplicas) { + { + in := &in + *out = make(KustomizeReplicas, len(*in)) + copy(*out, *in) + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizeReplicas. +func (in KustomizeReplicas) DeepCopy() KustomizeReplicas { + if in == nil { + return nil + } + out := new(KustomizeReplicas) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KustomizeResId) DeepCopyInto(out *KustomizeResId) { + *out = *in + out.KustomizeGvk = in.KustomizeGvk + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizeResId. +func (in *KustomizeResId) DeepCopy() *KustomizeResId { + if in == nil { + return nil + } + out := new(KustomizeResId) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KustomizeSelector) DeepCopyInto(out *KustomizeSelector) { + *out = *in + out.KustomizeResId = in.KustomizeResId + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizeSelector. +func (in *KustomizeSelector) DeepCopy() *KustomizeSelector { + if in == nil { + return nil + } + out := new(KustomizeSelector) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ListGenerator) DeepCopyInto(out *ListGenerator) { *out = *in @@ -2281,6 +2637,50 @@ func (in *OperationState) DeepCopy() *OperationState { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OptionalArray) DeepCopyInto(out *OptionalArray) { + *out = *in + if in.Array != nil { + in, out := &in.Array, &out.Array + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OptionalArray. +func (in *OptionalArray) DeepCopy() *OptionalArray { + if in == nil { + return nil + } + out := new(OptionalArray) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OptionalMap) DeepCopyInto(out *OptionalMap) { + *out = *in + if in.Map != nil { + in, out := &in.Map, &out.Map + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OptionalMap. +func (in *OptionalMap) DeepCopy() *OptionalMap { + if in == nil { + return nil + } + out := new(OptionalMap) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OrphanedResourceKey) DeepCopyInto(out *OrphanedResourceKey) { *out = *in @@ -2354,6 +2754,98 @@ func (in *OverrideIgnoreDiff) DeepCopy() *OverrideIgnoreDiff { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PluginConfigMapRef) DeepCopyInto(out *PluginConfigMapRef) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginConfigMapRef. +func (in *PluginConfigMapRef) DeepCopy() *PluginConfigMapRef { + if in == nil { + return nil + } + out := new(PluginConfigMapRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PluginGenerator) DeepCopyInto(out *PluginGenerator) { + *out = *in + out.ConfigMapRef = in.ConfigMapRef + in.Input.DeepCopyInto(&out.Input) + if in.RequeueAfterSeconds != nil { + in, out := &in.RequeueAfterSeconds, &out.RequeueAfterSeconds + *out = new(int64) + **out = **in + } + in.Template.DeepCopyInto(&out.Template) + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginGenerator. +func (in *PluginGenerator) DeepCopy() *PluginGenerator { + if in == nil { + return nil + } + out := new(PluginGenerator) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PluginInput) DeepCopyInto(out *PluginInput) { + *out = *in + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = make(PluginParameters, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginInput. +func (in *PluginInput) DeepCopy() *PluginInput { + if in == nil { + return nil + } + out := new(PluginInput) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in PluginParameters) DeepCopyInto(out *PluginParameters) { + { + in := &in + *out = make(PluginParameters, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginParameters. +func (in PluginParameters) DeepCopy() PluginParameters { + if in == nil { + return nil + } + out := new(PluginParameters) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ProjectRole) DeepCopyInto(out *ProjectRole) { *out = *in @@ -2421,6 +2913,16 @@ func (in *PullRequestGenerator) DeepCopyInto(out *PullRequestGenerator) { **out = **in } in.Template.DeepCopyInto(&out.Template) + if in.Bitbucket != nil { + in, out := &in.Bitbucket, &out.Bitbucket + *out = new(PullRequestGeneratorBitbucket) + (*in).DeepCopyInto(*out) + } + if in.AzureDevOps != nil { + in, out := &in.AzureDevOps, &out.AzureDevOps + *out = new(PullRequestGeneratorAzureDevOps) + (*in).DeepCopyInto(*out) + } return } @@ -2434,6 +2936,58 @@ func (in *PullRequestGenerator) DeepCopy() *PullRequestGenerator { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PullRequestGeneratorAzureDevOps) DeepCopyInto(out *PullRequestGeneratorAzureDevOps) { + *out = *in + if in.TokenRef != nil { + in, out := &in.TokenRef, &out.TokenRef + *out = new(SecretRef) + **out = **in + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PullRequestGeneratorAzureDevOps. +func (in *PullRequestGeneratorAzureDevOps) DeepCopy() *PullRequestGeneratorAzureDevOps { + if in == nil { + return nil + } + out := new(PullRequestGeneratorAzureDevOps) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PullRequestGeneratorBitbucket) DeepCopyInto(out *PullRequestGeneratorBitbucket) { + *out = *in + if in.BasicAuth != nil { + in, out := &in.BasicAuth, &out.BasicAuth + *out = new(BasicAuthBitbucketServer) + (*in).DeepCopyInto(*out) + } + if in.BearerToken != nil { + in, out := &in.BearerToken, &out.BearerToken + *out = new(BearerTokenBitbucketCloud) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PullRequestGeneratorBitbucket. +func (in *PullRequestGeneratorBitbucket) DeepCopy() *PullRequestGeneratorBitbucket { + if in == nil { + return nil + } + out := new(PullRequestGeneratorBitbucket) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PullRequestGeneratorBitbucketServer) DeepCopyInto(out *PullRequestGeneratorBitbucketServer) { *out = *in @@ -2463,6 +3017,11 @@ func (in *PullRequestGeneratorFilter) DeepCopyInto(out *PullRequestGeneratorFilt *out = new(string) **out = **in } + if in.TargetBranchMatch != nil { + in, out := &in.TargetBranchMatch, &out.TargetBranchMatch + *out = new(string) + **out = **in + } return } @@ -2968,6 +3527,7 @@ func (in *ResourceNode) DeepCopy() *ResourceNode { func (in *ResourceOverride) DeepCopyInto(out *ResourceOverride) { *out = *in in.IgnoreDifferences.DeepCopyInto(&out.IgnoreDifferences) + in.IgnoreResourceUpdates.DeepCopyInto(&out.IgnoreResourceUpdates) if in.KnownTypeFields != nil { in, out := &in.KnownTypeFields, &out.KnownTypeFields *out = make([]KnownTypeField, len(*in)) @@ -3129,6 +3689,7 @@ func (in *RevisionHistory) DeepCopyInto(out *RevisionHistory) { *out = make([]string, len(*in)) copy(*out, *in) } + out.InitiatedBy = in.InitiatedBy return } @@ -3210,6 +3771,18 @@ func (in *SCMProviderGenerator) DeepCopyInto(out *SCMProviderGenerator) { **out = **in } in.Template.DeepCopyInto(&out.Template) + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.AWSCodeCommit != nil { + in, out := &in.AWSCodeCommit, &out.AWSCodeCommit + *out = new(SCMProviderGeneratorAWSCodeCommit) + (*in).DeepCopyInto(*out) + } return } @@ -3223,6 +3796,33 @@ func (in *SCMProviderGenerator) DeepCopy() *SCMProviderGenerator { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SCMProviderGeneratorAWSCodeCommit) DeepCopyInto(out *SCMProviderGeneratorAWSCodeCommit) { + *out = *in + if in.TagFilters != nil { + in, out := &in.TagFilters, &out.TagFilters + *out = make([]*TagFilter, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TagFilter) + **out = **in + } + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SCMProviderGeneratorAWSCodeCommit. +func (in *SCMProviderGeneratorAWSCodeCommit) DeepCopy() *SCMProviderGeneratorAWSCodeCommit { + if in == nil { + return nil + } + out := new(SCMProviderGeneratorAWSCodeCommit) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SCMProviderGeneratorAzureDevOps) DeepCopyInto(out *SCMProviderGeneratorAzureDevOps) { *out = *in @@ -3377,6 +3977,11 @@ func (in *SCMProviderGeneratorGitlab) DeepCopyInto(out *SCMProviderGeneratorGitl *out = new(SecretRef) **out = **in } + if in.IncludeSharedProjects != nil { + in, out := &in.IncludeSharedProjects, &out.IncludeSharedProjects + *out = new(bool) + **out = **in + } return } @@ -3518,6 +4123,11 @@ func (in *SyncOperationResult) DeepCopyInto(out *SyncOperationResult) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.ManagedNamespaceMetadata != nil { + in, out := &in.ManagedNamespaceMetadata, &out.ManagedNamespaceMetadata + *out = new(ManagedNamespaceMetadata) + (*in).DeepCopyInto(*out) + } return } @@ -3771,3 +4381,19 @@ func (in *TLSClientConfig) DeepCopy() *TLSClientConfig { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TagFilter) DeepCopyInto(out *TagFilter) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TagFilter. +func (in *TagFilter) DeepCopy() *TagFilter { + if in == nil { + return nil + } + out := new(TagFilter) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index 0c0911e0387c5..869b10d0f82d6 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -17,8 +17,7 @@ type Interface interface { ArgoprojV1alpha1() argoprojv1alpha1.ArgoprojV1alpha1Interface } -// Clientset contains the clients for groups. Each group has exactly one -// version included in a Clientset. +// Clientset contains the clients for groups. type Clientset struct { *discovery.DiscoveryClient argoprojV1alpha1 *argoprojv1alpha1.ArgoprojV1alpha1Client diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index 115e39faa197d..5773b75ce6df2 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -21,14 +21,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go index a334cbed3235a..46f64a49e41e1 100644 --- a/pkg/client/clientset/versioned/scheme/register.go +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -21,14 +21,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go index 57bd66c672490..7d04eeb35ed52 100644 --- a/pkg/client/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -31,6 +31,11 @@ type sharedInformerFactory struct { // startedInformers is used for tracking which informers have been started. // This allows Start() to be called multiple times safely. startedInformers map[reflect.Type]bool + // wg tracks how many goroutines were started. + wg sync.WaitGroup + // shuttingDown is true when Shutdown has been called. It may still be running + // because it needs to wait for goroutines. + shuttingDown bool } // WithCustomResyncConfig sets a custom resync period for the specified informer types. @@ -91,20 +96,39 @@ func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResy return factory } -// Start initializes all requested informers. func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { f.lock.Lock() defer f.lock.Unlock() + if f.shuttingDown { + return + } + for informerType, informer := range f.informers { if !f.startedInformers[informerType] { - go informer.Run(stopCh) + f.wg.Add(1) + // We need a new variable in each loop iteration, + // otherwise the goroutine would use the loop variable + // and that keeps changing. + informer := informer + go func() { + defer f.wg.Done() + informer.Run(stopCh) + }() f.startedInformers[informerType] = true } } } -// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) Shutdown() { + f.lock.Lock() + f.shuttingDown = true + f.lock.Unlock() + + // Will return immediately if there is nothing to wait for. + f.wg.Wait() +} + func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { informers := func() map[reflect.Type]cache.SharedIndexInformer { f.lock.Lock() @@ -151,11 +175,58 @@ func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internal // SharedInformerFactory provides shared informers for resources in all known // API group versions. +// +// It is typically used like this: +// +// ctx, cancel := context.Background() +// defer cancel() +// factory := NewSharedInformerFactory(client, resyncPeriod) +// defer factory.WaitForStop() // Returns immediately if nothing was started. +// genericInformer := factory.ForResource(resource) +// typedInformer := factory.SomeAPIGroup().V1().SomeType() +// factory.Start(ctx.Done()) // Start processing these informers. +// synced := factory.WaitForCacheSync(ctx.Done()) +// for v, ok := range synced { +// if !ok { +// fmt.Fprintf(os.Stderr, "caches failed to sync: %v", v) +// return +// } +// } +// +// // Creating informers can also be created after Start, but then +// // Start must be called again: +// anotherGenericInformer := factory.ForResource(resource) +// factory.Start(ctx.Done()) type SharedInformerFactory interface { internalinterfaces.SharedInformerFactory - ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + + // Start initializes all requested informers. They are handled in goroutines + // which run until the stop channel gets closed. + Start(stopCh <-chan struct{}) + + // Shutdown marks a factory as shutting down. At that point no new + // informers can be started anymore and Start will return without + // doing anything. + // + // In addition, Shutdown blocks until all goroutines have terminated. For that + // to happen, the close channel(s) that they were started with must be closed, + // either before Shutdown gets called or while it is waiting. + // + // Shutdown may be called multiple times, even concurrently. All such calls will + // block until all goroutines have terminated. + Shutdown() + + // WaitForCacheSync blocks until all started informers' caches were synced + // or the stop channel gets closed. WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + // ForResource gives generic access to a shared informer of the matching type. + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + + // InternalInformerFor returns the SharedIndexInformer for obj using an internal + // client. + InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer + Argoproj() application.Interface } diff --git a/pkg/ratelimiter/ratelimiter.go b/pkg/ratelimiter/ratelimiter.go new file mode 100644 index 0000000000000..1c491a584873e --- /dev/null +++ b/pkg/ratelimiter/ratelimiter.go @@ -0,0 +1,124 @@ +package ratelimiter + +import ( + "math" + "sync" + "time" + + "golang.org/x/time/rate" + "k8s.io/client-go/util/workqueue" +) + +type AppControllerRateLimiterConfig struct { + BucketSize int64 + BucketQPS float64 + FailureCoolDown time.Duration + BaseDelay time.Duration + MaxDelay time.Duration + BackoffFactor float64 +} + +func GetDefaultAppRateLimiterConfig() *AppControllerRateLimiterConfig { + return &AppControllerRateLimiterConfig{ + // global queue rate limit config + 500, + // when WORKQUEUE_BUCKET_QPS is MaxFloat64 global bucket limiting is disabled(default) + math.MaxFloat64, + // individual item rate limit config + // when WORKQUEUE_FAILURE_COOLDOWN is 0 per item rate limiting is disabled(default) + 0, + time.Millisecond, + time.Second, + 1.5, + } +} + +// NewCustomAppControllerRateLimiter is a constructor for the rate limiter for a workqueue used by app controller. It has +// both overall and per-item rate limiting. The overall is a token bucket and the per-item is exponential(with auto resets) +func NewCustomAppControllerRateLimiter(cfg *AppControllerRateLimiterConfig) workqueue.RateLimiter { + return workqueue.NewMaxOfRateLimiter( + NewItemExponentialRateLimiterWithAutoReset(cfg.BaseDelay, cfg.MaxDelay, cfg.FailureCoolDown, cfg.BackoffFactor), + &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(cfg.BucketQPS), int(cfg.BucketSize))}, + ) +} + +type failureData struct { + failures int + lastFailure time.Time +} + +// ItemExponentialRateLimiterWithAutoReset does a simple baseDelay*2^ limit +// dealing with max failures and expiration/resets are up dependent on the cooldown period +type ItemExponentialRateLimiterWithAutoReset struct { + failuresLock sync.Mutex + failures map[interface{}]failureData + + baseDelay time.Duration + maxDelay time.Duration + coolDown time.Duration + backoffFactor float64 +} + +var _ workqueue.RateLimiter = &ItemExponentialRateLimiterWithAutoReset{} + +func NewItemExponentialRateLimiterWithAutoReset(baseDelay, maxDelay, failureCoolDown time.Duration, backoffFactor float64) workqueue.RateLimiter { + return &ItemExponentialRateLimiterWithAutoReset{ + failures: map[interface{}]failureData{}, + baseDelay: baseDelay, + maxDelay: maxDelay, + coolDown: failureCoolDown, + backoffFactor: backoffFactor, + } +} + +func (r *ItemExponentialRateLimiterWithAutoReset) When(item interface{}) time.Duration { + r.failuresLock.Lock() + defer r.failuresLock.Unlock() + + if _, ok := r.failures[item]; !ok { + r.failures[item] = failureData{ + failures: 0, + lastFailure: time.Now(), + } + } + + exp := r.failures[item] + + // if coolDown period is reached reset failures for item + if time.Since(exp.lastFailure) >= r.coolDown { + delete(r.failures, item) + return r.baseDelay + } + + r.failures[item] = failureData{ + failures: exp.failures + 1, + lastFailure: time.Now(), + } + + // The backoff is capped such that 'calculated' value never overflows. + backoff := float64(r.baseDelay.Nanoseconds()) * math.Pow(r.backoffFactor, float64(exp.failures)) + if backoff > math.MaxInt64 { + return r.maxDelay + } + + calculated := time.Duration(backoff) + if calculated > r.maxDelay { + return r.maxDelay + } + + return calculated +} + +func (r *ItemExponentialRateLimiterWithAutoReset) NumRequeues(item interface{}) int { + r.failuresLock.Lock() + defer r.failuresLock.Unlock() + + return r.failures[item].failures +} + +func (r *ItemExponentialRateLimiterWithAutoReset) Forget(item interface{}) { + r.failuresLock.Lock() + defer r.failuresLock.Unlock() + + delete(r.failures, item) +} diff --git a/reposerver/apiclient/clientset.go b/reposerver/apiclient/clientset.go index 1ef95466cd739..417dc758ef5bd 100644 --- a/reposerver/apiclient/clientset.go +++ b/reposerver/apiclient/clientset.go @@ -3,12 +3,12 @@ package apiclient import ( "crypto/tls" "crypto/x509" + "fmt" "time" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" log "github.com/sirupsen/logrus" - "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" @@ -17,6 +17,8 @@ import ( "github.com/argoproj/argo-cd/v2/util/io" ) +//go:generate go run github.com/vektra/mockery/v2@v2.15.0 --name=RepoServerServiceClient + const ( // MaxGRPCMessageSize contains max grpc message size MaxGRPCMessageSize = 100 * 1024 * 1024 @@ -46,7 +48,7 @@ type clientSet struct { func (c *clientSet) NewRepoServerClient() (io.Closer, RepoServerServiceClient, error) { conn, err := NewConnection(c.address, c.timeoutSeconds, &c.tlsConfig) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to open a new connection to repo server: %w", err) } return conn, NewRepoServerServiceClient(conn), nil } @@ -64,8 +66,8 @@ func NewConnection(address string, timeoutSeconds int, tlsConfig *TLSConfigurati grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)), grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(unaryInterceptors...)), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(MaxGRPCMessageSize), grpc.MaxCallSendMsgSize(MaxGRPCMessageSize)), - grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()), - grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()), + grpc.WithUnaryInterceptor(argogrpc.OTELUnaryClientInterceptor()), + grpc.WithStreamInterceptor(argogrpc.OTELStreamClientInterceptor()), } tlsC := &tls.Config{} diff --git a/reposerver/apiclient/clientset_test.go b/reposerver/apiclient/clientset_test.go new file mode 100644 index 0000000000000..617cbbd0796e5 --- /dev/null +++ b/reposerver/apiclient/clientset_test.go @@ -0,0 +1,91 @@ +package apiclient_test + +import ( + "testing" + + "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks" + "github.com/stretchr/testify/assert" +) + +func TestNewRepoServerClient_CorrectClientReturned(t *testing.T) { + + mockClientset := &mocks.Clientset{ + RepoServerServiceClient: &mocks.RepoServerServiceClient{}, + } + + closer, client, err := mockClientset.NewRepoServerClient() + + assert.NoError(t, err) + assert.NotNil(t, closer) + assert.NotNil(t, client) + assert.Equal(t, mockClientset.RepoServerServiceClient, client) +} + +func TestNewRepoServerClientset_InvalidInput(t *testing.T) { + // Call the function with invalid inputs + invalidClientset := apiclient.NewRepoServerClientset("", -1, apiclient.TLSConfiguration{}) + + assert.NotNil(t, invalidClientset) + assert.Implements(t, (*apiclient.Clientset)(nil), invalidClientset) +} + +func TestNewRepoServerClientset_SuccessfulConnection(t *testing.T) { + // Call the function with valid inputs + clientset := apiclient.NewRepoServerClientset("localhost:8080", 1, apiclient.TLSConfiguration{}) + + assert.NotNil(t, clientset) + assert.Implements(t, (*apiclient.Clientset)(nil), clientset) +} + +func TestNewRepoServerClientset_SuccessfulConnectionWithTLS(t *testing.T) { + // Call the function with valid inputs + clientset := apiclient.NewRepoServerClientset("localhost:8080", 1, apiclient.TLSConfiguration{ + DisableTLS: false, + StrictValidation: true, + Certificates: nil, + }) + + assert.NotNil(t, clientset) + assert.Implements(t, (*apiclient.Clientset)(nil), clientset) +} + +func TestNewConnection_TLSWithStrictValidation(t *testing.T) { + tlsConfig := apiclient.TLSConfiguration{ + DisableTLS: false, + StrictValidation: true, + Certificates: nil, + } + + conn, err := apiclient.NewConnection("example.com:443", 10, &tlsConfig) + + assert.NoError(t, err) + assert.NotNil(t, conn) +} + +func TestNewConnection_TLSWithStrictValidationAndCertificates(t *testing.T) { + tlsConfig := apiclient.TLSConfiguration{ + DisableTLS: false, + StrictValidation: true, + Certificates: nil, + } + + conn, err := apiclient.NewConnection("example.com:443", 10, &tlsConfig) + + assert.NoError(t, err) + assert.NotNil(t, conn) +} + +func TestNewConnection_InsecureConnection(t *testing.T) { + // Create a TLS configuration with TLS disabled + tlsConfig := apiclient.TLSConfiguration{ + DisableTLS: true, + StrictValidation: false, + Certificates: nil, + } + + conn, err := apiclient.NewConnection("example.com:80", 10, &tlsConfig) + + assert.NoError(t, err) + assert.NotNil(t, conn) +} diff --git a/reposerver/apiclient/mocks/RepoServerServiceClient.go b/reposerver/apiclient/mocks/RepoServerServiceClient.go index 8c6dcfaee2b72..25337c53a6373 100644 --- a/reposerver/apiclient/mocks/RepoServerServiceClient.go +++ b/reposerver/apiclient/mocks/RepoServerServiceClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.1. DO NOT EDIT. +// Code generated by mockery v2.21.1. DO NOT EDIT. package mocks @@ -33,6 +33,10 @@ func (_m *RepoServerServiceClient) GenerateManifest(ctx context.Context, in *api ret := _m.Called(_ca...) var r0 *apiclient.ManifestResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.ManifestRequest, ...grpc.CallOption) (*apiclient.ManifestResponse, error)); ok { + return rf(ctx, in, opts...) + } if rf, ok := ret.Get(0).(func(context.Context, *apiclient.ManifestRequest, ...grpc.CallOption) *apiclient.ManifestResponse); ok { r0 = rf(ctx, in, opts...) } else { @@ -41,7 +45,6 @@ func (_m *RepoServerServiceClient) GenerateManifest(ctx context.Context, in *api } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *apiclient.ManifestRequest, ...grpc.CallOption) error); ok { r1 = rf(ctx, in, opts...) } else { @@ -63,6 +66,10 @@ func (_m *RepoServerServiceClient) GenerateManifestWithFiles(ctx context.Context ret := _m.Called(_ca...) var r0 apiclient.RepoServerService_GenerateManifestWithFilesClient + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ...grpc.CallOption) (apiclient.RepoServerService_GenerateManifestWithFilesClient, error)); ok { + return rf(ctx, opts...) + } if rf, ok := ret.Get(0).(func(context.Context, ...grpc.CallOption) apiclient.RepoServerService_GenerateManifestWithFilesClient); ok { r0 = rf(ctx, opts...) } else { @@ -71,7 +78,6 @@ func (_m *RepoServerServiceClient) GenerateManifestWithFiles(ctx context.Context } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, ...grpc.CallOption) error); ok { r1 = rf(ctx, opts...) } else { @@ -93,6 +99,10 @@ func (_m *RepoServerServiceClient) GetAppDetails(ctx context.Context, in *apicli ret := _m.Called(_ca...) var r0 *apiclient.RepoAppDetailsResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.RepoServerAppDetailsQuery, ...grpc.CallOption) (*apiclient.RepoAppDetailsResponse, error)); ok { + return rf(ctx, in, opts...) + } if rf, ok := ret.Get(0).(func(context.Context, *apiclient.RepoServerAppDetailsQuery, ...grpc.CallOption) *apiclient.RepoAppDetailsResponse); ok { r0 = rf(ctx, in, opts...) } else { @@ -101,7 +111,6 @@ func (_m *RepoServerServiceClient) GetAppDetails(ctx context.Context, in *apicli } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *apiclient.RepoServerAppDetailsQuery, ...grpc.CallOption) error); ok { r1 = rf(ctx, in, opts...) } else { @@ -111,6 +120,72 @@ func (_m *RepoServerServiceClient) GetAppDetails(ctx context.Context, in *apicli return r0, r1 } +// GetGitDirectories provides a mock function with given fields: ctx, in, opts +func (_m *RepoServerServiceClient) GetGitDirectories(ctx context.Context, in *apiclient.GitDirectoriesRequest, opts ...grpc.CallOption) (*apiclient.GitDirectoriesResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *apiclient.GitDirectoriesResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.GitDirectoriesRequest, ...grpc.CallOption) (*apiclient.GitDirectoriesResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.GitDirectoriesRequest, ...grpc.CallOption) *apiclient.GitDirectoriesResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*apiclient.GitDirectoriesResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *apiclient.GitDirectoriesRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetGitFiles provides a mock function with given fields: ctx, in, opts +func (_m *RepoServerServiceClient) GetGitFiles(ctx context.Context, in *apiclient.GitFilesRequest, opts ...grpc.CallOption) (*apiclient.GitFilesResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *apiclient.GitFilesResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.GitFilesRequest, ...grpc.CallOption) (*apiclient.GitFilesResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.GitFilesRequest, ...grpc.CallOption) *apiclient.GitFilesResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*apiclient.GitFilesResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *apiclient.GitFilesRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetHelmCharts provides a mock function with given fields: ctx, in, opts func (_m *RepoServerServiceClient) GetHelmCharts(ctx context.Context, in *apiclient.HelmChartsRequest, opts ...grpc.CallOption) (*apiclient.HelmChartsResponse, error) { _va := make([]interface{}, len(opts)) @@ -123,6 +198,10 @@ func (_m *RepoServerServiceClient) GetHelmCharts(ctx context.Context, in *apicli ret := _m.Called(_ca...) var r0 *apiclient.HelmChartsResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.HelmChartsRequest, ...grpc.CallOption) (*apiclient.HelmChartsResponse, error)); ok { + return rf(ctx, in, opts...) + } if rf, ok := ret.Get(0).(func(context.Context, *apiclient.HelmChartsRequest, ...grpc.CallOption) *apiclient.HelmChartsResponse); ok { r0 = rf(ctx, in, opts...) } else { @@ -131,7 +210,6 @@ func (_m *RepoServerServiceClient) GetHelmCharts(ctx context.Context, in *apicli } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *apiclient.HelmChartsRequest, ...grpc.CallOption) error); ok { r1 = rf(ctx, in, opts...) } else { @@ -141,6 +219,36 @@ func (_m *RepoServerServiceClient) GetHelmCharts(ctx context.Context, in *apicli return r0, r1 } +// GetRevisionChartDetails provides a mock function with given fields: ctx, in, opts +func (_m *RepoServerServiceClient) GetRevisionChartDetails(ctx context.Context, in *apiclient.RepoServerRevisionChartDetailsRequest, opts ...grpc.CallOption) (*v1alpha1.ChartDetails, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *v1alpha1.ChartDetails + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.RepoServerRevisionChartDetailsRequest, ...grpc.CallOption) *v1alpha1.ChartDetails); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1alpha1.ChartDetails) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *apiclient.RepoServerRevisionChartDetailsRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetRevisionMetadata provides a mock function with given fields: ctx, in, opts func (_m *RepoServerServiceClient) GetRevisionMetadata(ctx context.Context, in *apiclient.RepoServerRevisionMetadataRequest, opts ...grpc.CallOption) (*v1alpha1.RevisionMetadata, error) { _va := make([]interface{}, len(opts)) @@ -153,6 +261,10 @@ func (_m *RepoServerServiceClient) GetRevisionMetadata(ctx context.Context, in * ret := _m.Called(_ca...) var r0 *v1alpha1.RevisionMetadata + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.RepoServerRevisionMetadataRequest, ...grpc.CallOption) (*v1alpha1.RevisionMetadata, error)); ok { + return rf(ctx, in, opts...) + } if rf, ok := ret.Get(0).(func(context.Context, *apiclient.RepoServerRevisionMetadataRequest, ...grpc.CallOption) *v1alpha1.RevisionMetadata); ok { r0 = rf(ctx, in, opts...) } else { @@ -161,7 +273,6 @@ func (_m *RepoServerServiceClient) GetRevisionMetadata(ctx context.Context, in * } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *apiclient.RepoServerRevisionMetadataRequest, ...grpc.CallOption) error); ok { r1 = rf(ctx, in, opts...) } else { @@ -183,6 +294,10 @@ func (_m *RepoServerServiceClient) ListApps(ctx context.Context, in *apiclient.L ret := _m.Called(_ca...) var r0 *apiclient.AppList + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.ListAppsRequest, ...grpc.CallOption) (*apiclient.AppList, error)); ok { + return rf(ctx, in, opts...) + } if rf, ok := ret.Get(0).(func(context.Context, *apiclient.ListAppsRequest, ...grpc.CallOption) *apiclient.AppList); ok { r0 = rf(ctx, in, opts...) } else { @@ -191,7 +306,6 @@ func (_m *RepoServerServiceClient) ListApps(ctx context.Context, in *apiclient.L } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *apiclient.ListAppsRequest, ...grpc.CallOption) error); ok { r1 = rf(ctx, in, opts...) } else { @@ -213,6 +327,10 @@ func (_m *RepoServerServiceClient) ListPlugins(ctx context.Context, in *emptypb. ret := _m.Called(_ca...) var r0 *apiclient.PluginList + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *emptypb.Empty, ...grpc.CallOption) (*apiclient.PluginList, error)); ok { + return rf(ctx, in, opts...) + } if rf, ok := ret.Get(0).(func(context.Context, *emptypb.Empty, ...grpc.CallOption) *apiclient.PluginList); ok { r0 = rf(ctx, in, opts...) } else { @@ -221,7 +339,6 @@ func (_m *RepoServerServiceClient) ListPlugins(ctx context.Context, in *emptypb. } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *emptypb.Empty, ...grpc.CallOption) error); ok { r1 = rf(ctx, in, opts...) } else { @@ -243,6 +360,10 @@ func (_m *RepoServerServiceClient) ListRefs(ctx context.Context, in *apiclient.L ret := _m.Called(_ca...) var r0 *apiclient.Refs + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.ListRefsRequest, ...grpc.CallOption) (*apiclient.Refs, error)); ok { + return rf(ctx, in, opts...) + } if rf, ok := ret.Get(0).(func(context.Context, *apiclient.ListRefsRequest, ...grpc.CallOption) *apiclient.Refs); ok { r0 = rf(ctx, in, opts...) } else { @@ -251,7 +372,6 @@ func (_m *RepoServerServiceClient) ListRefs(ctx context.Context, in *apiclient.L } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *apiclient.ListRefsRequest, ...grpc.CallOption) error); ok { r1 = rf(ctx, in, opts...) } else { @@ -273,6 +393,10 @@ func (_m *RepoServerServiceClient) ResolveRevision(ctx context.Context, in *apic ret := _m.Called(_ca...) var r0 *apiclient.ResolveRevisionResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.ResolveRevisionRequest, ...grpc.CallOption) (*apiclient.ResolveRevisionResponse, error)); ok { + return rf(ctx, in, opts...) + } if rf, ok := ret.Get(0).(func(context.Context, *apiclient.ResolveRevisionRequest, ...grpc.CallOption) *apiclient.ResolveRevisionResponse); ok { r0 = rf(ctx, in, opts...) } else { @@ -281,7 +405,6 @@ func (_m *RepoServerServiceClient) ResolveRevision(ctx context.Context, in *apic } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *apiclient.ResolveRevisionRequest, ...grpc.CallOption) error); ok { r1 = rf(ctx, in, opts...) } else { @@ -303,6 +426,10 @@ func (_m *RepoServerServiceClient) TestRepository(ctx context.Context, in *apicl ret := _m.Called(_ca...) var r0 *apiclient.TestRepositoryResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *apiclient.TestRepositoryRequest, ...grpc.CallOption) (*apiclient.TestRepositoryResponse, error)); ok { + return rf(ctx, in, opts...) + } if rf, ok := ret.Get(0).(func(context.Context, *apiclient.TestRepositoryRequest, ...grpc.CallOption) *apiclient.TestRepositoryResponse); ok { r0 = rf(ctx, in, opts...) } else { @@ -311,7 +438,6 @@ func (_m *RepoServerServiceClient) TestRepository(ctx context.Context, in *apicl } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *apiclient.TestRepositoryRequest, ...grpc.CallOption) error); ok { r1 = rf(ctx, in, opts...) } else { diff --git a/reposerver/apiclient/mocks/RepoServerService_GenerateManifestWithFilesClient.go b/reposerver/apiclient/mocks/RepoServerService_GenerateManifestWithFilesClient.go new file mode 100644 index 0000000000000..79151a7ca1f58 --- /dev/null +++ b/reposerver/apiclient/mocks/RepoServerService_GenerateManifestWithFilesClient.go @@ -0,0 +1,167 @@ +// Code generated by mockery v2.13.1. DO NOT EDIT. + +package mocks + +import ( + context "context" + + apiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + + metadata "google.golang.org/grpc/metadata" + + mock "github.com/stretchr/testify/mock" +) + +// RepoServerService_GenerateManifestWithFilesClient is an autogenerated mock type for the RepoServerService_GenerateManifestWithFilesClient type +type RepoServerService_GenerateManifestWithFilesClient struct { + mock.Mock +} + +// CloseAndRecv provides a mock function with given fields: +func (_m *RepoServerService_GenerateManifestWithFilesClient) CloseAndRecv() (*apiclient.ManifestResponse, error) { + ret := _m.Called() + + var r0 *apiclient.ManifestResponse + if rf, ok := ret.Get(0).(func() *apiclient.ManifestResponse); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*apiclient.ManifestResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CloseSend provides a mock function with given fields: +func (_m *RepoServerService_GenerateManifestWithFilesClient) CloseSend() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Context provides a mock function with given fields: +func (_m *RepoServerService_GenerateManifestWithFilesClient) Context() context.Context { + ret := _m.Called() + + var r0 context.Context + if rf, ok := ret.Get(0).(func() context.Context); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// Header provides a mock function with given fields: +func (_m *RepoServerService_GenerateManifestWithFilesClient) Header() (metadata.MD, error) { + ret := _m.Called() + + var r0 metadata.MD + if rf, ok := ret.Get(0).(func() metadata.MD); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(metadata.MD) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RecvMsg provides a mock function with given fields: m +func (_m *RepoServerService_GenerateManifestWithFilesClient) RecvMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Send provides a mock function with given fields: _a0 +func (_m *RepoServerService_GenerateManifestWithFilesClient) Send(_a0 *apiclient.ManifestRequestWithFiles) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(*apiclient.ManifestRequestWithFiles) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SendMsg provides a mock function with given fields: m +func (_m *RepoServerService_GenerateManifestWithFilesClient) SendMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Trailer provides a mock function with given fields: +func (_m *RepoServerService_GenerateManifestWithFilesClient) Trailer() metadata.MD { + ret := _m.Called() + + var r0 metadata.MD + if rf, ok := ret.Get(0).(func() metadata.MD); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(metadata.MD) + } + } + + return r0 +} + +type mockConstructorTestingTNewRepoServerService_GenerateManifestWithFilesClient interface { + mock.TestingT + Cleanup(func()) +} + +// NewRepoServerService_GenerateManifestWithFilesClient creates a new instance of RepoServerService_GenerateManifestWithFilesClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewRepoServerService_GenerateManifestWithFilesClient(t mockConstructorTestingTNewRepoServerService_GenerateManifestWithFilesClient) *RepoServerService_GenerateManifestWithFilesClient { + mock := &RepoServerService_GenerateManifestWithFilesClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/reposerver/apiclient/repository.pb.go b/reposerver/apiclient/repository.pb.go index f19807676943c..914a967db3dfc 100644 --- a/reposerver/apiclient/repository.pb.go +++ b/reposerver/apiclient/repository.pb.go @@ -36,26 +36,31 @@ type ManifestRequest struct { NoCache bool `protobuf:"varint,3,opt,name=noCache,proto3" json:"noCache,omitempty"` AppLabelKey string `protobuf:"bytes,4,opt,name=appLabelKey,proto3" json:"appLabelKey,omitempty"` // Name of the application for which the request is triggered - AppName string `protobuf:"bytes,5,opt,name=appName,proto3" json:"appName,omitempty"` - Namespace string `protobuf:"bytes,8,opt,name=namespace,proto3" json:"namespace,omitempty"` - ApplicationSource *v1alpha1.ApplicationSource `protobuf:"bytes,10,opt,name=applicationSource,proto3" json:"applicationSource,omitempty"` - Repos []*v1alpha1.Repository `protobuf:"bytes,11,rep,name=repos,proto3" json:"repos,omitempty"` - Plugins []*v1alpha1.ConfigManagementPlugin `protobuf:"bytes,12,rep,name=plugins,proto3" json:"plugins,omitempty"` - KustomizeOptions *v1alpha1.KustomizeOptions `protobuf:"bytes,13,opt,name=kustomizeOptions,proto3" json:"kustomizeOptions,omitempty"` - KubeVersion string `protobuf:"bytes,14,opt,name=kubeVersion,proto3" json:"kubeVersion,omitempty"` - ApiVersions []string `protobuf:"bytes,15,rep,name=apiVersions,proto3" json:"apiVersions,omitempty"` + AppName string `protobuf:"bytes,5,opt,name=appName,proto3" json:"appName,omitempty"` + Namespace string `protobuf:"bytes,8,opt,name=namespace,proto3" json:"namespace,omitempty"` + ApplicationSource *v1alpha1.ApplicationSource `protobuf:"bytes,10,opt,name=applicationSource,proto3" json:"applicationSource,omitempty"` + Repos []*v1alpha1.Repository `protobuf:"bytes,11,rep,name=repos,proto3" json:"repos,omitempty"` + // Deprecated: use sidecar plugins instead. + Plugins []*v1alpha1.ConfigManagementPlugin `protobuf:"bytes,12,rep,name=plugins,proto3" json:"plugins,omitempty"` + KustomizeOptions *v1alpha1.KustomizeOptions `protobuf:"bytes,13,opt,name=kustomizeOptions,proto3" json:"kustomizeOptions,omitempty"` + KubeVersion string `protobuf:"bytes,14,opt,name=kubeVersion,proto3" json:"kubeVersion,omitempty"` + ApiVersions []string `protobuf:"bytes,15,rep,name=apiVersions,proto3" json:"apiVersions,omitempty"` // Request to verify the signature when generating the manifests (only for Git repositories) - VerifySignature bool `protobuf:"varint,16,opt,name=verifySignature,proto3" json:"verifySignature,omitempty"` - HelmRepoCreds []*v1alpha1.RepoCreds `protobuf:"bytes,17,rep,name=helmRepoCreds,proto3" json:"helmRepoCreds,omitempty"` - NoRevisionCache bool `protobuf:"varint,18,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"` - TrackingMethod string `protobuf:"bytes,19,opt,name=trackingMethod,proto3" json:"trackingMethod,omitempty"` - EnabledSourceTypes map[string]bool `protobuf:"bytes,20,rep,name=enabledSourceTypes,proto3" json:"enabledSourceTypes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` - HelmOptions *v1alpha1.HelmOptions `protobuf:"bytes,21,opt,name=helmOptions,proto3" json:"helmOptions,omitempty"` - HasMultipleSources bool `protobuf:"varint,22,opt,name=hasMultipleSources,proto3" json:"hasMultipleSources,omitempty"` - RefSources map[string]*v1alpha1.RefTarget `protobuf:"bytes,23,rep,name=refSources,proto3" json:"refSources,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + VerifySignature bool `protobuf:"varint,16,opt,name=verifySignature,proto3" json:"verifySignature,omitempty"` + HelmRepoCreds []*v1alpha1.RepoCreds `protobuf:"bytes,17,rep,name=helmRepoCreds,proto3" json:"helmRepoCreds,omitempty"` + NoRevisionCache bool `protobuf:"varint,18,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"` + TrackingMethod string `protobuf:"bytes,19,opt,name=trackingMethod,proto3" json:"trackingMethod,omitempty"` + EnabledSourceTypes map[string]bool `protobuf:"bytes,20,rep,name=enabledSourceTypes,proto3" json:"enabledSourceTypes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + HelmOptions *v1alpha1.HelmOptions `protobuf:"bytes,21,opt,name=helmOptions,proto3" json:"helmOptions,omitempty"` + HasMultipleSources bool `protobuf:"varint,22,opt,name=hasMultipleSources,proto3" json:"hasMultipleSources,omitempty"` + RefSources map[string]*v1alpha1.RefTarget `protobuf:"bytes,23,rep,name=refSources,proto3" json:"refSources,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // This is used to surface "source not permitted" errors for Helm repositories + ProjectSourceRepos []string `protobuf:"bytes,24,rep,name=projectSourceRepos,proto3" json:"projectSourceRepos,omitempty"` + // This is used to surface "source not permitted" errors for Helm repositories + ProjectName string `protobuf:"bytes,25,opt,name=projectName,proto3" json:"projectName,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ManifestRequest) Reset() { *m = ManifestRequest{} } @@ -231,6 +236,20 @@ func (m *ManifestRequest) GetRefSources() map[string]*v1alpha1.RefTarget { return nil } +func (m *ManifestRequest) GetProjectSourceRepos() []string { + if m != nil { + return m.ProjectSourceRepos + } + return nil +} + +func (m *ManifestRequest) GetProjectName() string { + if m != nil { + return m.ProjectName + } + return "" +} + type ManifestRequestWithFiles struct { // Types that are valid to be assigned to Part: // *ManifestRequestWithFiles_Request @@ -1327,6 +1346,72 @@ func (m *RepoServerRevisionMetadataRequest) GetCheckSignature() bool { return false } +type RepoServerRevisionChartDetailsRequest struct { + // the repo + Repo *v1alpha1.Repository `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` + // the chart + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // the revision within the chart + Revision string `protobuf:"bytes,3,opt,name=revision,proto3" json:"revision,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RepoServerRevisionChartDetailsRequest) Reset() { *m = RepoServerRevisionChartDetailsRequest{} } +func (m *RepoServerRevisionChartDetailsRequest) String() string { return proto.CompactTextString(m) } +func (*RepoServerRevisionChartDetailsRequest) ProtoMessage() {} +func (*RepoServerRevisionChartDetailsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd8723cfcc820480, []int{18} +} +func (m *RepoServerRevisionChartDetailsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RepoServerRevisionChartDetailsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RepoServerRevisionChartDetailsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RepoServerRevisionChartDetailsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RepoServerRevisionChartDetailsRequest.Merge(m, src) +} +func (m *RepoServerRevisionChartDetailsRequest) XXX_Size() int { + return m.Size() +} +func (m *RepoServerRevisionChartDetailsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RepoServerRevisionChartDetailsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RepoServerRevisionChartDetailsRequest proto.InternalMessageInfo + +func (m *RepoServerRevisionChartDetailsRequest) GetRepo() *v1alpha1.Repository { + if m != nil { + return m.Repo + } + return nil +} + +func (m *RepoServerRevisionChartDetailsRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *RepoServerRevisionChartDetailsRequest) GetRevision() string { + if m != nil { + return m.Revision + } + return "" +} + // HelmAppSpec contains helm app name in source repo type HelmAppSpec struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` @@ -1346,7 +1431,7 @@ func (m *HelmAppSpec) Reset() { *m = HelmAppSpec{} } func (m *HelmAppSpec) String() string { return proto.CompactTextString(m) } func (*HelmAppSpec) ProtoMessage() {} func (*HelmAppSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_dd8723cfcc820480, []int{18} + return fileDescriptor_dd8723cfcc820480, []int{19} } func (m *HelmAppSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1423,7 +1508,7 @@ func (m *KustomizeAppSpec) Reset() { *m = KustomizeAppSpec{} } func (m *KustomizeAppSpec) String() string { return proto.CompactTextString(m) } func (*KustomizeAppSpec) ProtoMessage() {} func (*KustomizeAppSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_dd8723cfcc820480, []int{19} + return fileDescriptor_dd8723cfcc820480, []int{20} } func (m *KustomizeAppSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1470,7 +1555,7 @@ func (m *DirectoryAppSpec) Reset() { *m = DirectoryAppSpec{} } func (m *DirectoryAppSpec) String() string { return proto.CompactTextString(m) } func (*DirectoryAppSpec) ProtoMessage() {} func (*DirectoryAppSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_dd8723cfcc820480, []int{20} + return fileDescriptor_dd8723cfcc820480, []int{21} } func (m *DirectoryAppSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1530,7 +1615,7 @@ func (m *ParameterAnnouncement) Reset() { *m = ParameterAnnouncement{} } func (m *ParameterAnnouncement) String() string { return proto.CompactTextString(m) } func (*ParameterAnnouncement) ProtoMessage() {} func (*ParameterAnnouncement) Descriptor() ([]byte, []int) { - return fileDescriptor_dd8723cfcc820480, []int{21} + return fileDescriptor_dd8723cfcc820480, []int{22} } func (m *ParameterAnnouncement) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1634,7 +1719,7 @@ func (m *PluginAppSpec) Reset() { *m = PluginAppSpec{} } func (m *PluginAppSpec) String() string { return proto.CompactTextString(m) } func (*PluginAppSpec) ProtoMessage() {} func (*PluginAppSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_dd8723cfcc820480, []int{22} + return fileDescriptor_dd8723cfcc820480, []int{23} } func (m *PluginAppSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1681,7 +1766,7 @@ func (m *HelmChartsRequest) Reset() { *m = HelmChartsRequest{} } func (m *HelmChartsRequest) String() string { return proto.CompactTextString(m) } func (*HelmChartsRequest) ProtoMessage() {} func (*HelmChartsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_dd8723cfcc820480, []int{23} + return fileDescriptor_dd8723cfcc820480, []int{24} } func (m *HelmChartsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1729,7 +1814,7 @@ func (m *HelmChart) Reset() { *m = HelmChart{} } func (m *HelmChart) String() string { return proto.CompactTextString(m) } func (*HelmChart) ProtoMessage() {} func (*HelmChart) Descriptor() ([]byte, []int) { - return fileDescriptor_dd8723cfcc820480, []int{24} + return fileDescriptor_dd8723cfcc820480, []int{25} } func (m *HelmChart) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1783,7 +1868,7 @@ func (m *HelmChartsResponse) Reset() { *m = HelmChartsResponse{} } func (m *HelmChartsResponse) String() string { return proto.CompactTextString(m) } func (*HelmChartsResponse) ProtoMessage() {} func (*HelmChartsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_dd8723cfcc820480, []int{25} + return fileDescriptor_dd8723cfcc820480, []int{26} } func (m *HelmChartsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1819,6 +1904,260 @@ func (m *HelmChartsResponse) GetItems() []*HelmChart { return nil } +type GitFilesRequest struct { + Repo *v1alpha1.Repository `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` + SubmoduleEnabled bool `protobuf:"varint,2,opt,name=submoduleEnabled,proto3" json:"submoduleEnabled,omitempty"` + Revision string `protobuf:"bytes,3,opt,name=revision,proto3" json:"revision,omitempty"` + Path string `protobuf:"bytes,4,opt,name=path,proto3" json:"path,omitempty"` + NewGitFileGlobbingEnabled bool `protobuf:"varint,5,opt,name=NewGitFileGlobbingEnabled,proto3" json:"NewGitFileGlobbingEnabled,omitempty"` + NoRevisionCache bool `protobuf:"varint,6,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GitFilesRequest) Reset() { *m = GitFilesRequest{} } +func (m *GitFilesRequest) String() string { return proto.CompactTextString(m) } +func (*GitFilesRequest) ProtoMessage() {} +func (*GitFilesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd8723cfcc820480, []int{27} +} +func (m *GitFilesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GitFilesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GitFilesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GitFilesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GitFilesRequest.Merge(m, src) +} +func (m *GitFilesRequest) XXX_Size() int { + return m.Size() +} +func (m *GitFilesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GitFilesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GitFilesRequest proto.InternalMessageInfo + +func (m *GitFilesRequest) GetRepo() *v1alpha1.Repository { + if m != nil { + return m.Repo + } + return nil +} + +func (m *GitFilesRequest) GetSubmoduleEnabled() bool { + if m != nil { + return m.SubmoduleEnabled + } + return false +} + +func (m *GitFilesRequest) GetRevision() string { + if m != nil { + return m.Revision + } + return "" +} + +func (m *GitFilesRequest) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *GitFilesRequest) GetNewGitFileGlobbingEnabled() bool { + if m != nil { + return m.NewGitFileGlobbingEnabled + } + return false +} + +func (m *GitFilesRequest) GetNoRevisionCache() bool { + if m != nil { + return m.NoRevisionCache + } + return false +} + +type GitFilesResponse struct { + // Map consisting of path of the path to its contents in bytes + Map map[string][]byte `protobuf:"bytes,1,rep,name=map,proto3" json:"map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GitFilesResponse) Reset() { *m = GitFilesResponse{} } +func (m *GitFilesResponse) String() string { return proto.CompactTextString(m) } +func (*GitFilesResponse) ProtoMessage() {} +func (*GitFilesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd8723cfcc820480, []int{28} +} +func (m *GitFilesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GitFilesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GitFilesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GitFilesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GitFilesResponse.Merge(m, src) +} +func (m *GitFilesResponse) XXX_Size() int { + return m.Size() +} +func (m *GitFilesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GitFilesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GitFilesResponse proto.InternalMessageInfo + +func (m *GitFilesResponse) GetMap() map[string][]byte { + if m != nil { + return m.Map + } + return nil +} + +type GitDirectoriesRequest struct { + Repo *v1alpha1.Repository `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` + SubmoduleEnabled bool `protobuf:"varint,2,opt,name=submoduleEnabled,proto3" json:"submoduleEnabled,omitempty"` + Revision string `protobuf:"bytes,3,opt,name=revision,proto3" json:"revision,omitempty"` + NoRevisionCache bool `protobuf:"varint,4,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GitDirectoriesRequest) Reset() { *m = GitDirectoriesRequest{} } +func (m *GitDirectoriesRequest) String() string { return proto.CompactTextString(m) } +func (*GitDirectoriesRequest) ProtoMessage() {} +func (*GitDirectoriesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd8723cfcc820480, []int{29} +} +func (m *GitDirectoriesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GitDirectoriesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GitDirectoriesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GitDirectoriesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GitDirectoriesRequest.Merge(m, src) +} +func (m *GitDirectoriesRequest) XXX_Size() int { + return m.Size() +} +func (m *GitDirectoriesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GitDirectoriesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GitDirectoriesRequest proto.InternalMessageInfo + +func (m *GitDirectoriesRequest) GetRepo() *v1alpha1.Repository { + if m != nil { + return m.Repo + } + return nil +} + +func (m *GitDirectoriesRequest) GetSubmoduleEnabled() bool { + if m != nil { + return m.SubmoduleEnabled + } + return false +} + +func (m *GitDirectoriesRequest) GetRevision() string { + if m != nil { + return m.Revision + } + return "" +} + +func (m *GitDirectoriesRequest) GetNoRevisionCache() bool { + if m != nil { + return m.NoRevisionCache + } + return false +} + +type GitDirectoriesResponse struct { + // A set of directory paths + Paths []string `protobuf:"bytes,1,rep,name=paths,proto3" json:"paths,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GitDirectoriesResponse) Reset() { *m = GitDirectoriesResponse{} } +func (m *GitDirectoriesResponse) String() string { return proto.CompactTextString(m) } +func (*GitDirectoriesResponse) ProtoMessage() {} +func (*GitDirectoriesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd8723cfcc820480, []int{30} +} +func (m *GitDirectoriesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GitDirectoriesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GitDirectoriesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GitDirectoriesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GitDirectoriesResponse.Merge(m, src) +} +func (m *GitDirectoriesResponse) XXX_Size() int { + return m.Size() +} +func (m *GitDirectoriesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GitDirectoriesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GitDirectoriesResponse proto.InternalMessageInfo + +func (m *GitDirectoriesResponse) GetPaths() []string { + if m != nil { + return m.Paths + } + return nil +} + func init() { proto.RegisterType((*ManifestRequest)(nil), "repository.ManifestRequest") proto.RegisterMapType((map[string]bool)(nil), "repository.ManifestRequest.EnabledSourceTypesEntry") @@ -1844,6 +2183,7 @@ func init() { proto.RegisterMapType((map[string]*v1alpha1.RefTarget)(nil), "repository.RepoServerAppDetailsQuery.RefSourcesEntry") proto.RegisterType((*RepoAppDetailsResponse)(nil), "repository.RepoAppDetailsResponse") proto.RegisterType((*RepoServerRevisionMetadataRequest)(nil), "repository.RepoServerRevisionMetadataRequest") + proto.RegisterType((*RepoServerRevisionChartDetailsRequest)(nil), "repository.RepoServerRevisionChartDetailsRequest") proto.RegisterType((*HelmAppSpec)(nil), "repository.HelmAppSpec") proto.RegisterType((*KustomizeAppSpec)(nil), "repository.KustomizeAppSpec") proto.RegisterType((*DirectoryAppSpec)(nil), "repository.DirectoryAppSpec") @@ -1853,6 +2193,11 @@ func init() { proto.RegisterType((*HelmChartsRequest)(nil), "repository.HelmChartsRequest") proto.RegisterType((*HelmChart)(nil), "repository.HelmChart") proto.RegisterType((*HelmChartsResponse)(nil), "repository.HelmChartsResponse") + proto.RegisterType((*GitFilesRequest)(nil), "repository.GitFilesRequest") + proto.RegisterType((*GitFilesResponse)(nil), "repository.GitFilesResponse") + proto.RegisterMapType((map[string][]byte)(nil), "repository.GitFilesResponse.MapEntry") + proto.RegisterType((*GitDirectoriesRequest)(nil), "repository.GitDirectoriesRequest") + proto.RegisterType((*GitDirectoriesResponse)(nil), "repository.GitDirectoriesResponse") } func init() { @@ -1860,124 +2205,140 @@ func init() { } var fileDescriptor_dd8723cfcc820480 = []byte{ - // 1870 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x19, 0xdb, 0x6e, 0x1c, 0x49, - 0xd5, 0x73, 0xb1, 0x3d, 0x73, 0x9c, 0xf8, 0x52, 0x49, 0x9c, 0xce, 0x6c, 0xd6, 0xf2, 0x36, 0x10, - 0x99, 0xcd, 0x6e, 0x8f, 0xe2, 0x68, 0x77, 0x51, 0x16, 0x16, 0x79, 0xbd, 0x49, 0x1c, 0x25, 0x4e, - 0x4c, 0x27, 0x80, 0x16, 0x02, 0xa8, 0xdc, 0x53, 0xd3, 0x53, 0x3b, 0x7d, 0xa9, 0x74, 0x57, 0x0f, - 0x9a, 0x48, 0x3c, 0x20, 0x21, 0x24, 0x7e, 0x00, 0xf1, 0x27, 0x3c, 0xf2, 0xc4, 0xe5, 0x11, 0xf1, - 0x03, 0xa0, 0x7c, 0x09, 0xaa, 0x4b, 0x5f, 0xa7, 0xed, 0x64, 0x35, 0x8e, 0xf7, 0x61, 0x5f, 0xec, - 0xae, 0x53, 0xe7, 0x56, 0xa7, 0xce, 0xb5, 0x06, 0x6e, 0x44, 0x84, 0x85, 0x31, 0x89, 0x26, 0x24, - 0xea, 0xcb, 0x4f, 0xca, 0xc3, 0x68, 0x5a, 0xf8, 0xb4, 0x58, 0x14, 0xf2, 0x10, 0x41, 0x0e, 0xe9, - 0x3d, 0x72, 0x29, 0x1f, 0x25, 0xc7, 0x96, 0x13, 0xfa, 0x7d, 0x1c, 0xb9, 0x21, 0x8b, 0xc2, 0xaf, - 0xe4, 0xc7, 0x87, 0xce, 0xa0, 0x3f, 0xd9, 0xed, 0xb3, 0xb1, 0xdb, 0xc7, 0x8c, 0xc6, 0x7d, 0xcc, - 0x98, 0x47, 0x1d, 0xcc, 0x69, 0x18, 0xf4, 0x27, 0xb7, 0xb0, 0xc7, 0x46, 0xf8, 0x56, 0xdf, 0x25, - 0x01, 0x89, 0x30, 0x27, 0x03, 0xc5, 0xb9, 0xf7, 0x8e, 0x1b, 0x86, 0xae, 0x47, 0xfa, 0x72, 0x75, - 0x9c, 0x0c, 0xfb, 0xc4, 0x67, 0x5c, 0x8b, 0x35, 0xff, 0x72, 0x01, 0xd6, 0x0e, 0x71, 0x40, 0x87, - 0x24, 0xe6, 0x36, 0x79, 0x91, 0x90, 0x98, 0xa3, 0xe7, 0xd0, 0x16, 0xca, 0x18, 0x8d, 0xed, 0xc6, - 0xce, 0xca, 0xee, 0x81, 0x95, 0x6b, 0x63, 0xa5, 0xda, 0xc8, 0x8f, 0xdf, 0x38, 0x03, 0x6b, 0xb2, - 0x6b, 0xb1, 0xb1, 0x6b, 0x09, 0x6d, 0xac, 0x82, 0x36, 0x56, 0xaa, 0x8d, 0x65, 0x67, 0xc7, 0xb2, - 0x25, 0x57, 0xd4, 0x83, 0x4e, 0x44, 0x26, 0x34, 0xa6, 0x61, 0x60, 0x34, 0xb7, 0x1b, 0x3b, 0x5d, - 0x3b, 0x5b, 0x23, 0x03, 0x96, 0x83, 0x70, 0x1f, 0x3b, 0x23, 0x62, 0xb4, 0xb6, 0x1b, 0x3b, 0x1d, - 0x3b, 0x5d, 0xa2, 0x6d, 0x58, 0xc1, 0x8c, 0x3d, 0xc2, 0xc7, 0xc4, 0x7b, 0x48, 0xa6, 0x46, 0x5b, - 0x12, 0x16, 0x41, 0x82, 0x16, 0x33, 0xf6, 0x18, 0xfb, 0xc4, 0x58, 0x94, 0xbb, 0xe9, 0x12, 0x5d, - 0x87, 0x6e, 0x80, 0x7d, 0x12, 0x33, 0xec, 0x10, 0xa3, 0x23, 0xf7, 0x72, 0x00, 0xfa, 0x1d, 0x6c, - 0x14, 0x14, 0x7f, 0x1a, 0x26, 0x91, 0x43, 0x0c, 0x90, 0x47, 0x7f, 0x32, 0xdf, 0xd1, 0xf7, 0xaa, - 0x6c, 0xed, 0x59, 0x49, 0xe8, 0xd7, 0xb0, 0x28, 0x6f, 0xde, 0x58, 0xd9, 0x6e, 0x9d, 0xa9, 0xb5, - 0x15, 0x5b, 0x14, 0xc0, 0x32, 0xf3, 0x12, 0x97, 0x06, 0xb1, 0x71, 0x41, 0x4a, 0x78, 0x36, 0x9f, - 0x84, 0xfd, 0x30, 0x18, 0x52, 0xf7, 0x10, 0x07, 0xd8, 0x25, 0x3e, 0x09, 0xf8, 0x91, 0x64, 0x6e, - 0xa7, 0x42, 0xd0, 0x4b, 0x58, 0x1f, 0x27, 0x31, 0x0f, 0x7d, 0xfa, 0x92, 0x3c, 0x61, 0x82, 0x36, - 0x36, 0x2e, 0x4a, 0x6b, 0x3e, 0x9e, 0x4f, 0xf0, 0xc3, 0x0a, 0x57, 0x7b, 0x46, 0x8e, 0x70, 0x92, - 0x71, 0x72, 0x4c, 0x7e, 0x46, 0x22, 0xe9, 0x5d, 0xab, 0xca, 0x49, 0x0a, 0x20, 0xe5, 0x46, 0x54, - 0xaf, 0x62, 0x63, 0x6d, 0xbb, 0xa5, 0xdc, 0x28, 0x03, 0xa1, 0x1d, 0x58, 0x9b, 0x90, 0x88, 0x0e, - 0xa7, 0x4f, 0xa9, 0x1b, 0x60, 0x9e, 0x44, 0xc4, 0x58, 0x97, 0xae, 0x58, 0x05, 0x23, 0x1f, 0x2e, - 0x8e, 0x88, 0xe7, 0x0b, 0x93, 0xef, 0x47, 0x64, 0x10, 0x1b, 0x1b, 0xd2, 0xbe, 0xf7, 0xe7, 0xbf, - 0x41, 0xc9, 0xce, 0x2e, 0x73, 0x17, 0x8a, 0x05, 0xa1, 0xad, 0x23, 0x45, 0xc5, 0x08, 0x52, 0x8a, - 0x55, 0xc0, 0xe8, 0x06, 0xac, 0xf2, 0x08, 0x3b, 0x63, 0x1a, 0xb8, 0x87, 0x84, 0x8f, 0xc2, 0x81, - 0x71, 0x49, 0x5a, 0xa2, 0x02, 0x45, 0x0e, 0x20, 0x12, 0xe0, 0x63, 0x8f, 0x0c, 0x94, 0x2f, 0x3e, - 0x9b, 0x32, 0x12, 0x1b, 0x97, 0xe5, 0x29, 0x6e, 0x5b, 0x85, 0x0c, 0x55, 0x49, 0x10, 0xd6, 0xdd, - 0x19, 0xaa, 0xbb, 0x01, 0x8f, 0xa6, 0x76, 0x0d, 0x3b, 0x34, 0x86, 0x15, 0x71, 0x8e, 0xd4, 0x15, - 0xae, 0x48, 0x57, 0x78, 0x30, 0x9f, 0x8d, 0x0e, 0x72, 0x86, 0x76, 0x91, 0x3b, 0xb2, 0x00, 0x8d, - 0x70, 0x7c, 0x98, 0x78, 0x9c, 0x32, 0x8f, 0x28, 0x35, 0x62, 0x63, 0x53, 0x9a, 0xa9, 0x66, 0x07, - 0x3d, 0x04, 0x88, 0xc8, 0x30, 0xc5, 0xbb, 0x2a, 0x4f, 0x7e, 0xf3, 0xb4, 0x93, 0xdb, 0x19, 0xb6, - 0x3a, 0x71, 0x81, 0xbc, 0x77, 0x17, 0xae, 0x9e, 0x60, 0x18, 0xb4, 0x0e, 0xad, 0x31, 0x99, 0xca, - 0x84, 0xda, 0xb5, 0xc5, 0x27, 0xba, 0x0c, 0x8b, 0x13, 0xec, 0x25, 0x44, 0xa6, 0xc0, 0x8e, 0xad, - 0x16, 0x77, 0x9a, 0x3f, 0x68, 0xf4, 0xfe, 0xd8, 0x80, 0xb5, 0x8a, 0x98, 0x1a, 0xfa, 0x5f, 0x15, - 0xe9, 0xcf, 0xc0, 0xe9, 0x86, 0xcf, 0x70, 0xe4, 0x12, 0x5e, 0x50, 0xc4, 0xfc, 0x4f, 0x03, 0x8c, - 0xca, 0xf9, 0x7f, 0x4e, 0xf9, 0xe8, 0x1e, 0xf5, 0x48, 0x8c, 0x3e, 0x81, 0xe5, 0x48, 0xc1, 0x74, - 0x99, 0x78, 0xe7, 0x14, 0xb3, 0x1d, 0x2c, 0xd8, 0x29, 0x36, 0xfa, 0x0c, 0x3a, 0x3e, 0xe1, 0x78, - 0x80, 0x39, 0xd6, 0xba, 0x6f, 0xd7, 0x51, 0x0a, 0x29, 0x87, 0x1a, 0xef, 0x60, 0xc1, 0xce, 0x68, - 0xd0, 0x47, 0xb0, 0xe8, 0x8c, 0x92, 0x60, 0x2c, 0x0b, 0xc4, 0xca, 0xee, 0xbb, 0x27, 0x11, 0xef, - 0x0b, 0xa4, 0x83, 0x05, 0x5b, 0x61, 0x7f, 0xbe, 0x04, 0x6d, 0x86, 0x23, 0x6e, 0xde, 0x83, 0xcb, - 0x75, 0x22, 0x44, 0x55, 0x72, 0x46, 0xc4, 0x19, 0xc7, 0x89, 0xaf, 0xcd, 0x9c, 0xad, 0x11, 0x82, - 0x76, 0x4c, 0x5f, 0x2a, 0x53, 0xb7, 0x6c, 0xf9, 0x6d, 0x7e, 0x1f, 0x36, 0x66, 0xa4, 0x89, 0x4b, - 0x55, 0xba, 0x09, 0x0e, 0x17, 0xb4, 0x68, 0x33, 0x81, 0x2b, 0xcf, 0xa4, 0x2d, 0xb2, 0xd4, 0x7c, - 0x1e, 0x75, 0xd6, 0x3c, 0x80, 0xcd, 0xaa, 0xd8, 0x98, 0x85, 0x41, 0x4c, 0x44, 0x94, 0xc8, 0x5c, - 0x46, 0xc9, 0x20, 0xdf, 0x95, 0x5a, 0x74, 0xec, 0x9a, 0x1d, 0xf3, 0xf7, 0x4d, 0xd8, 0xb4, 0x49, - 0x1c, 0x7a, 0x13, 0x92, 0x26, 0x9a, 0xf3, 0x69, 0x15, 0x7e, 0x09, 0x2d, 0xcc, 0x98, 0x76, 0x93, - 0x07, 0x67, 0x56, 0x8c, 0x6d, 0xc1, 0x15, 0x7d, 0x00, 0x1b, 0xd8, 0x3f, 0xa6, 0x6e, 0x12, 0x26, - 0x71, 0x7a, 0x2c, 0xe9, 0x54, 0x5d, 0x7b, 0x76, 0xc3, 0x74, 0xe0, 0xea, 0x8c, 0x09, 0xb4, 0x39, - 0x8b, 0x0d, 0x4d, 0xa3, 0xd2, 0xd0, 0xd4, 0x0a, 0x69, 0x9e, 0x24, 0xe4, 0x1f, 0x0d, 0x58, 0xcf, - 0x43, 0x47, 0xb3, 0xbf, 0x0e, 0x5d, 0x5f, 0xc3, 0x62, 0xa3, 0x21, 0x0b, 0x56, 0x0e, 0x28, 0xf7, - 0x36, 0xcd, 0x6a, 0x6f, 0xb3, 0x09, 0x4b, 0xaa, 0xf5, 0xd4, 0x07, 0xd3, 0xab, 0x92, 0xca, 0xed, - 0x8a, 0xca, 0x5b, 0x00, 0x71, 0x96, 0xbf, 0x8c, 0x25, 0xb9, 0x5b, 0x80, 0x20, 0x13, 0x2e, 0xa8, - 0x4a, 0x68, 0x93, 0x38, 0xf1, 0xb8, 0xb1, 0x2c, 0x31, 0x4a, 0x30, 0x33, 0x84, 0xb5, 0x47, 0x54, - 0x9c, 0x61, 0x18, 0x9f, 0x8f, 0xb3, 0x7f, 0x0c, 0x6d, 0x21, 0x4c, 0x1c, 0xec, 0x38, 0xc2, 0x81, - 0x33, 0x22, 0xa9, 0xad, 0xb2, 0xb5, 0x08, 0x63, 0x8e, 0xdd, 0xd8, 0x68, 0x4a, 0xb8, 0xfc, 0x36, - 0xff, 0xda, 0x54, 0x9a, 0xee, 0x31, 0x16, 0x7f, 0xf3, 0xed, 0x6f, 0x7d, 0x41, 0x6e, 0xcd, 0x16, - 0xe4, 0x8a, 0xca, 0x5f, 0xa7, 0x20, 0x9f, 0x51, 0x99, 0x32, 0x13, 0x58, 0xde, 0x63, 0x4c, 0x28, - 0x82, 0x6e, 0x41, 0x1b, 0x33, 0xa6, 0x0c, 0x5e, 0xc9, 0xc8, 0x1a, 0x45, 0xfc, 0xd7, 0x2a, 0x49, - 0xd4, 0xde, 0x27, 0xd0, 0xcd, 0x40, 0xaf, 0x13, 0xdb, 0x2d, 0x8a, 0xdd, 0x06, 0x50, 0x1d, 0xe7, - 0x83, 0x60, 0x18, 0x8a, 0x2b, 0x15, 0xce, 0xae, 0x49, 0xe5, 0xb7, 0x79, 0x27, 0xc5, 0x90, 0xba, - 0x7d, 0x00, 0x8b, 0x94, 0x13, 0x3f, 0x55, 0x6e, 0xb3, 0xa8, 0x5c, 0xce, 0xc8, 0x56, 0x48, 0xe6, - 0x3f, 0x3b, 0x70, 0x4d, 0xdc, 0xd8, 0x53, 0x19, 0x26, 0x7b, 0x8c, 0x7d, 0x41, 0x38, 0xa6, 0x5e, - 0xfc, 0x93, 0x84, 0x44, 0xd3, 0xb7, 0xec, 0x18, 0x2e, 0x2c, 0xa9, 0x28, 0xd3, 0xf9, 0xee, 0xcc, - 0x87, 0x0f, 0xcd, 0x3e, 0x9f, 0x38, 0x5a, 0x6f, 0x67, 0xe2, 0xa8, 0x9b, 0x00, 0xda, 0xe7, 0x34, - 0x01, 0x9c, 0x3c, 0x04, 0x16, 0x46, 0xcb, 0xa5, 0xf2, 0x68, 0x59, 0xd3, 0x58, 0x2f, 0xbf, 0x69, - 0x63, 0xdd, 0xa9, 0x6d, 0xac, 0xfd, 0xda, 0x38, 0xee, 0x4a, 0x73, 0xff, 0xa8, 0xe8, 0x81, 0x27, - 0xfa, 0xda, 0x3c, 0x2d, 0x36, 0xbc, 0xd5, 0x16, 0xfb, 0xa7, 0xa5, 0x96, 0x59, 0x0d, 0xad, 0x1f, - 0xbd, 0xd9, 0x99, 0xbe, 0x4d, 0xcd, 0xf3, 0x1f, 0x64, 0xcf, 0xc4, 0xc2, 0xdc, 0x06, 0x59, 0x41, - 0x17, 0x75, 0x48, 0x94, 0x56, 0x9d, 0xb4, 0xc4, 0x37, 0xba, 0x09, 0x6d, 0x61, 0x64, 0xdd, 0xd4, - 0x5e, 0x2d, 0xda, 0x53, 0xdc, 0xc4, 0x1e, 0x63, 0x4f, 0x19, 0x71, 0x6c, 0x89, 0x84, 0xee, 0x40, - 0x37, 0x73, 0x7c, 0x1d, 0x59, 0xd7, 0x8b, 0x14, 0x59, 0x9c, 0xa4, 0x64, 0x39, 0xba, 0xa0, 0x1d, - 0xd0, 0x88, 0x38, 0xb2, 0xe5, 0x5b, 0x9c, 0xa5, 0xfd, 0x22, 0xdd, 0xcc, 0x68, 0x33, 0x74, 0x74, - 0x0b, 0x96, 0xd4, 0x94, 0x2f, 0x23, 0x68, 0x65, 0xf7, 0xda, 0x6c, 0x32, 0x4d, 0xa9, 0x34, 0xa2, - 0xf9, 0xf7, 0x06, 0xbc, 0x97, 0x3b, 0x44, 0x1a, 0x4d, 0x69, 0xd7, 0xfd, 0xcd, 0x57, 0xdc, 0x1b, - 0xb0, 0x2a, 0xdb, 0xfc, 0x7c, 0xd8, 0x57, 0xef, 0x4e, 0x15, 0xa8, 0xf9, 0xb7, 0x26, 0xac, 0x14, - 0x2e, 0xa2, 0xae, 0xf0, 0x88, 0xc6, 0x49, 0xde, 0xbf, 0x1c, 0x90, 0x64, 0x72, 0xed, 0xda, 0x05, - 0x08, 0x1a, 0x03, 0x30, 0x1c, 0x61, 0x9f, 0x70, 0x12, 0x89, 0x8c, 0x28, 0x22, 0xe7, 0xe1, 0xfc, - 0x51, 0x7a, 0x94, 0xf2, 0xb4, 0x0b, 0xec, 0x45, 0xe7, 0x27, 0x45, 0xc7, 0x3a, 0x0f, 0xea, 0x15, - 0xfa, 0x2d, 0xac, 0x0e, 0xa9, 0x47, 0x8e, 0x72, 0x45, 0x96, 0xa4, 0x22, 0x4f, 0xe6, 0x57, 0xe4, - 0x5e, 0x91, 0xaf, 0x5d, 0x11, 0x63, 0xbe, 0x0f, 0xeb, 0x55, 0xbf, 0x14, 0x4a, 0x52, 0x1f, 0xbb, - 0x99, 0xb5, 0xf4, 0xca, 0x44, 0xb0, 0x5e, 0xf5, 0x43, 0xf3, 0xbf, 0x4d, 0xb8, 0x92, 0xb1, 0xdb, - 0x0b, 0x82, 0x30, 0x09, 0x1c, 0xf9, 0x00, 0x55, 0x7b, 0x17, 0x97, 0x61, 0x91, 0x53, 0xee, 0x65, - 0x0d, 0x84, 0x5c, 0x88, 0x1a, 0xc0, 0xc3, 0xd0, 0xe3, 0x94, 0xe9, 0x7e, 0x38, 0x5d, 0x2a, 0x1f, - 0x79, 0x91, 0xd0, 0x88, 0x0c, 0x64, 0x44, 0x75, 0xec, 0x6c, 0x2d, 0xf6, 0x44, 0x77, 0x20, 0xdb, - 0x61, 0x65, 0xcc, 0x6c, 0x2d, 0xfd, 0x27, 0xf4, 0x3c, 0xe2, 0x08, 0x73, 0x14, 0x1a, 0xe6, 0x0a, - 0x54, 0x36, 0xe2, 0x3c, 0xa2, 0x81, 0xab, 0xdb, 0x65, 0xbd, 0x12, 0x7a, 0xe2, 0x28, 0xc2, 0x53, - 0xa3, 0x23, 0x0d, 0xa0, 0x16, 0xe8, 0x87, 0xd0, 0xf2, 0x31, 0xd3, 0x05, 0xe3, 0xfd, 0x52, 0x94, - 0xd5, 0x59, 0xc0, 0x3a, 0xc4, 0x4c, 0x65, 0x54, 0x41, 0xd6, 0xfb, 0x18, 0x3a, 0x29, 0xe0, 0x6b, - 0xb5, 0x56, 0x5f, 0xc1, 0xc5, 0x52, 0x10, 0xa3, 0x2f, 0x61, 0x33, 0xf7, 0xa8, 0xa2, 0x40, 0xdd, - 0x4c, 0xbd, 0xf7, 0x5a, 0xcd, 0xec, 0x13, 0x18, 0x98, 0x2f, 0x60, 0x43, 0xb8, 0xcc, 0xfe, 0x08, - 0x47, 0xfc, 0x9c, 0x46, 0x84, 0x4f, 0xa1, 0x9b, 0x89, 0xac, 0xf5, 0x99, 0x1e, 0x74, 0x26, 0xe9, - 0xc3, 0xa0, 0x9a, 0x11, 0xb2, 0xb5, 0xb9, 0x07, 0xa8, 0xa8, 0xaf, 0xce, 0xe4, 0x37, 0xcb, 0xcd, - 0xe5, 0x95, 0x6a, 0xda, 0x96, 0xe8, 0xba, 0xb7, 0xdc, 0xfd, 0xd3, 0x32, 0x6c, 0xe4, 0xa9, 0x50, - 0xfc, 0xa5, 0x0e, 0x41, 0x4f, 0x60, 0xfd, 0xbe, 0x7e, 0xaf, 0x4f, 0x27, 0x3f, 0x74, 0xda, 0x53, - 0x4a, 0xef, 0x7a, 0xfd, 0xa6, 0xd2, 0xc8, 0x5c, 0x40, 0x0e, 0x5c, 0xab, 0x32, 0xcc, 0x5f, 0x6d, - 0xbe, 0x7b, 0x0a, 0xe7, 0x0c, 0xeb, 0x75, 0x22, 0x76, 0x1a, 0xe8, 0x4b, 0x58, 0x2d, 0xbf, 0x2d, - 0xa0, 0x92, 0x2f, 0xd4, 0x3e, 0x77, 0xf4, 0xcc, 0xd3, 0x50, 0x32, 0xfd, 0x9f, 0x8b, 0x02, 0x5e, - 0x1a, 0xb4, 0x91, 0x59, 0x6e, 0x2f, 0xea, 0x1e, 0x22, 0x7a, 0xdf, 0x39, 0x15, 0x27, 0xe3, 0xfe, - 0x29, 0x74, 0xd2, 0xc1, 0xb4, 0x6c, 0xe6, 0xca, 0xb8, 0xda, 0x5b, 0x2f, 0xf3, 0x1b, 0xc6, 0xe6, - 0x02, 0xfa, 0x4c, 0x11, 0x8b, 0xc1, 0x65, 0x96, 0xb8, 0x30, 0x8e, 0xf5, 0x2e, 0xd5, 0x8c, 0x40, - 0xe6, 0x02, 0xfa, 0x31, 0xac, 0x88, 0xaf, 0x23, 0xfd, 0x52, 0xbe, 0x69, 0xa9, 0x1f, 0x66, 0xac, - 0xf4, 0x87, 0x19, 0xeb, 0xae, 0xcf, 0xf8, 0xb4, 0x57, 0x33, 0xa3, 0x68, 0x06, 0xcf, 0xe1, 0xe2, - 0x7d, 0xc2, 0xf3, 0x96, 0x02, 0x7d, 0xef, 0x8d, 0x1a, 0xaf, 0x9e, 0x59, 0x45, 0x9b, 0xed, 0x4a, - 0xcc, 0x05, 0xf4, 0xe7, 0x06, 0x5c, 0xba, 0x4f, 0x78, 0xb5, 0x48, 0xa3, 0x0f, 0xeb, 0x85, 0x9c, - 0x50, 0xcc, 0x7b, 0x8f, 0xe7, 0x8d, 0xdb, 0x32, 0x5b, 0x73, 0x01, 0x1d, 0xc9, 0x63, 0xe7, 0xf1, - 0x87, 0xde, 0xad, 0x0d, 0xb4, 0xcc, 0xfc, 0x5b, 0x27, 0x6d, 0xa7, 0x47, 0xfd, 0x7c, 0xef, 0x5f, - 0xaf, 0xb6, 0x1a, 0xff, 0x7e, 0xb5, 0xd5, 0xf8, 0xdf, 0xab, 0xad, 0xc6, 0x2f, 0x6e, 0xbf, 0xe6, - 0xe7, 0xb6, 0xc2, 0x2f, 0x78, 0x98, 0x51, 0xc7, 0xa3, 0x24, 0xe0, 0xc7, 0x4b, 0xf2, 0xd6, 0x6e, - 0xff, 0x3f, 0x00, 0x00, 0xff, 0xff, 0x46, 0x3e, 0x2d, 0xff, 0xe0, 0x1b, 0x00, 0x00, + // 2127 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x5a, 0x5b, 0x6f, 0x1b, 0xc7, + 0xf5, 0xe7, 0x92, 0x94, 0x44, 0x1e, 0xd9, 0x12, 0x35, 0xd6, 0x65, 0xc5, 0x38, 0x82, 0xb2, 0xff, + 0xbf, 0x0d, 0xd5, 0x4e, 0x48, 0x48, 0x46, 0xe2, 0xc2, 0x49, 0x53, 0x28, 0x8a, 0x2d, 0x39, 0xb6, + 0x6c, 0x75, 0xed, 0xb6, 0x48, 0xeb, 0xb6, 0x18, 0x2e, 0x87, 0xe4, 0x86, 0x7b, 0x19, 0xef, 0xce, + 0x2a, 0x90, 0x81, 0x3e, 0x14, 0x2d, 0xfa, 0x11, 0xfa, 0xd0, 0xaf, 0x51, 0x14, 0x7d, 0xec, 0x53, + 0x2f, 0x8f, 0x41, 0xbf, 0x40, 0x0b, 0xbf, 0x14, 0xe8, 0xa7, 0x28, 0xe6, 0xb2, 0x57, 0xae, 0x64, + 0xa7, 0x94, 0x15, 0xb4, 0x2f, 0xf6, 0xce, 0x99, 0x33, 0xe7, 0x9c, 0x39, 0x73, 0x2e, 0xbf, 0x19, + 0x0a, 0xae, 0x07, 0x84, 0xfa, 0x21, 0x09, 0x8e, 0x49, 0xd0, 0x15, 0x9f, 0x36, 0xf3, 0x83, 0x93, + 0xcc, 0x67, 0x87, 0x06, 0x3e, 0xf3, 0x11, 0xa4, 0x94, 0xf6, 0xc3, 0xa1, 0xcd, 0x46, 0x51, 0xaf, + 0x63, 0xf9, 0x6e, 0x17, 0x07, 0x43, 0x9f, 0x06, 0xfe, 0x17, 0xe2, 0xe3, 0x3d, 0xab, 0xdf, 0x3d, + 0xde, 0xe9, 0xd2, 0xf1, 0xb0, 0x8b, 0xa9, 0x1d, 0x76, 0x31, 0xa5, 0x8e, 0x6d, 0x61, 0x66, 0xfb, + 0x5e, 0xf7, 0x78, 0x1b, 0x3b, 0x74, 0x84, 0xb7, 0xbb, 0x43, 0xe2, 0x91, 0x00, 0x33, 0xd2, 0x97, + 0x92, 0xdb, 0x6f, 0x0d, 0x7d, 0x7f, 0xe8, 0x90, 0xae, 0x18, 0xf5, 0xa2, 0x41, 0x97, 0xb8, 0x94, + 0x29, 0xb5, 0xc6, 0xbf, 0x2e, 0xc1, 0xe2, 0x21, 0xf6, 0xec, 0x01, 0x09, 0x99, 0x49, 0x9e, 0x47, + 0x24, 0x64, 0xe8, 0x19, 0xd4, 0xb9, 0x31, 0xba, 0xb6, 0xa9, 0x6d, 0xcd, 0xef, 0x1c, 0x74, 0x52, + 0x6b, 0x3a, 0xb1, 0x35, 0xe2, 0xe3, 0x67, 0x56, 0xbf, 0x73, 0xbc, 0xd3, 0xa1, 0xe3, 0x61, 0x87, + 0x5b, 0xd3, 0xc9, 0x58, 0xd3, 0x89, 0xad, 0xe9, 0x98, 0xc9, 0xb6, 0x4c, 0x21, 0x15, 0xb5, 0xa1, + 0x11, 0x90, 0x63, 0x3b, 0xb4, 0x7d, 0x4f, 0xaf, 0x6e, 0x6a, 0x5b, 0x4d, 0x33, 0x19, 0x23, 0x1d, + 0xe6, 0x3c, 0x7f, 0x0f, 0x5b, 0x23, 0xa2, 0xd7, 0x36, 0xb5, 0xad, 0x86, 0x19, 0x0f, 0xd1, 0x26, + 0xcc, 0x63, 0x4a, 0x1f, 0xe2, 0x1e, 0x71, 0x1e, 0x90, 0x13, 0xbd, 0x2e, 0x16, 0x66, 0x49, 0x7c, + 0x2d, 0xa6, 0xf4, 0x11, 0x76, 0x89, 0x3e, 0x23, 0x66, 0xe3, 0x21, 0xba, 0x0a, 0x4d, 0x0f, 0xbb, + 0x24, 0xa4, 0xd8, 0x22, 0x7a, 0x43, 0xcc, 0xa5, 0x04, 0xf4, 0x73, 0x58, 0xca, 0x18, 0xfe, 0xc4, + 0x8f, 0x02, 0x8b, 0xe8, 0x20, 0xb6, 0xfe, 0x78, 0xba, 0xad, 0xef, 0x16, 0xc5, 0x9a, 0x93, 0x9a, + 0xd0, 0x4f, 0x61, 0x46, 0x9c, 0xbc, 0x3e, 0xbf, 0x59, 0x3b, 0x57, 0x6f, 0x4b, 0xb1, 0xc8, 0x83, + 0x39, 0xea, 0x44, 0x43, 0xdb, 0x0b, 0xf5, 0x4b, 0x42, 0xc3, 0xd3, 0xe9, 0x34, 0xec, 0xf9, 0xde, + 0xc0, 0x1e, 0x1e, 0x62, 0x0f, 0x0f, 0x89, 0x4b, 0x3c, 0x76, 0x24, 0x84, 0x9b, 0xb1, 0x12, 0xf4, + 0x02, 0x5a, 0xe3, 0x28, 0x64, 0xbe, 0x6b, 0xbf, 0x20, 0x8f, 0x29, 0x5f, 0x1b, 0xea, 0x97, 0x85, + 0x37, 0x1f, 0x4d, 0xa7, 0xf8, 0x41, 0x41, 0xaa, 0x39, 0xa1, 0x87, 0x07, 0xc9, 0x38, 0xea, 0x91, + 0x1f, 0x90, 0x40, 0x44, 0xd7, 0x82, 0x0c, 0x92, 0x0c, 0x49, 0x86, 0x91, 0xad, 0x46, 0xa1, 0xbe, + 0xb8, 0x59, 0x93, 0x61, 0x94, 0x90, 0xd0, 0x16, 0x2c, 0x1e, 0x93, 0xc0, 0x1e, 0x9c, 0x3c, 0xb1, + 0x87, 0x1e, 0x66, 0x51, 0x40, 0xf4, 0x96, 0x08, 0xc5, 0x22, 0x19, 0xb9, 0x70, 0x79, 0x44, 0x1c, + 0x97, 0xbb, 0x7c, 0x2f, 0x20, 0xfd, 0x50, 0x5f, 0x12, 0xfe, 0xdd, 0x9f, 0xfe, 0x04, 0x85, 0x38, + 0x33, 0x2f, 0x9d, 0x1b, 0xe6, 0xf9, 0xa6, 0xca, 0x14, 0x99, 0x23, 0x48, 0x1a, 0x56, 0x20, 0xa3, + 0xeb, 0xb0, 0xc0, 0x02, 0x6c, 0x8d, 0x6d, 0x6f, 0x78, 0x48, 0xd8, 0xc8, 0xef, 0xeb, 0x57, 0x84, + 0x27, 0x0a, 0x54, 0x64, 0x01, 0x22, 0x1e, 0xee, 0x39, 0xa4, 0x2f, 0x63, 0xf1, 0xe9, 0x09, 0x25, + 0xa1, 0xbe, 0x2c, 0x76, 0x71, 0xab, 0x93, 0xa9, 0x50, 0x85, 0x02, 0xd1, 0xb9, 0x3b, 0xb1, 0xea, + 0xae, 0xc7, 0x82, 0x13, 0xb3, 0x44, 0x1c, 0x1a, 0xc3, 0x3c, 0xdf, 0x47, 0x1c, 0x0a, 0x2b, 0x22, + 0x14, 0xee, 0x4f, 0xe7, 0xa3, 0x83, 0x54, 0xa0, 0x99, 0x95, 0x8e, 0x3a, 0x80, 0x46, 0x38, 0x3c, + 0x8c, 0x1c, 0x66, 0x53, 0x87, 0x48, 0x33, 0x42, 0x7d, 0x55, 0xb8, 0xa9, 0x64, 0x06, 0x3d, 0x00, + 0x08, 0xc8, 0x20, 0xe6, 0x5b, 0x13, 0x3b, 0xbf, 0x79, 0xd6, 0xce, 0xcd, 0x84, 0x5b, 0xee, 0x38, + 0xb3, 0x9c, 0x2b, 0xe7, 0xdb, 0x20, 0x16, 0x53, 0xd9, 0x2e, 0xd2, 0x5a, 0x17, 0x21, 0x56, 0x32, + 0xc3, 0x63, 0x51, 0x51, 0x45, 0xd1, 0x5a, 0x97, 0xd1, 0x9a, 0x21, 0xb5, 0xef, 0xc2, 0xda, 0x29, + 0xae, 0x46, 0x2d, 0xa8, 0x8d, 0xc9, 0x89, 0x28, 0xd1, 0x4d, 0x93, 0x7f, 0xa2, 0x65, 0x98, 0x39, + 0xc6, 0x4e, 0x44, 0x44, 0x51, 0x6d, 0x98, 0x72, 0x70, 0xa7, 0xfa, 0x6d, 0xad, 0xfd, 0x6b, 0x0d, + 0x16, 0x0b, 0x86, 0x97, 0xac, 0xff, 0x49, 0x76, 0xfd, 0x39, 0x84, 0xf1, 0xe0, 0x29, 0x0e, 0x86, + 0x84, 0x65, 0x0c, 0x31, 0xfe, 0xa6, 0x81, 0x5e, 0xf0, 0xe8, 0x0f, 0x6d, 0x36, 0xba, 0x67, 0x3b, + 0x24, 0x44, 0xb7, 0x61, 0x2e, 0x90, 0x34, 0xd5, 0x78, 0xde, 0x3a, 0xe3, 0x20, 0x0e, 0x2a, 0x66, + 0xcc, 0x8d, 0x3e, 0x86, 0x86, 0x4b, 0x18, 0xee, 0x63, 0x86, 0x95, 0xed, 0x9b, 0x65, 0x2b, 0xb9, + 0x96, 0x43, 0xc5, 0x77, 0x50, 0x31, 0x93, 0x35, 0xe8, 0x7d, 0x98, 0xb1, 0x46, 0x91, 0x37, 0x16, + 0x2d, 0x67, 0x7e, 0xe7, 0xed, 0xd3, 0x16, 0xef, 0x71, 0xa6, 0x83, 0x8a, 0x29, 0xb9, 0x3f, 0x99, + 0x85, 0x3a, 0xc5, 0x01, 0x33, 0xee, 0xc1, 0x72, 0x99, 0x0a, 0xde, 0xe7, 0xac, 0x11, 0xb1, 0xc6, + 0x61, 0xe4, 0x2a, 0x37, 0x27, 0x63, 0x84, 0xa0, 0x1e, 0xda, 0x2f, 0xa4, 0xab, 0x6b, 0xa6, 0xf8, + 0x36, 0xbe, 0x05, 0x4b, 0x13, 0xda, 0xf8, 0xa1, 0x4a, 0xdb, 0xb8, 0x84, 0x4b, 0x4a, 0xb5, 0x11, + 0xc1, 0xca, 0x53, 0xe1, 0x8b, 0xa4, 0xd8, 0x5f, 0x44, 0xe7, 0x36, 0x0e, 0x60, 0xb5, 0xa8, 0x36, + 0xa4, 0xbe, 0x17, 0x12, 0x1e, 0xfa, 0xa2, 0x3a, 0xda, 0xa4, 0x9f, 0xce, 0x0a, 0x2b, 0x1a, 0x66, + 0xc9, 0x8c, 0xf1, 0x8b, 0x2a, 0xac, 0x9a, 0x24, 0xf4, 0x9d, 0x63, 0x12, 0x97, 0xae, 0x8b, 0x01, + 0x1f, 0x3f, 0x86, 0x1a, 0xa6, 0x54, 0x85, 0xc9, 0xfd, 0x73, 0x6b, 0xef, 0x26, 0x97, 0x8a, 0xde, + 0x85, 0x25, 0xec, 0xf6, 0xec, 0x61, 0xe4, 0x47, 0x61, 0xbc, 0x2d, 0x11, 0x54, 0x4d, 0x73, 0x72, + 0xc2, 0xb0, 0x60, 0x6d, 0xc2, 0x05, 0xca, 0x9d, 0x59, 0x88, 0xa4, 0x15, 0x20, 0x52, 0xa9, 0x92, + 0xea, 0x69, 0x4a, 0xfe, 0xac, 0x41, 0x2b, 0x4d, 0x1d, 0x25, 0xfe, 0x2a, 0x34, 0x5d, 0x45, 0x0b, + 0x75, 0x4d, 0xd4, 0xa7, 0x94, 0x90, 0x47, 0x4b, 0xd5, 0x22, 0x5a, 0x5a, 0x85, 0x59, 0x09, 0x66, + 0xd5, 0xc6, 0xd4, 0x28, 0x67, 0x72, 0xbd, 0x60, 0xf2, 0x06, 0x40, 0x98, 0xd4, 0x2f, 0x7d, 0x56, + 0xcc, 0x66, 0x28, 0xc8, 0x80, 0x4b, 0xb2, 0xb7, 0x9a, 0x24, 0x8c, 0x1c, 0xa6, 0xcf, 0x09, 0x8e, + 0x1c, 0xcd, 0xf0, 0x61, 0xf1, 0xa1, 0xcd, 0xf7, 0x30, 0x08, 0x2f, 0x26, 0xd8, 0x3f, 0x80, 0x3a, + 0x57, 0xc6, 0x37, 0xd6, 0x0b, 0xb0, 0x67, 0x8d, 0x48, 0xec, 0xab, 0x64, 0xcc, 0xd3, 0x98, 0xe1, + 0x61, 0xa8, 0x57, 0x05, 0x5d, 0x7c, 0x1b, 0x7f, 0xa8, 0x4a, 0x4b, 0x77, 0x29, 0x0d, 0xbf, 0x79, + 0x40, 0x5d, 0xde, 0xe2, 0x6b, 0x93, 0x2d, 0xbe, 0x60, 0xf2, 0xd7, 0x69, 0xf1, 0xe7, 0xd4, 0xa6, + 0x8c, 0x08, 0xe6, 0x76, 0x29, 0xe5, 0x86, 0xa0, 0x6d, 0xa8, 0x63, 0x4a, 0xa5, 0xc3, 0x0b, 0x15, + 0x59, 0xb1, 0xf0, 0xff, 0x95, 0x49, 0x82, 0xb5, 0x7d, 0x1b, 0x9a, 0x09, 0xe9, 0x55, 0x6a, 0x9b, + 0x59, 0xb5, 0x9b, 0x00, 0x12, 0xc3, 0xde, 0xf7, 0x06, 0x3e, 0x3f, 0x52, 0x1e, 0xec, 0x6a, 0xa9, + 0xf8, 0x36, 0xee, 0xc4, 0x1c, 0xc2, 0xb6, 0x77, 0x61, 0xc6, 0x66, 0xc4, 0x8d, 0x8d, 0x5b, 0xcd, + 0x1a, 0x97, 0x0a, 0x32, 0x25, 0x93, 0xf1, 0x97, 0x06, 0xac, 0xf3, 0x13, 0x7b, 0x22, 0xd2, 0x64, + 0x97, 0xd2, 0x4f, 0x09, 0xc3, 0xb6, 0x13, 0x7e, 0x2f, 0x22, 0xc1, 0xc9, 0x1b, 0x0e, 0x8c, 0x21, + 0xcc, 0xca, 0x2c, 0x53, 0xf5, 0xee, 0xdc, 0xaf, 0x33, 0x4a, 0x7c, 0x7a, 0x87, 0xa9, 0xbd, 0x99, + 0x3b, 0x4c, 0xd9, 0x9d, 0xa2, 0x7e, 0x41, 0x77, 0x8a, 0xd3, 0xaf, 0x95, 0x99, 0xcb, 0xea, 0x6c, + 0xfe, 0xb2, 0x5a, 0x02, 0xd5, 0xe7, 0x5e, 0x17, 0xaa, 0x37, 0x4a, 0xa1, 0xba, 0x5b, 0x9a, 0xc7, + 0x4d, 0xe1, 0xee, 0xef, 0x64, 0x23, 0xf0, 0xd4, 0x58, 0x9b, 0x06, 0xb4, 0xc3, 0x1b, 0x05, 0xed, + 0xdf, 0xcf, 0x81, 0x70, 0x79, 0x0d, 0x7e, 0xff, 0xf5, 0xf6, 0x74, 0x06, 0x1c, 0xff, 0x9f, 0x03, + 0xcf, 0xbf, 0x12, 0x98, 0x89, 0xfa, 0xa9, 0x0f, 0x92, 0x86, 0xce, 0xfb, 0x10, 0x6f, 0xad, 0xaa, + 0x68, 0xf1, 0x6f, 0x74, 0x13, 0xea, 0xdc, 0xc9, 0x0a, 0xd4, 0xae, 0x65, 0xfd, 0xc9, 0x4f, 0x62, + 0x97, 0xd2, 0x27, 0x94, 0x58, 0xa6, 0x60, 0x42, 0x77, 0xa0, 0x99, 0x04, 0xbe, 0xca, 0xac, 0xab, + 0xd9, 0x15, 0x49, 0x9e, 0xc4, 0xcb, 0x52, 0x76, 0xbe, 0xb6, 0x6f, 0x07, 0xc4, 0x12, 0x90, 0x6f, + 0x66, 0x72, 0xed, 0xa7, 0xf1, 0x64, 0xb2, 0x36, 0x61, 0x47, 0xdb, 0x30, 0x2b, 0xdf, 0x0d, 0x44, + 0x06, 0xcd, 0xef, 0xac, 0x4f, 0x16, 0xd3, 0x78, 0x95, 0x62, 0x34, 0xfe, 0xa4, 0xc1, 0x3b, 0x69, + 0x40, 0xc4, 0xd9, 0x14, 0xa3, 0xee, 0x6f, 0xbe, 0xe3, 0x5e, 0x87, 0x05, 0x01, 0xf3, 0xd3, 0xe7, + 0x03, 0xf9, 0x92, 0x55, 0xa0, 0x1a, 0xbf, 0xd7, 0xe0, 0xda, 0xe4, 0x3e, 0xf6, 0x46, 0x38, 0x60, + 0xc9, 0xf1, 0x5e, 0xc4, 0x5e, 0xe2, 0x86, 0x57, 0x4d, 0x1b, 0x5e, 0x6e, 0x7f, 0xb5, 0xfc, 0xfe, + 0x8c, 0x3f, 0x56, 0x61, 0x3e, 0x13, 0x40, 0x65, 0x0d, 0x93, 0x03, 0x3e, 0x11, 0xb7, 0xe2, 0x62, + 0x27, 0x9a, 0x42, 0xd3, 0xcc, 0x50, 0xd0, 0x18, 0x80, 0xe2, 0x00, 0xbb, 0x84, 0x91, 0x80, 0x57, + 0x72, 0x9e, 0xf1, 0x0f, 0xa6, 0xaf, 0x2e, 0x47, 0xb1, 0x4c, 0x33, 0x23, 0x9e, 0x23, 0x56, 0xa1, + 0x3a, 0x54, 0xf5, 0x5b, 0x8d, 0xd0, 0x97, 0xb0, 0x30, 0xb0, 0x1d, 0x72, 0x94, 0x1a, 0x32, 0x2b, + 0x0c, 0x79, 0x3c, 0xbd, 0x21, 0xf7, 0xb2, 0x72, 0xcd, 0x82, 0x1a, 0xe3, 0x06, 0xb4, 0x8a, 0xf9, + 0xc4, 0x8d, 0xb4, 0x5d, 0x3c, 0x4c, 0xbc, 0xa5, 0x46, 0x06, 0x82, 0x56, 0x31, 0x7f, 0x8c, 0xbf, + 0x57, 0x61, 0x25, 0x11, 0xb7, 0xeb, 0x79, 0x7e, 0xe4, 0x59, 0xe2, 0x29, 0xae, 0xf4, 0x2c, 0x96, + 0x61, 0x86, 0xd9, 0xcc, 0x49, 0x80, 0x8f, 0x18, 0xf0, 0xde, 0xc5, 0x7c, 0xdf, 0x61, 0x36, 0x55, + 0x07, 0x1c, 0x0f, 0xe5, 0xd9, 0x3f, 0x8f, 0xec, 0x80, 0xf4, 0x45, 0x25, 0x68, 0x98, 0xc9, 0x98, + 0xcf, 0x71, 0x54, 0x23, 0x60, 0xbc, 0x74, 0x66, 0x32, 0x16, 0x71, 0xef, 0x3b, 0x0e, 0xb1, 0xb8, + 0x3b, 0x32, 0x40, 0xbf, 0x40, 0x15, 0x17, 0x08, 0x16, 0xd8, 0xde, 0x50, 0xc1, 0x7c, 0x35, 0xe2, + 0x76, 0xe2, 0x20, 0xc0, 0x27, 0x7a, 0x43, 0x38, 0x40, 0x0e, 0xd0, 0x47, 0x50, 0x73, 0x31, 0x55, + 0x8d, 0xee, 0x46, 0xae, 0x3a, 0x94, 0x79, 0xa0, 0x73, 0x88, 0xa9, 0xec, 0x04, 0x7c, 0x59, 0xfb, + 0x03, 0x68, 0xc4, 0x84, 0xaf, 0x05, 0x09, 0xbf, 0x80, 0xcb, 0xb9, 0xe2, 0x83, 0x3e, 0x87, 0xd5, + 0x34, 0xa2, 0xb2, 0x0a, 0x15, 0x08, 0x7c, 0xe7, 0x95, 0x96, 0x99, 0xa7, 0x08, 0x30, 0x9e, 0xc3, + 0x12, 0x0f, 0x19, 0x91, 0xf8, 0x17, 0x74, 0xb5, 0xf9, 0x10, 0x9a, 0x89, 0xca, 0xd2, 0x98, 0x69, + 0x43, 0xe3, 0x38, 0x7e, 0x22, 0x95, 0x77, 0x9b, 0x64, 0x6c, 0xec, 0x02, 0xca, 0xda, 0xab, 0x3a, + 0xd0, 0xcd, 0x3c, 0x28, 0x5e, 0x29, 0xb6, 0x1b, 0xc1, 0x1e, 0x63, 0xe2, 0xdf, 0x55, 0x61, 0x71, + 0xdf, 0x16, 0xaf, 0x1c, 0x17, 0x54, 0xe4, 0x6e, 0x40, 0x2b, 0x8c, 0x7a, 0xae, 0xdf, 0x8f, 0x1c, + 0xa2, 0x40, 0x81, 0xea, 0xf4, 0x13, 0xf4, 0xb3, 0x8a, 0x1f, 0x77, 0x16, 0xc5, 0x6c, 0xa4, 0x6e, + 0xb8, 0xe2, 0x1b, 0x7d, 0x04, 0xeb, 0x8f, 0xc8, 0x97, 0x6a, 0x3f, 0xfb, 0x8e, 0xdf, 0xeb, 0xd9, + 0xde, 0x30, 0x56, 0x32, 0x23, 0x94, 0x9c, 0xce, 0x50, 0x06, 0x15, 0x67, 0x4b, 0xa1, 0xa2, 0xf1, + 0x4b, 0x0d, 0x5a, 0xa9, 0xd7, 0x94, 0xdf, 0x6f, 0xcb, 0xfc, 0x90, 0x5e, 0xbf, 0x96, 0xf5, 0x7a, + 0x91, 0xf5, 0x3f, 0x4f, 0x8d, 0x4b, 0xd9, 0xd4, 0xf8, 0xa7, 0x06, 0x2b, 0xfb, 0x36, 0x8b, 0x8b, + 0x92, 0xfd, 0xdf, 0x76, 0x82, 0x25, 0xfe, 0xae, 0x97, 0xfb, 0xbb, 0x03, 0xab, 0xc5, 0x8d, 0x2a, + 0xa7, 0x2f, 0xc3, 0x0c, 0x3f, 0xf9, 0xf8, 0x3d, 0x40, 0x0e, 0x76, 0xbe, 0x6a, 0xc2, 0x52, 0xda, + 0xd0, 0xf9, 0xbf, 0xb6, 0x45, 0xd0, 0x63, 0x68, 0xed, 0xab, 0xdf, 0xe3, 0xe2, 0x77, 0x18, 0x74, + 0xd6, 0xc3, 0x66, 0xfb, 0x6a, 0xf9, 0xa4, 0x54, 0x6d, 0x54, 0x90, 0x05, 0xeb, 0x45, 0x81, 0xe9, + 0x1b, 0xea, 0xff, 0x9f, 0x21, 0x39, 0xe1, 0x7a, 0x95, 0x8a, 0x2d, 0x0d, 0x7d, 0x0e, 0x0b, 0xf9, + 0x97, 0x3e, 0x94, 0xab, 0x70, 0xa5, 0x8f, 0x8f, 0x6d, 0xe3, 0x2c, 0x96, 0xc4, 0xfe, 0x67, 0x1c, + 0x4e, 0xe7, 0x9e, 0xbd, 0x90, 0x91, 0x07, 0xfb, 0x65, 0xcf, 0x82, 0xed, 0xff, 0x3b, 0x93, 0x27, + 0x91, 0xfe, 0x21, 0x34, 0xe2, 0x67, 0xa2, 0xbc, 0x9b, 0x0b, 0x8f, 0x47, 0xed, 0x56, 0x5e, 0xde, + 0x20, 0x34, 0x2a, 0xe8, 0x63, 0xb9, 0x78, 0x97, 0xd2, 0x92, 0xc5, 0x99, 0xc7, 0x91, 0xf6, 0x95, + 0x92, 0x07, 0x09, 0xa3, 0x82, 0xbe, 0x0b, 0xf3, 0xfc, 0xeb, 0x48, 0xfd, 0x12, 0xb6, 0xda, 0x91, + 0x3f, 0xbc, 0x76, 0xe2, 0x1f, 0x5e, 0x3b, 0x77, 0x5d, 0xca, 0x4e, 0xda, 0x25, 0x2f, 0x06, 0x4a, + 0xc0, 0x33, 0xb8, 0xbc, 0x4f, 0x58, 0x0a, 0xf0, 0xd1, 0xb5, 0xd7, 0xba, 0x06, 0xb5, 0x8d, 0x22, + 0xdb, 0xe4, 0x1d, 0xc1, 0xa8, 0xa0, 0xdf, 0x68, 0x70, 0x65, 0x9f, 0xb0, 0x22, 0x64, 0x46, 0xef, + 0x95, 0x2b, 0x39, 0x05, 0x5a, 0xb7, 0x1f, 0x4d, 0x9b, 0xd9, 0x79, 0xb1, 0x46, 0x05, 0xfd, 0x56, + 0x83, 0xb5, 0x8c, 0x61, 0x59, 0x0c, 0x8c, 0xb6, 0xcf, 0x36, 0xae, 0x04, 0x2f, 0xb7, 0x3f, 0x9b, + 0xf2, 0x07, 0xce, 0x8c, 0x48, 0xa3, 0x82, 0x8e, 0xc4, 0x99, 0xa4, 0x2d, 0x0f, 0xbd, 0x5d, 0xda, + 0xdb, 0x12, 0xed, 0x1b, 0xa7, 0x4d, 0x27, 0xe7, 0xf0, 0x19, 0xcc, 0xef, 0x13, 0x16, 0xd7, 0xe7, + 0x7c, 0xa4, 0x15, 0xda, 0x62, 0x3e, 0x55, 0x8b, 0x25, 0x5d, 0x44, 0xcc, 0x92, 0x94, 0x95, 0xa9, + 0x53, 0xf9, 0x5c, 0x2d, 0x2d, 0xd6, 0xf9, 0x88, 0x29, 0x2f, 0x73, 0x46, 0xe5, 0x93, 0xdd, 0xbf, + 0xbe, 0xdc, 0xd0, 0xbe, 0x7a, 0xb9, 0xa1, 0xfd, 0xe3, 0xe5, 0x86, 0xf6, 0xa3, 0x5b, 0xaf, 0xf8, + 0xab, 0x84, 0xcc, 0x1f, 0x3a, 0x60, 0x6a, 0x5b, 0x8e, 0x4d, 0x3c, 0xd6, 0x9b, 0x15, 0xc1, 0x7f, + 0xeb, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf2, 0x91, 0xe2, 0xd9, 0x07, 0x21, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2010,8 +2371,14 @@ type RepoServerServiceClient interface { GetAppDetails(ctx context.Context, in *RepoServerAppDetailsQuery, opts ...grpc.CallOption) (*RepoAppDetailsResponse, error) // Get the meta-data (author, date, tags, message) for a specific revision of the repo GetRevisionMetadata(ctx context.Context, in *RepoServerRevisionMetadataRequest, opts ...grpc.CallOption) (*v1alpha1.RevisionMetadata, error) + // Get the chart details (author, date, tags, message) for a specific revision of the repo + GetRevisionChartDetails(ctx context.Context, in *RepoServerRevisionChartDetailsRequest, opts ...grpc.CallOption) (*v1alpha1.ChartDetails, error) // GetHelmCharts returns list of helm charts in the specified repository GetHelmCharts(ctx context.Context, in *HelmChartsRequest, opts ...grpc.CallOption) (*HelmChartsResponse, error) + // GetGitFiles returns a set of file paths and their contents for the given repo + GetGitFiles(ctx context.Context, in *GitFilesRequest, opts ...grpc.CallOption) (*GitFilesResponse, error) + // GetGitDirectories returns a set of directory paths for the given repo + GetGitDirectories(ctx context.Context, in *GitDirectoriesRequest, opts ...grpc.CallOption) (*GitDirectoriesResponse, error) } type repoServerServiceClient struct { @@ -2128,6 +2495,15 @@ func (c *repoServerServiceClient) GetRevisionMetadata(ctx context.Context, in *R return out, nil } +func (c *repoServerServiceClient) GetRevisionChartDetails(ctx context.Context, in *RepoServerRevisionChartDetailsRequest, opts ...grpc.CallOption) (*v1alpha1.ChartDetails, error) { + out := new(v1alpha1.ChartDetails) + err := c.cc.Invoke(ctx, "/repository.RepoServerService/GetRevisionChartDetails", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *repoServerServiceClient) GetHelmCharts(ctx context.Context, in *HelmChartsRequest, opts ...grpc.CallOption) (*HelmChartsResponse, error) { out := new(HelmChartsResponse) err := c.cc.Invoke(ctx, "/repository.RepoServerService/GetHelmCharts", in, out, opts...) @@ -2137,6 +2513,24 @@ func (c *repoServerServiceClient) GetHelmCharts(ctx context.Context, in *HelmCha return out, nil } +func (c *repoServerServiceClient) GetGitFiles(ctx context.Context, in *GitFilesRequest, opts ...grpc.CallOption) (*GitFilesResponse, error) { + out := new(GitFilesResponse) + err := c.cc.Invoke(ctx, "/repository.RepoServerService/GetGitFiles", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *repoServerServiceClient) GetGitDirectories(ctx context.Context, in *GitDirectoriesRequest, opts ...grpc.CallOption) (*GitDirectoriesResponse, error) { + out := new(GitDirectoriesResponse) + err := c.cc.Invoke(ctx, "/repository.RepoServerService/GetGitDirectories", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // RepoServerServiceServer is the server API for RepoServerService service. type RepoServerServiceServer interface { // GenerateManifest generates manifest for application in specified repo name and revision @@ -2157,8 +2551,14 @@ type RepoServerServiceServer interface { GetAppDetails(context.Context, *RepoServerAppDetailsQuery) (*RepoAppDetailsResponse, error) // Get the meta-data (author, date, tags, message) for a specific revision of the repo GetRevisionMetadata(context.Context, *RepoServerRevisionMetadataRequest) (*v1alpha1.RevisionMetadata, error) + // Get the chart details (author, date, tags, message) for a specific revision of the repo + GetRevisionChartDetails(context.Context, *RepoServerRevisionChartDetailsRequest) (*v1alpha1.ChartDetails, error) // GetHelmCharts returns list of helm charts in the specified repository GetHelmCharts(context.Context, *HelmChartsRequest) (*HelmChartsResponse, error) + // GetGitFiles returns a set of file paths and their contents for the given repo + GetGitFiles(context.Context, *GitFilesRequest) (*GitFilesResponse, error) + // GetGitDirectories returns a set of directory paths for the given repo + GetGitDirectories(context.Context, *GitDirectoriesRequest) (*GitDirectoriesResponse, error) } // UnimplementedRepoServerServiceServer can be embedded to have forward compatible implementations. @@ -2192,9 +2592,18 @@ func (*UnimplementedRepoServerServiceServer) GetAppDetails(ctx context.Context, func (*UnimplementedRepoServerServiceServer) GetRevisionMetadata(ctx context.Context, req *RepoServerRevisionMetadataRequest) (*v1alpha1.RevisionMetadata, error) { return nil, status.Errorf(codes.Unimplemented, "method GetRevisionMetadata not implemented") } +func (*UnimplementedRepoServerServiceServer) GetRevisionChartDetails(ctx context.Context, req *RepoServerRevisionChartDetailsRequest) (*v1alpha1.ChartDetails, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetRevisionChartDetails not implemented") +} func (*UnimplementedRepoServerServiceServer) GetHelmCharts(ctx context.Context, req *HelmChartsRequest) (*HelmChartsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetHelmCharts not implemented") } +func (*UnimplementedRepoServerServiceServer) GetGitFiles(ctx context.Context, req *GitFilesRequest) (*GitFilesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetGitFiles not implemented") +} +func (*UnimplementedRepoServerServiceServer) GetGitDirectories(ctx context.Context, req *GitDirectoriesRequest) (*GitDirectoriesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetGitDirectories not implemented") +} func RegisterRepoServerServiceServer(s *grpc.Server, srv RepoServerServiceServer) { s.RegisterService(&_RepoServerService_serviceDesc, srv) @@ -2370,6 +2779,24 @@ func _RepoServerService_GetRevisionMetadata_Handler(srv interface{}, ctx context return interceptor(ctx, in, info, handler) } +func _RepoServerService_GetRevisionChartDetails_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RepoServerRevisionChartDetailsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RepoServerServiceServer).GetRevisionChartDetails(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/repository.RepoServerService/GetRevisionChartDetails", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RepoServerServiceServer).GetRevisionChartDetails(ctx, req.(*RepoServerRevisionChartDetailsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _RepoServerService_GetHelmCharts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(HelmChartsRequest) if err := dec(in); err != nil { @@ -2388,6 +2815,42 @@ func _RepoServerService_GetHelmCharts_Handler(srv interface{}, ctx context.Conte return interceptor(ctx, in, info, handler) } +func _RepoServerService_GetGitFiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GitFilesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RepoServerServiceServer).GetGitFiles(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/repository.RepoServerService/GetGitFiles", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RepoServerServiceServer).GetGitFiles(ctx, req.(*GitFilesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RepoServerService_GetGitDirectories_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GitDirectoriesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RepoServerServiceServer).GetGitDirectories(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/repository.RepoServerService/GetGitDirectories", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RepoServerServiceServer).GetGitDirectories(ctx, req.(*GitDirectoriesRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _RepoServerService_serviceDesc = grpc.ServiceDesc{ ServiceName: "repository.RepoServerService", HandlerType: (*RepoServerServiceServer)(nil), @@ -2424,10 +2887,22 @@ var _RepoServerService_serviceDesc = grpc.ServiceDesc{ MethodName: "GetRevisionMetadata", Handler: _RepoServerService_GetRevisionMetadata_Handler, }, + { + MethodName: "GetRevisionChartDetails", + Handler: _RepoServerService_GetRevisionChartDetails_Handler, + }, { MethodName: "GetHelmCharts", Handler: _RepoServerService_GetHelmCharts_Handler, }, + { + MethodName: "GetGitFiles", + Handler: _RepoServerService_GetGitFiles_Handler, + }, + { + MethodName: "GetGitDirectories", + Handler: _RepoServerService_GetGitDirectories_Handler, + }, }, Streams: []grpc.StreamDesc{ { @@ -2463,6 +2938,26 @@ func (m *ManifestRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.ProjectName) > 0 { + i -= len(m.ProjectName) + copy(dAtA[i:], m.ProjectName) + i = encodeVarintRepository(dAtA, i, uint64(len(m.ProjectName))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xca + } + if len(m.ProjectSourceRepos) > 0 { + for iNdEx := len(m.ProjectSourceRepos) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ProjectSourceRepos[iNdEx]) + copy(dAtA[i:], m.ProjectSourceRepos[iNdEx]) + i = encodeVarintRepository(dAtA, i, uint64(len(m.ProjectSourceRepos[iNdEx]))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc2 + } + } if len(m.RefSources) > 0 { for k := range m.RefSources { v := m.RefSources[k] @@ -3711,6 +4206,59 @@ func (m *RepoServerRevisionMetadataRequest) MarshalToSizedBuffer(dAtA []byte) (i return len(dAtA) - i, nil } +func (m *RepoServerRevisionChartDetailsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RepoServerRevisionChartDetailsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RepoServerRevisionChartDetailsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Revision) > 0 { + i -= len(m.Revision) + copy(dAtA[i:], m.Revision) + i = encodeVarintRepository(dAtA, i, uint64(len(m.Revision))) + i-- + dAtA[i] = 0x1a + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintRepository(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Repo != nil { + { + size, err := m.Repo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRepository(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *HelmAppSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -4123,98 +4671,331 @@ func (m *HelmChartsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func encodeVarintRepository(dAtA []byte, offset int, v uint64) int { - offset -= sovRepository(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ +func (m *GitFilesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - dAtA[offset] = uint8(v) - return base + return dAtA[:n], nil } -func (m *ManifestRequest) Size() (n int) { - if m == nil { - return 0 - } + +func (m *GitFilesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GitFilesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.Repo != nil { - l = m.Repo.Size() - n += 1 + l + sovRepository(uint64(l)) - } - l = len(m.Revision) - if l > 0 { - n += 1 + l + sovRepository(uint64(l)) - } - if m.NoCache { - n += 2 - } - l = len(m.AppLabelKey) - if l > 0 { - n += 1 + l + sovRepository(uint64(l)) - } - l = len(m.AppName) - if l > 0 { - n += 1 + l + sovRepository(uint64(l)) - } - l = len(m.Namespace) - if l > 0 { - n += 1 + l + sovRepository(uint64(l)) - } - if m.ApplicationSource != nil { - l = m.ApplicationSource.Size() - n += 1 + l + sovRepository(uint64(l)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Repos) > 0 { - for _, e := range m.Repos { - l = e.Size() - n += 1 + l + sovRepository(uint64(l)) + if m.NoRevisionCache { + i-- + if m.NoRevisionCache { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } + i-- + dAtA[i] = 0x30 } - if len(m.Plugins) > 0 { - for _, e := range m.Plugins { - l = e.Size() - n += 1 + l + sovRepository(uint64(l)) + if m.NewGitFileGlobbingEnabled { + i-- + if m.NewGitFileGlobbingEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } + i-- + dAtA[i] = 0x28 } - if m.KustomizeOptions != nil { - l = m.KustomizeOptions.Size() - n += 1 + l + sovRepository(uint64(l)) + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintRepository(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x22 } - l = len(m.KubeVersion) - if l > 0 { - n += 1 + l + sovRepository(uint64(l)) + if len(m.Revision) > 0 { + i -= len(m.Revision) + copy(dAtA[i:], m.Revision) + i = encodeVarintRepository(dAtA, i, uint64(len(m.Revision))) + i-- + dAtA[i] = 0x1a } - if len(m.ApiVersions) > 0 { - for _, s := range m.ApiVersions { - l = len(s) - n += 1 + l + sovRepository(uint64(l)) + if m.SubmoduleEnabled { + i-- + if m.SubmoduleEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } + i-- + dAtA[i] = 0x10 } - if m.VerifySignature { - n += 3 - } - if len(m.HelmRepoCreds) > 0 { - for _, e := range m.HelmRepoCreds { - l = e.Size() - n += 2 + l + sovRepository(uint64(l)) + if m.Repo != nil { + { + size, err := m.Repo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRepository(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa } - if m.NoRevisionCache { - n += 3 + return len(dAtA) - i, nil +} + +func (m *GitFilesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - l = len(m.TrackingMethod) - if l > 0 { - n += 2 + l + sovRepository(uint64(l)) + return dAtA[:n], nil +} + +func (m *GitFilesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GitFilesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.EnabledSourceTypes) > 0 { - for k, v := range m.EnabledSourceTypes { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovRepository(uint64(len(k))) + 1 + 1 - n += mapEntrySize + 2 + sovRepository(uint64(mapEntrySize)) + if len(m.Map) > 0 { + for k := range m.Map { + v := m.Map[k] + baseI := i + if len(v) > 0 { + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintRepository(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + } + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintRepository(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintRepository(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *GitDirectoriesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GitDirectoriesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GitDirectoriesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.NoRevisionCache { + i-- + if m.NoRevisionCache { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if len(m.Revision) > 0 { + i -= len(m.Revision) + copy(dAtA[i:], m.Revision) + i = encodeVarintRepository(dAtA, i, uint64(len(m.Revision))) + i-- + dAtA[i] = 0x1a + } + if m.SubmoduleEnabled { + i-- + if m.SubmoduleEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Repo != nil { + { + size, err := m.Repo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRepository(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GitDirectoriesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GitDirectoriesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GitDirectoriesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Paths) > 0 { + for iNdEx := len(m.Paths) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Paths[iNdEx]) + copy(dAtA[i:], m.Paths[iNdEx]) + i = encodeVarintRepository(dAtA, i, uint64(len(m.Paths[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintRepository(dAtA []byte, offset int, v uint64) int { + offset -= sovRepository(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ManifestRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Repo != nil { + l = m.Repo.Size() + n += 1 + l + sovRepository(uint64(l)) + } + l = len(m.Revision) + if l > 0 { + n += 1 + l + sovRepository(uint64(l)) + } + if m.NoCache { + n += 2 + } + l = len(m.AppLabelKey) + if l > 0 { + n += 1 + l + sovRepository(uint64(l)) + } + l = len(m.AppName) + if l > 0 { + n += 1 + l + sovRepository(uint64(l)) + } + l = len(m.Namespace) + if l > 0 { + n += 1 + l + sovRepository(uint64(l)) + } + if m.ApplicationSource != nil { + l = m.ApplicationSource.Size() + n += 1 + l + sovRepository(uint64(l)) + } + if len(m.Repos) > 0 { + for _, e := range m.Repos { + l = e.Size() + n += 1 + l + sovRepository(uint64(l)) + } + } + if len(m.Plugins) > 0 { + for _, e := range m.Plugins { + l = e.Size() + n += 1 + l + sovRepository(uint64(l)) + } + } + if m.KustomizeOptions != nil { + l = m.KustomizeOptions.Size() + n += 1 + l + sovRepository(uint64(l)) + } + l = len(m.KubeVersion) + if l > 0 { + n += 1 + l + sovRepository(uint64(l)) + } + if len(m.ApiVersions) > 0 { + for _, s := range m.ApiVersions { + l = len(s) + n += 1 + l + sovRepository(uint64(l)) + } + } + if m.VerifySignature { + n += 3 + } + if len(m.HelmRepoCreds) > 0 { + for _, e := range m.HelmRepoCreds { + l = e.Size() + n += 2 + l + sovRepository(uint64(l)) + } + } + if m.NoRevisionCache { + n += 3 + } + l = len(m.TrackingMethod) + if l > 0 { + n += 2 + l + sovRepository(uint64(l)) + } + if len(m.EnabledSourceTypes) > 0 { + for k, v := range m.EnabledSourceTypes { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovRepository(uint64(len(k))) + 1 + 1 + n += mapEntrySize + 2 + sovRepository(uint64(mapEntrySize)) } } if m.HelmOptions != nil { @@ -4237,6 +5018,16 @@ func (m *ManifestRequest) Size() (n int) { n += mapEntrySize + 2 + sovRepository(uint64(mapEntrySize)) } } + if len(m.ProjectSourceRepos) > 0 { + for _, s := range m.ProjectSourceRepos { + l = len(s) + n += 2 + l + sovRepository(uint64(l)) + } + } + l = len(m.ProjectName) + if l > 0 { + n += 2 + l + sovRepository(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -4688,6 +5479,30 @@ func (m *RepoServerRevisionMetadataRequest) Size() (n int) { return n } +func (m *RepoServerRevisionChartDetailsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Repo != nil { + l = m.Repo.Size() + n += 1 + l + sovRepository(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovRepository(uint64(l)) + } + l = len(m.Revision) + if l > 0 { + n += 1 + l + sovRepository(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *HelmAppSpec) Size() (n int) { if m == nil { return 0 @@ -4883,31 +5698,132 @@ func (m *HelmChartsResponse) Size() (n int) { return n } -func sovRepository(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozRepository(x uint64) (n int) { - return sovRepository(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *ManifestRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRepository - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } +func (m *GitFilesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Repo != nil { + l = m.Repo.Size() + n += 1 + l + sovRepository(uint64(l)) + } + if m.SubmoduleEnabled { + n += 2 + } + l = len(m.Revision) + if l > 0 { + n += 1 + l + sovRepository(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + sovRepository(uint64(l)) + } + if m.NewGitFileGlobbingEnabled { + n += 2 + } + if m.NoRevisionCache { + n += 2 + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *GitFilesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Map) > 0 { + for k, v := range m.Map { + _ = k + _ = v + l = 0 + if len(v) > 0 { + l = 1 + len(v) + sovRepository(uint64(len(v))) + } + mapEntrySize := 1 + len(k) + sovRepository(uint64(len(k))) + l + n += mapEntrySize + 1 + sovRepository(uint64(mapEntrySize)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *GitDirectoriesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Repo != nil { + l = m.Repo.Size() + n += 1 + l + sovRepository(uint64(l)) + } + if m.SubmoduleEnabled { + n += 2 + } + l = len(m.Revision) + if l > 0 { + n += 1 + l + sovRepository(uint64(l)) + } + if m.NoRevisionCache { + n += 2 + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *GitDirectoriesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Paths) > 0 { + for _, s := range m.Paths { + l = len(s) + n += 1 + l + sovRepository(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovRepository(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozRepository(x uint64) (n int) { + return sovRepository(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ManifestRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) @@ -5712,6 +6628,70 @@ func (m *ManifestRequest) Unmarshal(dAtA []byte) error { } m.RefSources[mapkey] = mapvalue iNdEx = postIndex + case 24: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProjectSourceRepos", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProjectSourceRepos = append(m.ProjectSourceRepos, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 25: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProjectName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProjectName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) @@ -8473,7 +9453,7 @@ func (m *RepoServerRevisionMetadataRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *HelmAppSpec) Unmarshal(dAtA []byte) error { +func (m *RepoServerRevisionChartDetailsRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -8496,17 +9476,17 @@ func (m *HelmAppSpec) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: HelmAppSpec: wiretype end group for non-group") + return fmt.Errorf("proto: RepoServerRevisionChartDetailsRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: HelmAppSpec: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RepoServerRevisionChartDetailsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Repo", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowRepository @@ -8516,27 +9496,31 @@ func (m *HelmAppSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthRepository } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthRepository } if postIndex > l { return io.ErrUnexpectedEOF } - m.Name = string(dAtA[iNdEx:postIndex]) + if m.Repo == nil { + m.Repo = &v1alpha1.Repository{} + } + if err := m.Repo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 3: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ValueFiles", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -8564,45 +9548,11 @@ func (m *HelmAppSpec) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ValueFiles = append(m.ValueFiles, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Parameters", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRepository - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthRepository - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthRepository - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Parameters = append(m.Parameters, &v1alpha1.HelmParameter{}) - if err := m.Parameters[len(m.Parameters)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Values", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -8630,21 +9580,202 @@ func (m *HelmAppSpec) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Values = string(dAtA[iNdEx:postIndex]) + m.Revision = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FileParameters", wireType) + default: + iNdEx = preIndex + skippy, err := skipRepository(dAtA[iNdEx:]) + if err != nil { + return err } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRepository - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRepository + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HelmAppSpec) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HelmAppSpec: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HelmAppSpec: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValueFiles", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValueFiles = append(m.ValueFiles, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Parameters", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Parameters = append(m.Parameters, &v1alpha1.HelmParameter{}) + if err := m.Parameters[len(m.Parameters)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Values", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Values = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FileParameters", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { @@ -9616,6 +10747,638 @@ func (m *HelmChartsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *GitFilesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GitFilesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GitFilesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Repo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Repo == nil { + m.Repo = &v1alpha1.Repository{} + } + if err := m.Repo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubmoduleEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.SubmoduleEnabled = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Revision = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NewGitFileGlobbingEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.NewGitFileGlobbingEnabled = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NoRevisionCache", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.NoRevisionCache = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipRepository(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRepository + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GitFilesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GitFilesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GitFilesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Map", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Map == nil { + m.Map = make(map[string][]byte) + } + var mapkey string + mapvalue := []byte{} + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthRepository + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthRepository + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapbyteLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapbyteLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intMapbyteLen := int(mapbyteLen) + if intMapbyteLen < 0 { + return ErrInvalidLengthRepository + } + postbytesIndex := iNdEx + intMapbyteLen + if postbytesIndex < 0 { + return ErrInvalidLengthRepository + } + if postbytesIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = make([]byte, mapbyteLen) + copy(mapvalue, dAtA[iNdEx:postbytesIndex]) + iNdEx = postbytesIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipRepository(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRepository + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Map[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipRepository(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRepository + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GitDirectoriesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GitDirectoriesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GitDirectoriesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Repo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Repo == nil { + m.Repo = &v1alpha1.Repository{} + } + if err := m.Repo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubmoduleEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.SubmoduleEnabled = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Revision = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NoRevisionCache", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.NoRevisionCache = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipRepository(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRepository + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GitDirectoriesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GitDirectoriesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GitDirectoriesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Paths", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRepository + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRepository + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Paths = append(m.Paths, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipRepository(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRepository + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipRepository(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/reposerver/cache/cache.go b/reposerver/cache/cache.go index 61b4ff6abd85b..5b15299660ad4 100644 --- a/reposerver/cache/cache.go +++ b/reposerver/cache/cache.go @@ -12,7 +12,6 @@ import ( "github.com/argoproj/gitops-engine/pkg/utils/text" "github.com/go-git/go-git/v5/plumbing" - "github.com/go-redis/redis/v8" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -25,11 +24,13 @@ import ( ) var ErrCacheMiss = cacheutil.ErrCacheMiss +var ErrCacheKeyLocked = cacheutil.ErrCacheKeyLocked type Cache struct { - cache *cacheutil.Cache - repoCacheExpiration time.Duration - revisionCacheExpiration time.Duration + cache *cacheutil.Cache + repoCacheExpiration time.Duration + revisionCacheExpiration time.Duration + revisionCacheLockTimeout time.Duration } // ClusterRuntimeInfo holds cluster runtime information @@ -40,25 +41,27 @@ type ClusterRuntimeInfo interface { GetKubeVersion() string } -func NewCache(cache *cacheutil.Cache, repoCacheExpiration time.Duration, revisionCacheExpiration time.Duration) *Cache { - return &Cache{cache, repoCacheExpiration, revisionCacheExpiration} +func NewCache(cache *cacheutil.Cache, repoCacheExpiration time.Duration, revisionCacheExpiration time.Duration, revisionCacheLockTimeout time.Duration) *Cache { + return &Cache{cache, repoCacheExpiration, revisionCacheExpiration, revisionCacheLockTimeout} } -func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...func(client *redis.Client)) func() (*Cache, error) { +func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...cacheutil.Options) func() (*Cache, error) { var repoCacheExpiration time.Duration var revisionCacheExpiration time.Duration + var revisionCacheLockTimeout time.Duration cmd.Flags().DurationVar(&repoCacheExpiration, "repo-cache-expiration", env.ParseDurationFromEnv("ARGOCD_REPO_CACHE_EXPIRATION", 24*time.Hour, 0, math.MaxInt64), "Cache expiration for repo state, incl. app lists, app details, manifest generation, revision meta-data") cmd.Flags().DurationVar(&revisionCacheExpiration, "revision-cache-expiration", env.ParseDurationFromEnv("ARGOCD_RECONCILIATION_TIMEOUT", 3*time.Minute, 0, math.MaxInt64), "Cache expiration for cached revision") + cmd.Flags().DurationVar(&revisionCacheLockTimeout, "revision-cache-lock-timeout", env.ParseDurationFromEnv("ARGOCD_REVISION_CACHE_LOCK_TIMEOUT", 10*time.Second, 0, math.MaxInt64), "Cache TTL for locks to prevent duplicate requests on revisions, set to 0 to disable") repoFactory := cacheutil.AddCacheFlagsToCmd(cmd, opts...) return func() (*Cache, error) { cache, err := repoFactory() if err != nil { - return nil, err + return nil, fmt.Errorf("error adding cache flags to cmd: %w", err) } - return NewCache(cache, repoCacheExpiration, revisionCacheExpiration), nil + return NewCache(cache, repoCacheExpiration, revisionCacheExpiration, revisionCacheLockTimeout), nil } } @@ -88,24 +91,31 @@ func getRefTargetRevisionMappingForCacheKey(refTargetRevisionMapping appv1.RefTa return res } -func appSourceKey(appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping) uint32 { - return hash.FNVa(appSourceKeyJSON(appSrc, srcRefs)) +func appSourceKey(appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, refSourceCommitSHAs ResolvedRevisions) uint32 { + return hash.FNVa(appSourceKeyJSON(appSrc, srcRefs, refSourceCommitSHAs)) } +// ResolvedRevisions is a map of "normalized git URL" -> "git commit SHA". When one source references another source, +// the referenced source revision may change, for example, when someone pushes a commit to the referenced branch. This +// map lets us keep track of the current revision for each referenced source. +type ResolvedRevisions map[string]string + type appSourceKeyStruct struct { - AppSrc *appv1.ApplicationSource `json:"appSrc"` - SrcRefs refTargetRevisionMappingForCacheKey `json:"srcRefs"` + AppSrc *appv1.ApplicationSource `json:"appSrc"` + SrcRefs refTargetRevisionMappingForCacheKey `json:"srcRefs"` + ResolvedRevisions ResolvedRevisions `json:"resolvedRevisions,omitempty"` } -func appSourceKeyJSON(appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping) string { +func appSourceKeyJSON(appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, refSourceCommitSHAs ResolvedRevisions) string { appSrc = appSrc.DeepCopy() if !appSrc.IsHelm() { appSrc.RepoURL = "" // superseded by commitSHA appSrc.TargetRevision = "" // superseded by commitSHA } appSrcStr, _ := json.Marshal(appSourceKeyStruct{ - AppSrc: appSrc, - SrcRefs: getRefTargetRevisionMappingForCacheKey(srcRefs), + AppSrc: appSrc, + SrcRefs: getRefTargetRevisionMappingForCacheKey(srcRefs), + ResolvedRevisions: refSourceCommitSHAs, }) return string(appSrcStr) } @@ -139,7 +149,12 @@ func (c *Cache) ListApps(repoUrl, revision string) (map[string]string, error) { } func (c *Cache) SetApps(repoUrl, revision string, apps map[string]string) error { - return c.cache.SetItem(listApps(repoUrl, revision), apps, c.repoCacheExpiration, apps == nil) + return c.cache.SetItem( + listApps(repoUrl, revision), + apps, + &cacheutil.CacheActionOpts{ + Expiration: c.repoCacheExpiration, + Delete: apps == nil}) } func helmIndexRefsKey(repo string) string { @@ -148,7 +163,14 @@ func helmIndexRefsKey(repo string) string { // SetHelmIndex stores helm repository index.yaml content to cache func (c *Cache) SetHelmIndex(repo string, indexData []byte) error { - return c.cache.SetItem(helmIndexRefsKey(repo), indexData, c.revisionCacheExpiration, false) + if indexData == nil { + // Logged as warning upstream + return fmt.Errorf("helm index data is nil, skipping cache") + } + return c.cache.SetItem( + helmIndexRefsKey(repo), + indexData, + &cacheutil.CacheActionOpts{Expiration: c.revisionCacheExpiration}) } // GetHelmIndex retrieves helm repository index.yaml content from cache @@ -166,26 +188,110 @@ func (c *Cache) SetGitReferences(repo string, references []*plumbing.Reference) for i := range references { input = append(input, references[i].Strings()) } - return c.cache.SetItem(gitRefsKey(repo), input, c.revisionCacheExpiration, false) + return c.cache.SetItem(gitRefsKey(repo), input, &cacheutil.CacheActionOpts{Expiration: c.revisionCacheExpiration}) } -// GetGitReferences retrieves resolved Git repository references from cache -func (c *Cache) GetGitReferences(repo string, references *[]*plumbing.Reference) error { +// Converts raw cache items to plumbing.Reference objects +func GitRefCacheItemToReferences(cacheItem [][2]string) *[]*plumbing.Reference { + var res []*plumbing.Reference + for i := range cacheItem { + // Skip empty data + if cacheItem[i][0] != "" || cacheItem[i][1] != "" { + res = append(res, plumbing.NewReferenceFromStrings(cacheItem[i][0], cacheItem[i][1])) + } + } + return &res +} + +// TryLockGitRefCache attempts to lock the key for the Git repository references if the key doesn't exist, returns the value of +// GetGitReferences after calling the SET +func (c *Cache) TryLockGitRefCache(repo string, lockId string, references *[]*plumbing.Reference) (string, error) { + // This try set with DisableOverwrite is important for making sure that only one process is able to claim ownership + // A normal get + set, or just set would cause ownership to go to whoever the last writer was, and during race conditions + // leads to duplicate requests + err := c.cache.SetItem(gitRefsKey(repo), [][2]string{{cacheutil.CacheLockedValue, lockId}}, &cacheutil.CacheActionOpts{ + Expiration: c.revisionCacheLockTimeout, + DisableOverwrite: true}) + if err != nil { + // Log but ignore this error since we'll want to retry, failing to obtain the lock should not throw an error + log.Errorf("Error attempting to acquire git references cache lock: %v", err) + } + return c.GetGitReferences(repo, references) +} + +// Retrieves the cache item for git repo references. Returns foundLockId, error +func (c *Cache) GetGitReferences(repo string, references *[]*plumbing.Reference) (string, error) { var input [][2]string - if err := c.cache.GetItem(gitRefsKey(repo), &input); err != nil { - return err + err := c.cache.GetItem(gitRefsKey(repo), &input) + valueExists := len(input) > 0 && len(input[0]) > 0 + switch { + // Unexpected Error + case err != nil && err != ErrCacheMiss: + log.Errorf("Error attempting to retrieve git references from cache: %v", err) + return "", err + // Value is set + case valueExists && input[0][0] != cacheutil.CacheLockedValue: + *references = *GitRefCacheItemToReferences(input) + return "", nil + // Key is locked + case valueExists: + return input[0][1], nil + // No key or empty key + default: + return "", nil } - var res []*plumbing.Reference - for i := range input { - res = append(res, plumbing.NewReferenceFromStrings(input[i][0], input[i][1])) +} + +// GetOrLockGitReferences retrieves the git references if they exist, otherwise creates a lock and returns so the caller can populate the cache +// Returns isLockOwner, localLockId, error +func (c *Cache) GetOrLockGitReferences(repo string, lockId string, references *[]*plumbing.Reference) (string, error) { + // Value matches the ttl on the lock in TryLockGitRefCache + waitUntil := time.Now().Add(c.revisionCacheLockTimeout) + // Wait only the maximum amount of time configured for the lock + // if the configured time is zero then the for loop will never run and instead act as the owner immediately + for time.Now().Before(waitUntil) { + // Get current cache state + if foundLockId, err := c.GetGitReferences(repo, references); foundLockId == lockId || err != nil || (references != nil && len(*references) > 0) { + return foundLockId, err + } + if foundLockId, err := c.TryLockGitRefCache(repo, lockId, references); foundLockId == lockId || err != nil || (references != nil && len(*references) > 0) { + return foundLockId, err + } + time.Sleep(1 * time.Second) } - *references = res - return nil + // If configured time is 0 then this is expected + if c.revisionCacheLockTimeout > 0 { + log.Debug("Repository cache was unable to acquire lock or valid data within timeout") + } + // Timeout waiting for lock + return lockId, nil +} + +// UnlockGitReferences unlocks the key for the Git repository references if needed +func (c *Cache) UnlockGitReferences(repo string, lockId string) error { + var input [][2]string + var err error + if err = c.cache.GetItem(gitRefsKey(repo), &input); err == nil && + input != nil && + len(input) > 0 && + len(input[0]) > 1 && + input[0][0] == cacheutil.CacheLockedValue && + input[0][1] == lockId { + // We have the lock, so remove it + return c.cache.SetItem(gitRefsKey(repo), input, &cacheutil.CacheActionOpts{Delete: true}) + } + return err } -func manifestCacheKey(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, namespace string, trackingMethod string, appLabelKey string, appName string, info ClusterRuntimeInfo) string { +// refSourceCommitSHAs is a list of resolved revisions for each ref source. This allows us to invalidate the cache +// when someone pushes a commit to a source which is referenced from the main source (the one referred to by `revision`). +func manifestCacheKey(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, namespace string, trackingMethod string, appLabelKey string, appName string, info ClusterRuntimeInfo, refSourceCommitSHAs ResolvedRevisions) string { + // TODO: this function is getting unwieldy. We should probably consolidate some of this stuff into a struct. For + // example, revision could be part of ResolvedRevisions. And srcRefs is probably redundant now that + // refSourceCommitSHAs has been added. We don't need to know the _target_ revisions of the referenced sources + // when the _resolved_ revisions are already part of the key. trackingKey := trackingKey(appLabelKey, trackingMethod) - return fmt.Sprintf("mfst|%s|%s|%s|%s|%d", trackingKey, appName, revision, namespace, appSourceKey(appSrc, srcRefs)+clusterRuntimeInfoKey(info)) + return fmt.Sprintf("mfst|%s|%s|%s|%s|%d", trackingKey, appName, revision, namespace, appSourceKey(appSrc, srcRefs, refSourceCommitSHAs)+clusterRuntimeInfoKey(info)) } func trackingKey(appLabelKey string, trackingMethod string) string { @@ -198,11 +304,11 @@ func trackingKey(appLabelKey string, trackingMethod string) string { // LogDebugManifestCacheKeyFields logs all the information included in a manifest cache key. It's intended to be run // before every manifest cache operation to help debug cache misses. -func LogDebugManifestCacheKeyFields(message string, reason string, revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string) { +func LogDebugManifestCacheKeyFields(message string, reason string, revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, refSourceCommitSHAs ResolvedRevisions) { if log.IsLevelEnabled(log.DebugLevel) { log.WithFields(log.Fields{ "revision": revision, - "appSrc": appSourceKeyJSON(appSrc, srcRefs), + "appSrc": appSourceKeyJSON(appSrc, srcRefs, refSourceCommitSHAs), "namespace": namespace, "trackingKey": trackingKey(appLabelKey, trackingMethod), "appName": appName, @@ -212,8 +318,14 @@ func LogDebugManifestCacheKeyFields(message string, reason string, revision stri } } -func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, res *CachedManifestResponse) error { - err := c.cache.GetItem(manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo), res) +func (c *Cache) SetNewRevisionManifests(newRevision string, revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, refSourceCommitSHAs ResolvedRevisions) error { + oldKey := manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs) + newKey := manifestCacheKey(newRevision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs) + return c.cache.RenameItem(oldKey, newKey, c.repoCacheExpiration) +} + +func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, res *CachedManifestResponse, refSourceCommitSHAs ResolvedRevisions) error { + err := c.cache.GetItem(manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs), res) if err != nil { return err @@ -228,9 +340,9 @@ func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, s if hash != res.CacheEntryHash || res.ManifestResponse == nil && res.MostRecentError == "" { log.Warnf("Manifest hash did not match expected value or cached manifests response is empty, treating as a cache miss: %s", appName) - LogDebugManifestCacheKeyFields("deleting manifests cache", "manifest hash did not match or cached response is empty", revision, appSrc, srcRefs, clusterInfo, namespace, trackingMethod, appLabelKey, appName) + LogDebugManifestCacheKeyFields("deleting manifests cache", "manifest hash did not match or cached response is empty", revision, appSrc, srcRefs, clusterInfo, namespace, trackingMethod, appLabelKey, appName, refSourceCommitSHAs) - err = c.DeleteManifests(revision, appSrc, srcRefs, clusterInfo, namespace, trackingMethod, appLabelKey, appName) + err = c.DeleteManifests(revision, appSrc, srcRefs, clusterInfo, namespace, trackingMethod, appLabelKey, appName, refSourceCommitSHAs) if err != nil { return fmt.Errorf("Unable to delete manifest after hash mismatch, %v", err) } @@ -245,7 +357,7 @@ func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, s return nil } -func (c *Cache) SetManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, res *CachedManifestResponse) error { +func (c *Cache) SetManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, res *CachedManifestResponse, refSourceCommitSHAs ResolvedRevisions) error { // Generate and apply the cache entry hash, before writing if res != nil { res = res.shallowCopy() @@ -256,26 +368,39 @@ func (c *Cache) SetManifests(revision string, appSrc *appv1.ApplicationSource, s res.CacheEntryHash = hash } - return c.cache.SetItem(manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo), res, c.repoCacheExpiration, res == nil) + return c.cache.SetItem( + manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs), + res, + &cacheutil.CacheActionOpts{ + Expiration: c.repoCacheExpiration, + Delete: res == nil}) } -func (c *Cache) DeleteManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace, trackingMethod, appLabelKey, appName string) error { - return c.cache.SetItem(manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo), "", c.repoCacheExpiration, true) +func (c *Cache) DeleteManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace, trackingMethod, appLabelKey, appName string, refSourceCommitSHAs ResolvedRevisions) error { + return c.cache.SetItem( + manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs), + "", + &cacheutil.CacheActionOpts{Delete: true}) } -func appDetailsCacheKey(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, trackingMethod appv1.TrackingMethod) string { +func appDetailsCacheKey(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, trackingMethod appv1.TrackingMethod, refSourceCommitSHAs ResolvedRevisions) string { if trackingMethod == "" { trackingMethod = argo.TrackingMethodLabel } - return fmt.Sprintf("appdetails|%s|%d|%s", revision, appSourceKey(appSrc, srcRefs), trackingMethod) + return fmt.Sprintf("appdetails|%s|%d|%s", revision, appSourceKey(appSrc, srcRefs, refSourceCommitSHAs), trackingMethod) } -func (c *Cache) GetAppDetails(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, res *apiclient.RepoAppDetailsResponse, trackingMethod appv1.TrackingMethod) error { - return c.cache.GetItem(appDetailsCacheKey(revision, appSrc, srcRefs, trackingMethod), res) +func (c *Cache) GetAppDetails(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, res *apiclient.RepoAppDetailsResponse, trackingMethod appv1.TrackingMethod, refSourceCommitSHAs ResolvedRevisions) error { + return c.cache.GetItem(appDetailsCacheKey(revision, appSrc, srcRefs, trackingMethod, refSourceCommitSHAs), res) } -func (c *Cache) SetAppDetails(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, res *apiclient.RepoAppDetailsResponse, trackingMethod appv1.TrackingMethod) error { - return c.cache.SetItem(appDetailsCacheKey(revision, appSrc, srcRefs, trackingMethod), res, c.repoCacheExpiration, res == nil) +func (c *Cache) SetAppDetails(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, res *apiclient.RepoAppDetailsResponse, trackingMethod appv1.TrackingMethod, refSourceCommitSHAs ResolvedRevisions) error { + return c.cache.SetItem( + appDetailsCacheKey(revision, appSrc, srcRefs, trackingMethod, refSourceCommitSHAs), + res, + &cacheutil.CacheActionOpts{ + Expiration: c.repoCacheExpiration, + Delete: res == nil}) } func revisionMetadataKey(repoURL, revision string) string { @@ -288,7 +413,58 @@ func (c *Cache) GetRevisionMetadata(repoURL, revision string) (*appv1.RevisionMe } func (c *Cache) SetRevisionMetadata(repoURL, revision string, item *appv1.RevisionMetadata) error { - return c.cache.SetItem(revisionMetadataKey(repoURL, revision), item, c.repoCacheExpiration, false) + return c.cache.SetItem( + revisionMetadataKey(repoURL, revision), + item, + &cacheutil.CacheActionOpts{Expiration: c.repoCacheExpiration}) +} + +func revisionChartDetailsKey(repoURL, chart, revision string) string { + return fmt.Sprintf("chartdetails|%s|%s|%s", repoURL, chart, revision) +} + +func (c *Cache) GetRevisionChartDetails(repoURL, chart, revision string) (*appv1.ChartDetails, error) { + item := &appv1.ChartDetails{} + return item, c.cache.GetItem(revisionChartDetailsKey(repoURL, chart, revision), item) +} + +func (c *Cache) SetRevisionChartDetails(repoURL, chart, revision string, item *appv1.ChartDetails) error { + return c.cache.SetItem( + revisionChartDetailsKey(repoURL, chart, revision), + item, + &cacheutil.CacheActionOpts{Expiration: c.repoCacheExpiration}) +} + +func gitFilesKey(repoURL, revision, pattern string) string { + return fmt.Sprintf("gitfiles|%s|%s|%s", repoURL, revision, pattern) +} + +func (c *Cache) SetGitFiles(repoURL, revision, pattern string, files map[string][]byte) error { + return c.cache.SetItem( + gitFilesKey(repoURL, revision, pattern), + &files, + &cacheutil.CacheActionOpts{Expiration: c.repoCacheExpiration}) +} + +func (c *Cache) GetGitFiles(repoURL, revision, pattern string) (map[string][]byte, error) { + var item map[string][]byte + return item, c.cache.GetItem(gitFilesKey(repoURL, revision, pattern), &item) +} + +func gitDirectoriesKey(repoURL, revision string) string { + return fmt.Sprintf("gitdirs|%s|%s", repoURL, revision) +} + +func (c *Cache) SetGitDirectories(repoURL, revision string, directories []string) error { + return c.cache.SetItem( + gitDirectoriesKey(repoURL, revision), + &directories, + &cacheutil.CacheActionOpts{Expiration: c.repoCacheExpiration}) +} + +func (c *Cache) GetGitDirectories(repoURL, revision string) ([]string, error) { + var item []string + return item, c.cache.GetItem(gitDirectoriesKey(repoURL, revision), &item) } func (cmr *CachedManifestResponse) shallowCopy() *CachedManifestResponse { diff --git a/reposerver/cache/cache_test.go b/reposerver/cache/cache_test.go index 747962c5ccaf3..452a9f6e14edb 100644 --- a/reposerver/cache/cache_test.go +++ b/reposerver/cache/cache_test.go @@ -3,36 +3,48 @@ package cache import ( "encoding/json" "errors" + "fmt" "strings" "testing" "time" - "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" - . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + "github.com/argoproj/argo-cd/v2/reposerver/cache/mocks" cacheutil "github.com/argoproj/argo-cd/v2/util/cache" + "github.com/go-git/go-git/v5/plumbing" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) -type fixtures struct { +type MockedCache struct { + mock.Mock *Cache } +type fixtures struct { + mockCache *mocks.MockRepoCache + cache *MockedCache +} + func newFixtures() *fixtures { - return &fixtures{NewCache( - cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Hour)), - 1*time.Minute, - 1*time.Minute, - )} + mockCache := mocks.NewMockRepoCache(&mocks.MockCacheOptions{RevisionCacheExpiration: 1 * time.Minute, RepoCacheExpiration: 1 * time.Minute}) + newBaseCache := cacheutil.NewCache(mockCache.RedisClient) + baseCache := NewCache(newBaseCache, 1*time.Minute, 1*time.Minute, 10*time.Second) + return &fixtures{mockCache: mockCache, cache: &MockedCache{Cache: baseCache}} } func TestCache_GetRevisionMetadata(t *testing.T) { - cache := newFixtures().Cache + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + mockCache := fixtures.mockCache // cache miss _, err := cache.GetRevisionMetadata("my-repo-url", "my-revision") assert.Equal(t, ErrCacheMiss, err) + mockCache.RedisClient.AssertCalled(t, "Get", mock.Anything, mock.Anything) // populate cache err = cache.SetRevisionMetadata("my-repo-url", "my-revision", &RevisionMetadata{Message: "my-message"}) assert.NoError(t, err) @@ -46,10 +58,14 @@ func TestCache_GetRevisionMetadata(t *testing.T) { value, err := cache.GetRevisionMetadata("my-repo-url", "my-revision") assert.NoError(t, err) assert.Equal(t, &RevisionMetadata{Message: "my-message"}, value) + mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 1, ExternalGets: 4}) } func TestCache_ListApps(t *testing.T) { - cache := newFixtures().Cache + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + mockCache := fixtures.mockCache // cache miss _, err := cache.ListApps("my-repo-url", "my-revision") assert.Equal(t, ErrCacheMiss, err) @@ -66,66 +82,79 @@ func TestCache_ListApps(t *testing.T) { value, err := cache.ListApps("my-repo-url", "my-revision") assert.NoError(t, err) assert.Equal(t, map[string]string{"foo": "bar"}, value) + mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 1, ExternalGets: 4}) } func TestCache_GetManifests(t *testing.T) { - cache := newFixtures().Cache + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + mockCache := fixtures.mockCache // cache miss q := &apiclient.ManifestRequest{} value := &CachedManifestResponse{} - err := cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value) + err := cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil) assert.Equal(t, ErrCacheMiss, err) // populate cache res := &CachedManifestResponse{ManifestResponse: &apiclient.ManifestResponse{SourceType: "my-source-type"}} - err = cache.SetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", res) + err = cache.SetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", res, nil) assert.NoError(t, err) t.Run("expect cache miss because of changed revision", func(t *testing.T) { - err = cache.GetManifests("other-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value) + err = cache.GetManifests("other-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil) assert.Equal(t, ErrCacheMiss, err) }) t.Run("expect cache miss because of changed path", func(t *testing.T) { - err = cache.GetManifests("my-revision", &ApplicationSource{Path: "other-path"}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value) + err = cache.GetManifests("my-revision", &ApplicationSource{Path: "other-path"}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil) assert.Equal(t, ErrCacheMiss, err) }) t.Run("expect cache miss because of changed namespace", func(t *testing.T) { - err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "other-namespace", "", "my-app-label-key", "my-app-label-value", value) + err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "other-namespace", "", "my-app-label-key", "my-app-label-value", value, nil) assert.Equal(t, ErrCacheMiss, err) }) t.Run("expect cache miss because of changed app label key", func(t *testing.T) { - err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "other-app-label-key", "my-app-label-value", value) + err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "other-app-label-key", "my-app-label-value", value, nil) assert.Equal(t, ErrCacheMiss, err) }) t.Run("expect cache miss because of changed app label value", func(t *testing.T) { - err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "other-app-label-value", value) + err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "other-app-label-value", value, nil) + assert.Equal(t, ErrCacheMiss, err) + }) + t.Run("expect cache miss because of changed referenced source", func(t *testing.T) { + err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "other-app-label-value", value, map[string]string{"my-referenced-source": "my-referenced-revision"}) assert.Equal(t, ErrCacheMiss, err) }) t.Run("expect cache hit", func(t *testing.T) { - err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value) + err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil) assert.NoError(t, err) assert.Equal(t, &CachedManifestResponse{ManifestResponse: &apiclient.ManifestResponse{SourceType: "my-source-type"}}, value) }) + mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 1, ExternalGets: 8}) } func TestCache_GetAppDetails(t *testing.T) { - cache := newFixtures().Cache + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + mockCache := fixtures.mockCache // cache miss value := &apiclient.RepoAppDetailsResponse{} - emptyRefSources := map[string]*appv1.RefTarget{} - err := cache.GetAppDetails("my-revision", &ApplicationSource{}, emptyRefSources, value, "") + emptyRefSources := map[string]*RefTarget{} + err := cache.GetAppDetails("my-revision", &ApplicationSource{}, emptyRefSources, value, "", nil) assert.Equal(t, ErrCacheMiss, err) res := &apiclient.RepoAppDetailsResponse{Type: "my-type"} - err = cache.SetAppDetails("my-revision", &ApplicationSource{}, emptyRefSources, res, "") + err = cache.SetAppDetails("my-revision", &ApplicationSource{}, emptyRefSources, res, "", nil) assert.NoError(t, err) //cache miss - err = cache.GetAppDetails("other-revision", &ApplicationSource{}, emptyRefSources, value, "") + err = cache.GetAppDetails("other-revision", &ApplicationSource{}, emptyRefSources, value, "", nil) assert.Equal(t, ErrCacheMiss, err) //cache miss - err = cache.GetAppDetails("my-revision", &ApplicationSource{Path: "other-path"}, emptyRefSources, value, "") + err = cache.GetAppDetails("my-revision", &ApplicationSource{Path: "other-path"}, emptyRefSources, value, "", nil) assert.Equal(t, ErrCacheMiss, err) // cache hit - err = cache.GetAppDetails("my-revision", &ApplicationSource{}, emptyRefSources, value, "") + err = cache.GetAppDetails("my-revision", &ApplicationSource{}, emptyRefSources, value, "", nil) assert.NoError(t, err) assert.Equal(t, &apiclient.RepoAppDetailsResponse{Type: "my-type"}, value) + mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 1, ExternalGets: 4}) } func TestAddCacheFlagsToCmd(t *testing.T) { @@ -142,6 +171,7 @@ func TestCachedManifestResponse_HashBehavior(t *testing.T) { cacheutil.NewCache(inMemCache), 1*time.Minute, 1*time.Minute, + 10*time.Second, ) response := apiclient.ManifestResponse{ @@ -149,7 +179,7 @@ func TestCachedManifestResponse_HashBehavior(t *testing.T) { Revision: "revision", Manifests: []string{"sample-text"}, } - appSrc := &appv1.ApplicationSource{} + appSrc := &ApplicationSource{} appKey := "key" appValue := "value" @@ -162,7 +192,7 @@ func TestCachedManifestResponse_HashBehavior(t *testing.T) { NumberOfConsecutiveFailures: 0, } q := &apiclient.ManifestRequest{} - err := repoCache.SetManifests(response.Revision, appSrc, q.RefSources, q, response.Namespace, "", appKey, appValue, store) + err := repoCache.SetManifests(response.Revision, appSrc, q.RefSources, q, response.Namespace, "", appKey, appValue, store, nil) if err != nil { t.Fatal(err) } @@ -193,7 +223,7 @@ func TestCachedManifestResponse_HashBehavior(t *testing.T) { // Retrieve the value using 'GetManifests' and confirm it works retrievedVal := &CachedManifestResponse{} - err = repoCache.GetManifests(response.Revision, appSrc, q.RefSources, q, response.Namespace, "", appKey, appValue, retrievedVal) + err = repoCache.GetManifests(response.Revision, appSrc, q.RefSources, q, response.Namespace, "", appKey, appValue, retrievedVal, nil) if err != nil { t.Fatal(err) } @@ -216,7 +246,7 @@ func TestCachedManifestResponse_HashBehavior(t *testing.T) { // Retrieve the value using GetManifests and confirm it returns a cache miss retrievedVal = &CachedManifestResponse{} - err = repoCache.GetManifests(response.Revision, appSrc, q.RefSources, q, response.Namespace, "", appKey, appValue, retrievedVal) + err = repoCache.GetManifests(response.Revision, appSrc, q.RefSources, q, response.Namespace, "", appKey, appValue, retrievedVal, nil) assert.True(t, err == cacheutil.ErrCacheMiss) @@ -306,3 +336,431 @@ func TestCachedManifestResponse_ShallowCopyExpectedFields(t *testing.T) { } } + +func TestGetGitReferences(t *testing.T) { + t.Run("Valid args, nothing in cache, in-memory only", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + var references []*plumbing.Reference + lockOwner, err := cache.GetGitReferences("test-repo", &references) + assert.NoError(t, err, "Error is cache miss handled inside function") + assert.Equal(t, "", lockOwner, "Lock owner should be empty") + assert.Nil(t, references) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1}) + }) + + t.Run("Valid args, nothing in cache, external only", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + var references []*plumbing.Reference + lockOwner, err := cache.GetGitReferences("test-repo", &references) + assert.NoError(t, err, "Error is cache miss handled inside function") + assert.Equal(t, "", lockOwner, "Lock owner should be empty") + assert.Nil(t, references) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1}) + }) + + t.Run("Valid args, value in cache, in-memory only", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + err := cache.SetGitReferences("test-repo", *GitRefCacheItemToReferences([][2]string{{"test-repo", "ref: test"}})) + assert.NoError(t, err) + var references []*plumbing.Reference + lockOwner, err := cache.GetGitReferences("test-repo", &references) + assert.NoError(t, err) + assert.Equal(t, "", lockOwner, "Lock owner should be empty") + assert.Equal(t, 1, len(references)) + assert.Equal(t, "test", (references)[0].Target().String()) + assert.Equal(t, "test-repo", (references)[0].Name().String()) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 1, ExternalGets: 1}) + }) + + t.Run("cache error", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + fixtures.mockCache.RedisClient.On("Get", mock.Anything, mock.Anything).Unset() + fixtures.mockCache.RedisClient.On("Get", mock.Anything, mock.Anything).Return(errors.New("test cache error")) + var references []*plumbing.Reference + lockOwner, err := cache.GetGitReferences("test-repo", &references) + assert.ErrorContains(t, err, "test cache error", "Error should be propagated") + assert.Equal(t, "", lockOwner, "Lock owner should be empty") + assert.Nil(t, references) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1}) + }) + +} + +func TestGitRefCacheItemToReferences_DataChecks(t *testing.T) { + references := *GitRefCacheItemToReferences(nil) + assert.Equal(t, 0, len(references), "No data should be handled gracefully by returning an empty slice") + references = *GitRefCacheItemToReferences([][2]string{{"", ""}}) + assert.Equal(t, 0, len(references), "Empty data should be discarded") + references = *GitRefCacheItemToReferences([][2]string{{"test", ""}}) + assert.Equal(t, 1, len(references), "Just the key being set should not be discarded") + assert.Equal(t, "test", references[0].Name().String(), "Name should be set and equal test") + references = *GitRefCacheItemToReferences([][2]string{{"", "ref: test1"}}) + assert.Equal(t, 1, len(references), "Just the value being set should not be discarded") + assert.Equal(t, "test1", references[0].Target().String(), "Target should be set and equal test1") + references = *GitRefCacheItemToReferences([][2]string{{"test2", "ref: test2"}}) + assert.Equal(t, 1, len(references), "Valid data is should be preserved") + assert.Equal(t, "test2", references[0].Name().String(), "Name should be set and equal test2") + assert.Equal(t, "test2", references[0].Target().String(), "Target should be set and equal test2") + references = *GitRefCacheItemToReferences([][2]string{{"test3", "ref: test3"}, {"test4", "ref: test4"}}) + assert.Equal(t, 2, len(references), "Valid data is should be preserved") + assert.Equal(t, "test3", references[0].Name().String(), "Name should be set and equal test3") + assert.Equal(t, "test3", references[0].Target().String(), "Target should be set and equal test3") + assert.Equal(t, "test4", references[1].Name().String(), "Name should be set and equal test4") + assert.Equal(t, "test4", references[1].Target().String(), "Target should be set and equal test4") +} + +func TestTryLockGitRefCache_OwnershipFlows(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + utilCache := cache.cache + var references []*plumbing.Reference + // Test setting the lock + _, err := cache.TryLockGitRefCache("my-repo-url", "my-lock-id", &references) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 1, ExternalGets: 1}) + assert.NoError(t, err) + var output [][2]string + key := fmt.Sprintf("git-refs|%s", "my-repo-url") + err = utilCache.GetItem(key, &output) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 1, ExternalGets: 2}) + assert.NoError(t, err) + assert.Equal(t, "locked", output[0][0], "The lock should be set") + assert.Equal(t, "my-lock-id", output[0][1], "The lock should be set to the provided lock id") + // Test not being able to overwrite the lock + _, err = cache.TryLockGitRefCache("my-repo-url", "other-lock-id", &references) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 2, ExternalGets: 3}) + assert.NoError(t, err) + err = utilCache.GetItem(key, &output) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 2, ExternalGets: 4}) + assert.NoError(t, err) + assert.Equal(t, "locked", output[0][0], "The lock should not have changed") + assert.Equal(t, "my-lock-id", output[0][1], "The lock should not have changed") + // Test can overwrite once there is nothing set + err = utilCache.SetItem(key, [][2]string{}, &cacheutil.CacheActionOpts{Expiration: 0, Delete: true}) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 2, ExternalGets: 4, ExternalDeletes: 1}) + assert.NoError(t, err) + _, err = cache.TryLockGitRefCache("my-repo-url", "other-lock-id", &references) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 3, ExternalGets: 5, ExternalDeletes: 1}) + assert.NoError(t, err) + err = utilCache.GetItem(key, &output) + assert.NoError(t, err) + assert.Equal(t, "locked", output[0][0], "The lock should be set") + assert.Equal(t, "other-lock-id", output[0][1], "The lock id should have changed to other-lock-id") + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 3, ExternalGets: 6, ExternalDeletes: 1}) +} + +func TestGetOrLockGitReferences(t *testing.T) { + t.Run("Test cache lock get lock", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + var references []*plumbing.Reference + lockId, err := cache.GetOrLockGitReferences("test-repo", "test-lock-id", &references) + assert.NoError(t, err) + assert.Equal(t, lockId, "test-lock-id") + assert.NotEqual(t, "", lockId, "Lock id should be set") + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 1, ExternalGets: 2}) + }) + + t.Run("Test cache lock, cache hit local", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + err := cache.SetGitReferences("test-repo", *GitRefCacheItemToReferences([][2]string{{"test-repo", "ref: test"}})) + assert.NoError(t, err) + var references []*plumbing.Reference + lockId, err := cache.GetOrLockGitReferences("test-repo", "test-lock-id", &references) + assert.NoError(t, err) + assert.NotEqual(t, lockId, "test-lock-id") + assert.Equal(t, "", lockId, "Lock id should not be set") + assert.Equal(t, "test-repo", references[0].Name().String()) + assert.Equal(t, "test", references[0].Target().String()) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 1, ExternalGets: 1}) + }) + + t.Run("Test cache lock, cache hit remote", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + err := fixtures.cache.cache.SetItem( + "git-refs|test-repo", + [][2]string{{"test-repo", "ref: test"}}, + &cacheutil.CacheActionOpts{ + Expiration: 30 * time.Second}) + assert.NoError(t, err) + var references []*plumbing.Reference + lockId, err := cache.GetOrLockGitReferences("test-repo", "test-lock-id", &references) + assert.NoError(t, err) + assert.NotEqual(t, lockId, "test-lock-id") + assert.Equal(t, "", lockId, "Lock id should not be set") + assert.Equal(t, "test-repo", references[0].Name().String()) + assert.Equal(t, "test", references[0].Target().String()) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 1, ExternalGets: 1}) + }) + + t.Run("Test miss, populated by external", func(t *testing.T) { + // Tests the case where another process populates the external cache when trying + // to obtain the lock + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + fixtures.mockCache.RedisClient.On("Get", mock.Anything, mock.Anything).Unset() + fixtures.mockCache.RedisClient.On("Get", mock.Anything, mock.Anything).Return(cacheutil.ErrCacheMiss).Once().Run(func(args mock.Arguments) { + err := cache.SetGitReferences("test-repo", *GitRefCacheItemToReferences([][2]string{{"test-repo", "ref: test"}})) + assert.NoError(t, err) + }).On("Get", mock.Anything, mock.Anything).Return(nil) + var references []*plumbing.Reference + lockId, err := cache.GetOrLockGitReferences("test-repo", "test-lock-id", &references) + assert.NoError(t, err) + assert.NotEqual(t, lockId, "test-lock-id") + assert.Equal(t, "", lockId, "Lock id should not be set") + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 2, ExternalGets: 2}) + }) + + t.Run("Test cache lock timeout", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + // Create conditions for cache hit, which would result in false on updateCache if we weren't reaching the timeout + err := cache.SetGitReferences("test-repo", *GitRefCacheItemToReferences([][2]string{{"test-repo", "ref: test"}})) + assert.NoError(t, err) + cache.revisionCacheLockTimeout = -1 * time.Second + var references []*plumbing.Reference + lockId, err := cache.GetOrLockGitReferences("test-repo", "test-lock-id", &references) + assert.NoError(t, err) + assert.Equal(t, lockId, "test-lock-id") + assert.NotEqual(t, "", lockId, "Lock id should be set") + cache.revisionCacheLockTimeout = 10 * time.Second + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 1}) + }) + + t.Run("Test cache lock error", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + fixtures.cache.revisionCacheLockTimeout = 10 * time.Second + fixtures.mockCache.RedisClient.On("Set", mock.Anything).Unset() + fixtures.mockCache.RedisClient.On("Set", mock.Anything).Return(errors.New("test cache error")).Once(). + On("Set", mock.Anything).Return(nil) + var references []*plumbing.Reference + lockId, err := cache.GetOrLockGitReferences("test-repo", "test-lock-id", &references) + assert.NoError(t, err) + assert.Equal(t, lockId, "test-lock-id") + assert.NotEqual(t, "", lockId, "Lock id should be set") + fixtures.mockCache.RedisClient.AssertNumberOfCalls(t, "Set", 2) + fixtures.mockCache.RedisClient.AssertNumberOfCalls(t, "Get", 4) + }) +} + +func TestUnlockGitReferences(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + + t.Run("Test not locked", func(t *testing.T) { + err := cache.UnlockGitReferences("test-repo", "") + assert.Error(t, err) + assert.Contains(t, err.Error(), "key is missing") + }) + + t.Run("Test unlock", func(t *testing.T) { + // Get lock + var references []*plumbing.Reference + lockId, err := cache.GetOrLockGitReferences("test-repo", "test-lock-id", &references) + assert.NoError(t, err) + assert.Equal(t, lockId, "test-lock-id") + assert.NotEqual(t, "", lockId, "Lock id should be set") + // Release lock + err = cache.UnlockGitReferences("test-repo", lockId) + assert.NoError(t, err) + }) +} + +func TestSetHelmIndex(t *testing.T) { + t.Run("SetHelmIndex with valid data", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + err := fixtures.cache.SetHelmIndex("test-repo", []byte("test-data")) + assert.NoError(t, err) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalSets: 1}) + }) + t.Run("SetHelmIndex with nil", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + err := fixtures.cache.SetHelmIndex("test-repo", nil) + assert.Error(t, err, "nil data should not be cached") + var indexData []byte + err = fixtures.cache.GetHelmIndex("test-repo", &indexData) + assert.Error(t, err) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1}) + }) +} + +func TestRevisionChartDetails(t *testing.T) { + t.Run("GetRevisionChartDetails cache miss", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + details, err := fixtures.cache.GetRevisionChartDetails("test-repo", "test-revision", "v1.0.0") + assert.ErrorAs(t, err, &ErrCacheMiss) + assert.Equal(t, &appv1.ChartDetails{}, details) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1}) + }) + t.Run("GetRevisionChartDetails cache miss local", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + expectedItem := &appv1.ChartDetails{ + Description: "test-chart", + Home: "v1.0.0", + Maintainers: []string{"test-maintainer"}, + } + err := cache.cache.SetItem( + revisionChartDetailsKey("test-repo", "test-revision", "v1.0.0"), + expectedItem, + &cacheutil.CacheActionOpts{Expiration: 30 * time.Second}) + assert.NoError(t, err) + details, err := fixtures.cache.GetRevisionChartDetails("test-repo", "test-revision", "v1.0.0") + assert.NoError(t, err) + assert.Equal(t, expectedItem, details) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1, ExternalSets: 1}) + }) + + t.Run("GetRevisionChartDetails cache hit local", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + expectedItem := &appv1.ChartDetails{ + Description: "test-chart", + Home: "v1.0.0", + Maintainers: []string{"test-maintainer"}, + } + err := cache.cache.SetItem( + revisionChartDetailsKey("test-repo", "test-revision", "v1.0.0"), + expectedItem, + &cacheutil.CacheActionOpts{Expiration: 30 * time.Second}) + assert.NoError(t, err) + details, err := fixtures.cache.GetRevisionChartDetails("test-repo", "test-revision", "v1.0.0") + assert.NoError(t, err) + assert.Equal(t, expectedItem, details) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1, ExternalSets: 1}) + }) + + t.Run("SetRevisionChartDetails", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + expectedItem := &appv1.ChartDetails{ + Description: "test-chart", + Home: "v1.0.0", + Maintainers: []string{"test-maintainer"}, + } + err := fixtures.cache.SetRevisionChartDetails("test-repo", "test-revision", "v1.0.0", expectedItem) + assert.NoError(t, err) + details, err := fixtures.cache.GetRevisionChartDetails("test-repo", "test-revision", "v1.0.0") + assert.NoError(t, err) + assert.Equal(t, expectedItem, details) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1, ExternalSets: 1}) + }) + +} + +func TestGetGitDirectories(t *testing.T) { + t.Run("GetGitDirectories cache miss", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + directories, err := fixtures.cache.GetGitDirectories("test-repo", "test-revision") + assert.ErrorAs(t, err, &ErrCacheMiss) + assert.Equal(t, 0, len(directories)) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1}) + }) + t.Run("GetGitDirectories cache miss local", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + expectedItem := []string{"test/dir", "test/dir2"} + err := cache.cache.SetItem( + gitDirectoriesKey("test-repo", "test-revision"), + expectedItem, + &cacheutil.CacheActionOpts{Expiration: 30 * time.Second}) + assert.NoError(t, err) + directories, err := fixtures.cache.GetGitDirectories("test-repo", "test-revision") + assert.NoError(t, err) + assert.Equal(t, expectedItem, directories) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1, ExternalSets: 1}) + }) + + t.Run("GetGitDirectories cache hit local", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + expectedItem := []string{"test/dir", "test/dir2"} + err := cache.cache.SetItem( + gitDirectoriesKey("test-repo", "test-revision"), + expectedItem, + &cacheutil.CacheActionOpts{Expiration: 30 * time.Second}) + assert.NoError(t, err) + directories, err := fixtures.cache.GetGitDirectories("test-repo", "test-revision") + assert.NoError(t, err) + assert.Equal(t, expectedItem, directories) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1, ExternalSets: 1}) + }) + + t.Run("SetGitDirectories", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + expectedItem := []string{"test/dir", "test/dir2"} + err := fixtures.cache.SetGitDirectories("test-repo", "test-revision", expectedItem) + assert.NoError(t, err) + directories, err := fixtures.cache.GetGitDirectories("test-repo", "test-revision") + assert.NoError(t, err) + assert.Equal(t, expectedItem, directories) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1, ExternalSets: 1}) + }) + +} + +func TestGetGitFiles(t *testing.T) { + t.Run("GetGitFiles cache miss", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + directories, err := fixtures.cache.GetGitFiles("test-repo", "test-revision", "*.json") + assert.ErrorAs(t, err, &ErrCacheMiss) + assert.Equal(t, 0, len(directories)) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1}) + }) + t.Run("GetGitFiles cache hit", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + cache := fixtures.cache + expectedItem := map[string][]byte{"test/file.json": []byte("\"test\":\"contents\""), "test/file1.json": []byte("\"test1\":\"contents1\"")} + err := cache.cache.SetItem( + gitFilesKey("test-repo", "test-revision", "*.json"), + expectedItem, + &cacheutil.CacheActionOpts{Expiration: 30 * time.Second}) + assert.NoError(t, err) + files, err := fixtures.cache.GetGitFiles("test-repo", "test-revision", "*.json") + assert.NoError(t, err) + assert.Equal(t, expectedItem, files) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1, ExternalSets: 1}) + }) + + t.Run("SetGitFiles", func(t *testing.T) { + fixtures := newFixtures() + t.Cleanup(fixtures.mockCache.StopRedisCallback) + expectedItem := map[string][]byte{"test/file.json": []byte("\"test\":\"contents\""), "test/file1.json": []byte("\"test1\":\"contents1\"")} + err := fixtures.cache.SetGitFiles("test-repo", "test-revision", "*.json", expectedItem) + assert.NoError(t, err) + files, err := fixtures.cache.GetGitFiles("test-repo", "test-revision", "*.json") + assert.NoError(t, err) + assert.Equal(t, expectedItem, files) + fixtures.mockCache.AssertCacheCalledTimes(t, &mocks.CacheCallCounts{ExternalGets: 1, ExternalSets: 1}) + }) + +} diff --git a/reposerver/cache/mocks/reposervercache.go b/reposerver/cache/mocks/reposervercache.go new file mode 100644 index 0000000000000..0e49b5816178e --- /dev/null +++ b/reposerver/cache/mocks/reposervercache.go @@ -0,0 +1,71 @@ +package mocks + +import ( + "testing" + "time" + + "github.com/alicebob/miniredis/v2" + cacheutil "github.com/argoproj/argo-cd/v2/util/cache" + cacheutilmocks "github.com/argoproj/argo-cd/v2/util/cache/mocks" + "github.com/redis/go-redis/v9" + "github.com/stretchr/testify/mock" +) + +type MockCacheType int + +const ( + MockCacheTypeRedis MockCacheType = iota + MockCacheTypeInMem +) + +type MockRepoCache struct { + mock.Mock + RedisClient *cacheutilmocks.MockCacheClient + StopRedisCallback func() +} + +type MockCacheOptions struct { + RepoCacheExpiration time.Duration + RevisionCacheExpiration time.Duration + ReadDelay time.Duration + WriteDelay time.Duration +} + +type CacheCallCounts struct { + ExternalSets int + ExternalGets int + ExternalDeletes int +} + +// Checks that the cache was called the expected number of times +func (mockCache *MockRepoCache) AssertCacheCalledTimes(t *testing.T, calls *CacheCallCounts) { + mockCache.RedisClient.AssertNumberOfCalls(t, "Get", calls.ExternalGets) + mockCache.RedisClient.AssertNumberOfCalls(t, "Set", calls.ExternalSets) + mockCache.RedisClient.AssertNumberOfCalls(t, "Delete", calls.ExternalDeletes) +} + +func (mockCache *MockRepoCache) ConfigureDefaultCallbacks() { + mockCache.RedisClient.On("Get", mock.Anything, mock.Anything).Return(nil) + mockCache.RedisClient.On("Set", mock.Anything).Return(nil) + mockCache.RedisClient.On("Delete", mock.Anything).Return(nil) +} + +func NewInMemoryRedis() (*redis.Client, func()) { + cacheutil.NewInMemoryCache(5 * time.Second) + mr, err := miniredis.Run() + if err != nil { + panic(err) + } + return redis.NewClient(&redis.Options{Addr: mr.Addr()}), mr.Close +} + +func NewMockRepoCache(cacheOpts *MockCacheOptions) *MockRepoCache { + redisClient, stopRedis := NewInMemoryRedis() + redisCacheClient := &cacheutilmocks.MockCacheClient{ + ReadDelay: cacheOpts.ReadDelay, + WriteDelay: cacheOpts.WriteDelay, + BaseCache: cacheutil.NewRedisCache(redisClient, cacheOpts.RepoCacheExpiration, cacheutil.RedisCompressionNone)} + newMockCache := &MockRepoCache{RedisClient: redisCacheClient, StopRedisCallback: stopRedis} + newMockCache.ConfigureDefaultCallbacks() + return newMockCache +} diff --git a/reposerver/gpgwatcher.go b/reposerver/gpgwatcher.go index f2bf09722680d..5b43d6a24ac76 100644 --- a/reposerver/gpgwatcher.go +++ b/reposerver/gpgwatcher.go @@ -19,9 +19,13 @@ func StartGPGWatcher(sourcePath string) error { forceSync := false watcher, err := fsnotify.NewWatcher() if err != nil { - return err + return fmt.Errorf("failed to create fsnotify Watcher: %w", err) } - defer watcher.Close() + defer func(watcher *fsnotify.Watcher) { + if err = watcher.Close(); err != nil { + log.Errorf("Error closing watcher: %v", err) + } + }(watcher) done := make(chan bool) go func() { @@ -31,10 +35,10 @@ func StartGPGWatcher(sourcePath string) error { if !ok { return } - if event.Op&fsnotify.Create == fsnotify.Create || event.Op&fsnotify.Remove == fsnotify.Remove { + if event.Has(fsnotify.Create) || event.Has(fsnotify.Remove) { // In case our watched path is re-created (i.e. during e2e tests), we need to watch again // For more robustness, we retry re-creating the watcher up to maxRecreateRetries - if event.Name == sourcePath && event.Op&fsnotify.Remove == fsnotify.Remove { + if event.Name == sourcePath && event.Has(fsnotify.Remove) { log.Warnf("Re-creating watcher on %s", sourcePath) attempt := 0 for { @@ -79,7 +83,7 @@ func StartGPGWatcher(sourcePath string) error { err = watcher.Add(sourcePath) if err != nil { - return err + return fmt.Errorf("failed to add a new source to the watcher: %w", err) } <-done return fmt.Errorf("Abnormal termination of GPG watcher, refusing to continue.") diff --git a/reposerver/metrics/githandlers.go b/reposerver/metrics/githandlers.go index cce632cf56813..09a0591002c52 100644 --- a/reposerver/metrics/githandlers.go +++ b/reposerver/metrics/githandlers.go @@ -1,11 +1,27 @@ package metrics import ( + "context" + "math" "time" + "golang.org/x/sync/semaphore" + + "github.com/argoproj/argo-cd/v2/util/env" "github.com/argoproj/argo-cd/v2/util/git" ) +var ( + lsRemoteParallelismLimit = env.ParseInt64FromEnv("ARGOCD_GIT_LS_REMOTE_PARALLELISM_LIMIT", 0, 0, math.MaxInt64) + lsRemoteParallelismLimitSemaphore *semaphore.Weighted +) + +func init() { + if lsRemoteParallelismLimit > 0 { + lsRemoteParallelismLimitSemaphore = semaphore.NewWeighted(lsRemoteParallelismLimit) + } +} + // NewGitClientEventHandlers creates event handlers that update Git related metrics func NewGitClientEventHandlers(metricsServer *MetricsServer) git.EventHandlers { return git.EventHandlers{ @@ -19,7 +35,15 @@ func NewGitClientEventHandlers(metricsServer *MetricsServer) git.EventHandlers { OnLsRemote: func(repo string) func() { startTime := time.Now() metricsServer.IncGitRequest(repo, GitRequestTypeLsRemote) + if lsRemoteParallelismLimitSemaphore != nil { + // The `Acquire` method returns either `nil` or error of the provided context. The + // context.Background() is never canceled, so it is safe to ignore the error. + _ = lsRemoteParallelismLimitSemaphore.Acquire(context.Background(), 1) + } return func() { + if lsRemoteParallelismLimitSemaphore != nil { + lsRemoteParallelismLimitSemaphore.Release(1) + } metricsServer.ObserveGitRequestDuration(repo, GitRequestTypeLsRemote, time.Since(startTime)) } }, diff --git a/reposerver/metrics/githandlers_test.go b/reposerver/metrics/githandlers_test.go new file mode 100644 index 0000000000000..6eaeeca82cc36 --- /dev/null +++ b/reposerver/metrics/githandlers_test.go @@ -0,0 +1,122 @@ +package metrics + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "golang.org/x/sync/semaphore" +) + +func TestMain(m *testing.M) { + os.Exit(m.Run()) +} + +func TestEdgeCasesAndErrorHandling(t *testing.T) { + tests := []struct { + name string + setup func() + teardown func() + testFunc func(t *testing.T) + }{ + { + name: "lsRemoteParallelismLimitSemaphore is nil", + testFunc: func(t *testing.T) { + lsRemoteParallelismLimitSemaphore = nil + assert.NotPanics(t, func() { + NewGitClientEventHandlers(&MetricsServer{}) + }) + }, + }, + { + name: "lsRemoteParallelismLimitSemaphore is not nil", + setup: func() { + lsRemoteParallelismLimitSemaphore = semaphore.NewWeighted(1) + }, + teardown: func() { + lsRemoteParallelismLimitSemaphore = nil + }, + testFunc: func(t *testing.T) { + assert.NotPanics(t, func() { + NewGitClientEventHandlers(&MetricsServer{}) + }) + }, + }, + { + name: "lsRemoteParallelismLimitSemaphore is not nil and Acquire returns error", + setup: func() { + lsRemoteParallelismLimitSemaphore = semaphore.NewWeighted(1) + }, + teardown: func() { + lsRemoteParallelismLimitSemaphore = nil + }, + testFunc: func(t *testing.T) { + assert.NotPanics(t, func() { + NewGitClientEventHandlers(&MetricsServer{}) + }) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.setup != nil { + tt.setup() + } + if tt.teardown != nil { + defer tt.teardown() + } + tt.testFunc(t) + }) + } +} + +func TestSemaphoreFunctionality(t *testing.T) { + os.Setenv("ARGOCD_GIT_LSREMOTE_PARALLELISM_LIMIT", "1") + + tests := []struct { + name string + setup func() + teardown func() + testFunc func(t *testing.T) + }{ + { + name: "lsRemoteParallelismLimitSemaphore is not nil", + setup: func() { + lsRemoteParallelismLimitSemaphore = semaphore.NewWeighted(1) + }, + teardown: func() { + lsRemoteParallelismLimitSemaphore = nil + }, + testFunc: func(t *testing.T) { + assert.NotPanics(t, func() { + NewGitClientEventHandlers(&MetricsServer{}) + }) + }, + }, + { + name: "lsRemoteParallelismLimitSemaphore is not nil and Acquire returns error", + setup: func() { + lsRemoteParallelismLimitSemaphore = semaphore.NewWeighted(1) + }, + teardown: func() { + lsRemoteParallelismLimitSemaphore = nil + }, + testFunc: func(t *testing.T) { + assert.NotPanics(t, func() { + NewGitClientEventHandlers(&MetricsServer{}) + }) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.setup != nil { + tt.setup() + } + if tt.teardown != nil { + defer tt.teardown() + } + tt.testFunc(t) + }) + } +} diff --git a/reposerver/metrics/metrics.go b/reposerver/metrics/metrics.go index e629b75e63d3c..44f3dbd01e1bb 100644 --- a/reposerver/metrics/metrics.go +++ b/reposerver/metrics/metrics.go @@ -12,6 +12,7 @@ import ( type MetricsServer struct { handler http.Handler + gitFetchFailCounter *prometheus.CounterVec gitRequestCounter *prometheus.CounterVec gitRequestHistogram *prometheus.HistogramVec repoPendingRequestsGauge *prometheus.GaugeVec @@ -32,6 +33,15 @@ func NewMetricsServer() *MetricsServer { registry.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})) registry.MustRegister(collectors.NewGoCollector()) + gitFetchFailCounter := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "argocd_git_fetch_fail_total", + Help: "Number of git fetch requests failures by repo server", + }, + []string{"repo", "revision"}, + ) + registry.MustRegister(gitFetchFailCounter) + gitRequestCounter := prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "argocd_git_request_total", @@ -81,6 +91,7 @@ func NewMetricsServer() *MetricsServer { return &MetricsServer{ handler: promhttp.HandlerFor(registry, promhttp.HandlerOpts{}), + gitFetchFailCounter: gitFetchFailCounter, gitRequestCounter: gitRequestCounter, gitRequestHistogram: gitRequestHistogram, repoPendingRequestsGauge: repoPendingRequestsGauge, @@ -93,6 +104,10 @@ func (m *MetricsServer) GetHandler() http.Handler { return m.handler } +func (m *MetricsServer) IncGitFetchFail(repo string, revision string) { + m.gitFetchFailCounter.WithLabelValues(repo, revision).Inc() +} + // IncGitRequest increments the git requests counter func (m *MetricsServer) IncGitRequest(repo string, requestType GitRequestType) { m.gitRequestCounter.WithLabelValues(repo, string(requestType)).Inc() diff --git a/reposerver/repository/chart.go b/reposerver/repository/chart.go new file mode 100644 index 0000000000000..f4bcf48fba569 --- /dev/null +++ b/reposerver/repository/chart.go @@ -0,0 +1,30 @@ +package repository + +import ( + "fmt" + "strings" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "sigs.k8s.io/yaml" +) + +func getChartDetails(chartYAML string) (*v1alpha1.ChartDetails, error) { + var chart Chart + err := yaml.Unmarshal([]byte(chartYAML), &chart) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal chart: %w", err) + } + var maintainers []string + for _, maintainer := range chart.Maintainers { + if maintainer.Email != "" { + maintainers = append(maintainers, strings.Trim(fmt.Sprintf("%v <%v>", maintainer.Name, maintainer.Email), " ")) + } else { + maintainers = append(maintainers, fmt.Sprintf("%v", maintainer.Name)) + } + } + return &v1alpha1.ChartDetails{ + Description: chart.Description, + Maintainers: maintainers, + Home: chart.Home, + }, nil +} diff --git a/reposerver/repository/chart_test.go b/reposerver/repository/chart_test.go new file mode 100644 index 0000000000000..b22e7c21bede5 --- /dev/null +++ b/reposerver/repository/chart_test.go @@ -0,0 +1,63 @@ +package repository + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_getChartDetailsNotSet(t *testing.T) { + chart1 := `apiVersion: v3 +name: mychart +version: 0.0.0` + + cd, err := getChartDetails(chart1) + assert.NoError(t, err) + assert.Equal(t, cd.Description, "") + assert.Equal(t, cd.Maintainers, []string(nil)) + assert.Equal(t, cd.Home, "") +} + +func Test_getChartDetailsSet(t *testing.T) { + chart1 := `apiVersion: v3 +name: mychart +version: 0.0.0 +description: a good chart +home: https://example.com +maintainers: +- name: alex + email: example@example.com +` + + cd, err := getChartDetails(chart1) + assert.NoError(t, err) + assert.Equal(t, cd.Description, "a good chart") + assert.Equal(t, cd.Maintainers, []string{"alex "}) + assert.Equal(t, cd.Home, "https://example.com") + + chart1 = `apiVersion: v3 +name: mychart +version: 0.0.0 +description: a good chart +home: https://example.com +maintainers: +- name: alex +` + cd, err = getChartDetails(chart1) + assert.NoError(t, err) + assert.Equal(t, cd.Maintainers, []string{"alex"}) +} + +func Test_getChartDetailsBad(t *testing.T) { + chart1 := `apiVersion: v3 +name: mychart +version: 0.0.0 +description: a good chart +home: https://example.com +maintainers: alex +` + + cd, err := getChartDetails(chart1) + assert.Error(t, err) + assert.Nil(t, cd) +} diff --git a/reposerver/repository/lock.go b/reposerver/repository/lock.go index 05eddf667d82a..fa8da9c3e5089 100644 --- a/reposerver/repository/lock.go +++ b/reposerver/repository/lock.go @@ -55,7 +55,7 @@ func (r *repositoryLock) Lock(path string, revision string, allowConcurrent bool initCloser, err := init() if err != nil { state.cond.L.Unlock() - return nil, err + return nil, fmt.Errorf("failed to initialize repository resources: %w", err) } state.initCloser = initCloser state.revision = revision diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go index 19a30f7182cda..e962e811ee2b5 100644 --- a/reposerver/repository/repository.go +++ b/reposerver/repository/repository.go @@ -10,31 +10,20 @@ import ( "io/fs" "net/url" "os" - "os/exec" "path" "path/filepath" "regexp" "strings" "time" - "github.com/golang/protobuf/ptypes/empty" - - kubeyaml "k8s.io/apimachinery/pkg/util/yaml" - - "k8s.io/apimachinery/pkg/api/resource" - - "github.com/argoproj/argo-cd/v2/common" - "github.com/argoproj/argo-cd/v2/util/io/files" - "github.com/argoproj/argo-cd/v2/util/manifeststream" - "github.com/Masterminds/semver/v3" "github.com/TomOnTime/utfutil" "github.com/argoproj/gitops-engine/pkg/utils/kube" textutils "github.com/argoproj/gitops-engine/pkg/utils/text" "github.com/argoproj/pkg/sync" jsonpatch "github.com/evanphx/json-patch" - "github.com/ghodss/yaml" gogit "github.com/go-git/go-git/v5" + "github.com/golang/protobuf/ptypes/empty" "github.com/google/go-jsonnet" "github.com/google/uuid" grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" @@ -42,29 +31,34 @@ import ( "golang.org/x/sync/semaphore" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + kubeyaml "k8s.io/apimachinery/pkg/util/yaml" + "sigs.k8s.io/yaml" pluginclient "github.com/argoproj/argo-cd/v2/cmpserver/apiclient" + "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/reposerver/cache" - reposervercache "github.com/argoproj/argo-cd/v2/reposerver/cache" "github.com/argoproj/argo-cd/v2/reposerver/metrics" "github.com/argoproj/argo-cd/v2/util/app/discovery" argopath "github.com/argoproj/argo-cd/v2/util/app/path" "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/cmp" - executil "github.com/argoproj/argo-cd/v2/util/exec" + "github.com/argoproj/argo-cd/v2/util/env" "github.com/argoproj/argo-cd/v2/util/git" "github.com/argoproj/argo-cd/v2/util/glob" "github.com/argoproj/argo-cd/v2/util/gpg" "github.com/argoproj/argo-cd/v2/util/grpc" "github.com/argoproj/argo-cd/v2/util/helm" "github.com/argoproj/argo-cd/v2/util/io" + "github.com/argoproj/argo-cd/v2/util/io/files" pathutil "github.com/argoproj/argo-cd/v2/util/io/path" "github.com/argoproj/argo-cd/v2/util/kustomize" + "github.com/argoproj/argo-cd/v2/util/manifeststream" "github.com/argoproj/argo-cd/v2/util/text" ) @@ -77,7 +71,12 @@ const ( ociPrefix = "oci://" ) -var ErrExceededMaxCombinedManifestFileSize = errors.New("exceeded max combined manifest file size") +var ( + ErrExceededMaxCombinedManifestFileSize = errors.New("exceeded max combined manifest file size") + // helmConcurrencyDefault if true then helm concurrent manifest generation is enabled + // TODO: remove env variable and usage of .argocd-allow-concurrency once we are sure that it is safe to enable it by default + helmConcurrencyDefault = env.ParseBoolFromEnv("ARGOCD_HELM_ALLOW_CONCURRENCY", false) +) // Service implements ManifestService interface type Service struct { @@ -87,7 +86,7 @@ type Service struct { chartPaths io.TempPaths gitRepoInitializer func(rootPath string) goio.Closer repoLock *repositoryLock - cache *reposervercache.Cache + cache *cache.Cache parallelismLimitSemaphore *semaphore.Weighted metricsServer *metrics.MetricsServer resourceTracking argo.ResourceTracking @@ -109,10 +108,13 @@ type RepoServerInitConstants struct { AllowOutOfBoundsSymlinks bool StreamedManifestMaxExtractedSize int64 StreamedManifestMaxTarSize int64 + HelmManifestMaxExtractedSize int64 + HelmRegistryMaxIndexSize int64 + DisableHelmManifestMaxExtractedSize bool } // NewService returns a new instance of the Manifest service -func NewService(metricsServer *metrics.MetricsServer, cache *reposervercache.Cache, initConstants RepoServerInitConstants, resourceTracking argo.ResourceTracking, gitCredsStore git.CredsStore, rootDir string) *Service { +func NewService(metricsServer *metrics.MetricsServer, cache *cache.Cache, initConstants RepoServerInitConstants, resourceTracking argo.ResourceTracking, gitCredsStore git.CredsStore, rootDir string) *Service { var parallelismLimitSemaphore *semaphore.Weighted if initConstants.ParallelismLimit > 0 { parallelismLimitSemaphore = semaphore.NewWeighted(initConstants.ParallelismLimit) @@ -149,16 +151,16 @@ func (s *Service) Init() error { // give itself read permissions to list previously written directories err = os.Chmod(s.rootDir, 0700) } - var files []fs.DirEntry + var dirEntries []fs.DirEntry if err == nil { - files, err = os.ReadDir(s.rootDir) + dirEntries, err = os.ReadDir(s.rootDir) } if err != nil { log.Warnf("Failed to restore cloned repositories paths: %v", err) return nil } - for _, file := range files { + for _, file := range dirEntries { if !file.IsDir() { continue } @@ -175,11 +177,11 @@ func (s *Service) Init() error { return os.Chmod(s.rootDir, 0300) } -// List a subset of the refs (currently, branches and tags) of a git repo +// ListRefs List a subset of the refs (currently, branches and tags) of a git repo func (s *Service) ListRefs(ctx context.Context, q *apiclient.ListRefsRequest) (*apiclient.Refs, error) { gitClient, err := s.newClient(q.Repo) if err != nil { - return nil, err + return nil, fmt.Errorf("error creating git client: %w", err) } s.metricsServer.IncPendingRepoRequest(q.Repo.Repo) @@ -202,7 +204,7 @@ func (s *Service) ListRefs(ctx context.Context, q *apiclient.ListRefsRequest) (* func (s *Service) ListApps(ctx context.Context, q *apiclient.ListAppsRequest) (*apiclient.AppList, error) { gitClient, commitSHA, err := s.newClientResolveRevision(q.Repo, q.Revision) if err != nil { - return nil, err + return nil, fmt.Errorf("error setting up git client and resolving given revision: %w", err) } if apps, err := s.cache.ListApps(q.Repo.Repo, commitSHA); err == nil { log.Infof("cache hit: %s/%s", q.Repo.Repo, q.Revision) @@ -217,13 +219,13 @@ func (s *Service) ListApps(ctx context.Context, q *apiclient.ListAppsRequest) (* }) if err != nil { - return nil, err + return nil, fmt.Errorf("error acquiring repository lock: %w", err) } defer io.Close(closer) - apps, err := discovery.Discover(ctx, gitClient.Root(), q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs) + apps, err := discovery.Discover(ctx, gitClient.Root(), gitClient.Root(), q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs, []string{}) if err != nil { - return nil, err + return nil, fmt.Errorf("error discovering applications: %w", err) } err = s.cache.SetApps(q.Repo.Repo, commitSHA, apps) if err != nil { @@ -242,7 +244,7 @@ func (s *Service) ListPlugins(ctx context.Context, _ *empty.Empty) (*apiclient.P return nil, fmt.Errorf("failed to get plugins from dir %v, error=%w", pluginSockFilePath, err) } - plugins := []*apiclient.PluginInfo{} + var plugins []*apiclient.PluginInfo for _, file := range sockFiles { if file.Type() == os.ModeSocket { plugins = append(plugins, &apiclient.PluginInfo{Name: strings.TrimSuffix(file.Name(), ".sock")}) @@ -287,34 +289,42 @@ func (s *Service) runRepoOperation( repo *v1alpha1.Repository, source *v1alpha1.ApplicationSource, verifyCommit bool, - cacheFn func(cacheKey string, firstInvocation bool) (bool, error), + cacheFn func(cacheKey string, refSourceCommitSHAs cache.ResolvedRevisions, firstInvocation bool) (bool, error), operation func(repoRoot, commitSHA, cacheKey string, ctxSrc operationContextSrc) error, settings operationSettings, - hasMultipleSources bool) error { + hasMultipleSources bool, + refSources map[string]*v1alpha1.RefTarget) error { if sanitizer, ok := grpc.SanitizerFromContext(ctx); ok { - // make sure randomized path replaced with '.' in the error message - sanitizer.AddRegexReplacement(regexp.MustCompile(`(`+regexp.QuoteMeta(s.rootDir)+`/.*?)/`), ".") + // make sure a randomized path replaced with '.' in the error message + sanitizer.AddRegexReplacement(getRepoSanitizerRegex(s.rootDir), "") } var gitClient git.Client var helmClient helm.Client var err error + gitClientOpts := git.WithCache(s.cache, !settings.noRevisionCache && !settings.noCache) revision = textutils.FirstNonEmpty(revision, source.TargetRevision) + unresolvedRevision := revision if source.IsHelm() { helmClient, revision, err = s.newHelmClientResolveRevision(repo, revision, source.Chart, settings.noCache || settings.noRevisionCache) if err != nil { return err } } else { - gitClient, revision, err = s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, !settings.noRevisionCache && !settings.noCache)) + gitClient, revision, err = s.newClientResolveRevision(repo, revision, gitClientOpts) if err != nil { return err } } + repoRefs, err := resolveReferencedSources(hasMultipleSources, source.Helm, refSources, s.newClientResolveRevision, gitClientOpts) + if err != nil { + return err + } + if !settings.noCache { - if ok, err := cacheFn(revision, true); ok { + if ok, err := cacheFn(revision, repoRefs, true); ok { return err } } @@ -330,14 +340,6 @@ func (s *Service) runRepoOperation( defer settings.sem.Release(1) } - // do not generate manifests if Path and Chart fields are not set for a source in Multiple Sources - if hasMultipleSources && source.Path == "" && source.Chart == "" { - log.WithFields(map[string]interface{}{ - "source": source, - }).Debugf("not generating manifests as path and chart fields are empty") - return nil - } - if source.IsHelm() { if settings.noCache { err = helmClient.CleanChartCache(source.Chart, revision) @@ -349,7 +351,7 @@ func (s *Service) runRepoOperation( if source.Helm != nil { helmPassCredentials = source.Helm.PassCredentials } - chartPath, closer, err := helmClient.ExtractChart(source.Chart, revision, helmPassCredentials) + chartPath, closer, err := helmClient.ExtractChart(source.Chart, revision, helmPassCredentials, s.initConstants.HelmManifestMaxExtractedSize, s.initConstants.DisableHelmManifestMaxExtractedSize) if err != nil { return err } @@ -403,14 +405,20 @@ func (s *Service) runRepoOperation( } } - commitSHA, err := gitClient.CommitSHA() - if err != nil { - return err + var commitSHA string + if hasMultipleSources { + commitSHA = revision + } else { + commit, err := gitClient.CommitSHA() + if err != nil { + return fmt.Errorf("failed to get commit SHA: %w", err) + } + commitSHA = commit } // double-check locking if !settings.noCache { - if ok, err := cacheFn(revision, false); ok { + if ok, err := cacheFn(revision, repoRefs, false); ok { return err } } @@ -420,7 +428,16 @@ func (s *Service) runRepoOperation( return operation(gitClient.Root(), commitSHA, revision, func() (*operationContext, error) { var signature string if verifyCommit { - signature, err = gitClient.VerifyCommitSignature(revision) + // When the revision is an annotated tag, we need to pass the unresolved revision (i.e. the tag name) + // to the verification routine. For everything else, we work with the SHA that the target revision is + // pointing to (i.e. the resolved revision). + var rev string + if gitClient.IsAnnotatedTag(revision) { + rev = unresolvedRevision + } else { + rev = revision + } + signature, err = gitClient.VerifyCommitSignature(rev) if err != nil { return nil, err } @@ -434,11 +451,78 @@ func (s *Service) runRepoOperation( } } +func getRepoSanitizerRegex(rootDir string) *regexp.Regexp { + // This regex assumes that the sensitive part of the path (the component immediately after "rootDir") contains no + // spaces. This assumption allows us to avoid sanitizing "more info" in "/tmp/_argocd-repo/SENSITIVE more info". + // + // The no-spaces assumption holds for our actual use case, which is "/tmp/_argocd-repo/{random UUID}". The UUID will + // only ever contain digits and hyphens. + return regexp.MustCompile(regexp.QuoteMeta(rootDir) + `/[^ /]*`) +} + +type gitClientGetter func(repo *v1alpha1.Repository, revision string, opts ...git.ClientOpts) (git.Client, string, error) + +// resolveReferencedSources resolves the revisions for the given referenced sources. This lets us invalidate the cached +// when one or more referenced sources change. +// +// Much of this logic is duplicated in runManifestGenAsync. If making changes here, check whether runManifestGenAsync +// should be updated. +func resolveReferencedSources(hasMultipleSources bool, source *v1alpha1.ApplicationSourceHelm, refSources map[string]*v1alpha1.RefTarget, newClientResolveRevision gitClientGetter, gitClientOpts git.ClientOpts) (map[string]string, error) { + repoRefs := make(map[string]string) + if !hasMultipleSources || source == nil { + return repoRefs, nil + } + + for _, valueFile := range source.ValueFiles { + if strings.HasPrefix(valueFile, "$") { + refVar := strings.Split(valueFile, "/")[0] + + refSourceMapping, ok := refSources[refVar] + if !ok { + if len(refSources) == 0 { + return nil, fmt.Errorf("source referenced %q, but no source has a 'ref' field defined", refVar) + } + refKeys := make([]string, 0) + for refKey := range refSources { + refKeys = append(refKeys, refKey) + } + return nil, fmt.Errorf("source referenced %q, which is not one of the available sources (%s)", refVar, strings.Join(refKeys, ", ")) + } + if refSourceMapping.Chart != "" { + return nil, fmt.Errorf("source has a 'chart' field defined, but Helm charts are not yet not supported for 'ref' sources") + } + normalizedRepoURL := git.NormalizeGitURL(refSourceMapping.Repo.Repo) + _, ok = repoRefs[normalizedRepoURL] + if !ok { + _, referencedCommitSHA, err := newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision, gitClientOpts) + if err != nil { + log.Errorf("Failed to get git client for repo %s: %v", refSourceMapping.Repo.Repo, err) + return nil, fmt.Errorf("failed to get git client for repo %s", refSourceMapping.Repo.Repo) + } + + repoRefs[normalizedRepoURL] = referencedCommitSHA + } + } + } + return repoRefs, nil +} + func (s *Service) GenerateManifest(ctx context.Context, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) { var res *apiclient.ManifestResponse var err error - cacheFn := func(cacheKey string, firstInvocation bool) (bool, error) { - ok, resp, err := s.getManifestCacheEntry(cacheKey, q, firstInvocation) + + // Skip this path for ref only sources + if q.HasMultipleSources && q.ApplicationSource.Path == "" && q.ApplicationSource.Chart == "" && q.ApplicationSource.Ref != "" { + log.Debugf("Skipping manifest generation for ref only source for application: %s and ref %s", q.AppName, q.ApplicationSource.Ref) + _, revision, err := s.newClientResolveRevision(q.Repo, q.Revision, git.WithCache(s.cache, !q.NoRevisionCache && !q.NoCache)) + res = &apiclient.ManifestResponse{ + Revision: revision, + } + return res, err + } + + cacheFn := func(cacheKey string, refSourceCommitSHAs cache.ResolvedRevisions, firstInvocation bool) (bool, error) { + ok, resp, err := s.getManifestCacheEntry(cacheKey, q, refSourceCommitSHAs, firstInvocation) res = resp return ok, err } @@ -447,6 +531,17 @@ func (s *Service) GenerateManifest(ctx context.Context, q *apiclient.ManifestReq var promise *ManifestResponsePromise operation := func(repoRoot, commitSHA, cacheKey string, ctxSrc operationContextSrc) error { + // do not generate manifests if Path and Chart fields are not set for a source in Multiple Sources + if q.HasMultipleSources && q.ApplicationSource.Path == "" && q.ApplicationSource.Chart == "" { + log.WithFields(map[string]interface{}{ + "source": q.ApplicationSource, + }).Debugf("not generating manifests as path and chart fields are empty") + res = &apiclient.ManifestResponse{ + Revision: commitSHA, + } + return nil + } + promise = s.runManifestGen(ctx, repoRoot, commitSHA, cacheKey, ctxSrc, q) // The fist channel to send the message will resume this operation. // The main purpose for using channels here is to be able to unlock @@ -465,7 +560,7 @@ func (s *Service) GenerateManifest(ctx context.Context, q *apiclient.ManifestReq } settings := operationSettings{sem: s.parallelismLimitSemaphore, noCache: q.NoCache, noRevisionCache: q.NoRevisionCache, allowConcurrent: q.ApplicationSource.AllowsConcurrentProcessing()} - err = s.runRepoOperation(ctx, q.Revision, q.Repo, q.ApplicationSource, q.VerifySignature, cacheFn, operation, settings, q.HasMultipleSources) + err = s.runRepoOperation(ctx, q.Revision, q.Repo, q.ApplicationSource, q.VerifySignature, cacheFn, operation, settings, q.HasMultipleSources, q.RefSources) // if the tarDoneCh message is sent it means that the manifest // generation is being managed by the cmp-server. In this case @@ -479,10 +574,6 @@ func (s *Service) GenerateManifest(ctx context.Context, q *apiclient.ManifestReq return nil, err } } - - if q.HasMultipleSources && err == nil && res == nil { - res = &apiclient.ManifestResponse{} - } return res, err } @@ -596,9 +687,12 @@ func (s *Service) runManifestGen(ctx context.Context, repoRoot, commitSHA, cache } type repoRef struct { - revision string + // revision is the git revision - can be any valid revision like a branch, tag, or commit SHA. + revision string + // commitSHA is the actual commit to which revision refers. commitSHA string - key string + // key is the name of the key which was used to reference this repo. + key string } func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, cacheKey string, opContextSrc operationContextSrc, q *apiclient.ManifestRequest, ch *generateManifestCh) { @@ -610,13 +704,15 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, // GenerateManifests mutates the source (applies overrides). Those overrides shouldn't be reflected in the cache // key. Overrides will break the cache anyway, because changes to overrides will change the revision. appSourceCopy := q.ApplicationSource.DeepCopy() + repoRefs := make(map[string]repoRef) var manifestGenResult *apiclient.ManifestResponse opContext, err := opContextSrc() if err == nil { + // Much of the multi-source handling logic is duplicated in resolveReferencedSources. If making changes here, + // check whether they should be replicated in resolveReferencedSources. if q.HasMultipleSources { if q.ApplicationSource.Helm != nil { - repoRefs := make(map[string]repoRef) // Checkout every one of the referenced sources to the target revision before generating Manifests for _, valueFile := range q.ApplicationSource.Helm.ValueFiles { @@ -647,9 +743,10 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, return } } else { - gitClient, referencedCommitSHA, err := s.newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision) + gitClient, referencedCommitSHA, err := s.newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision, git.WithCache(s.cache, !q.NoRevisionCache && !q.NoCache)) if err != nil { - ch.errCh <- fmt.Errorf("failed to get git client for repo %s", q.Repo.Repo) + log.Errorf("Failed to get git client for repo %s: %v", refSourceMapping.Repo.Repo, err) + ch.errCh <- fmt.Errorf("failed to get git client for repo %s", refSourceMapping.Repo.Repo) return } @@ -702,17 +799,28 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, manifestGenResult, err = GenerateManifests(ctx, opContext.appPath, repoRoot, commitSHA, q, false, s.gitCredsStore, s.initConstants.MaxCombinedDirectoryManifestsSize, s.gitRepoPaths, WithCMPTarDoneChannel(ch.tarDoneCh), WithCMPTarExcludedGlobs(s.initConstants.CMPTarExcludedGlobs)) } + refSourceCommitSHAs := make(map[string]string) + if len(repoRefs) > 0 { + for normalizedURL, repoRef := range repoRefs { + refSourceCommitSHAs[normalizedURL] = repoRef.commitSHA + } + } if err != nil { + logCtx := log.WithFields(log.Fields{ + "application": q.AppName, + "appNamespace": q.Namespace, + }) + // If manifest generation error caching is enabled if s.initConstants.PauseGenerationAfterFailedGenerationAttempts > 0 { - cache.LogDebugManifestCacheKeyFields("getting manifests cache", "GenerateManifests error", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName) + cache.LogDebugManifestCacheKeyFields("getting manifests cache", "GenerateManifests error", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs) // Retrieve a new copy (if available) of the cached response: this ensures we are updating the latest copy of the cache, // rather than a copy of the cache that occurred before (a potentially lengthy) manifest generation. innerRes := &cache.CachedManifestResponse{} - cacheErr := s.cache.GetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, innerRes) - if cacheErr != nil && cacheErr != reposervercache.ErrCacheMiss { - log.Warnf("manifest cache set error %s: %v", appSourceCopy.String(), cacheErr) + cacheErr := s.cache.GetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, innerRes, refSourceCommitSHAs) + if cacheErr != nil && cacheErr != cache.ErrCacheMiss { + logCtx.Warnf("manifest cache get error %s: %v", appSourceCopy.String(), cacheErr) ch.errCh <- cacheErr return } @@ -723,14 +831,14 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, innerRes.FirstFailureTimestamp = s.now().Unix() } - cache.LogDebugManifestCacheKeyFields("setting manifests cache", "GenerateManifests error", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName) + cache.LogDebugManifestCacheKeyFields("setting manifests cache", "GenerateManifests error", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs) // Update the cache to include failure information innerRes.NumberOfConsecutiveFailures++ innerRes.MostRecentError = err.Error() - cacheErr = s.cache.SetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, innerRes) + cacheErr = s.cache.SetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, innerRes, refSourceCommitSHAs) if cacheErr != nil { - log.Warnf("manifest cache set error %s: %v", appSourceCopy.String(), cacheErr) + logCtx.Warnf("manifest cache set error %s: %v", appSourceCopy.String(), cacheErr) ch.errCh <- cacheErr return } @@ -740,7 +848,7 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, return } - cache.LogDebugManifestCacheKeyFields("setting manifests cache", "fresh GenerateManifests response", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName) + cache.LogDebugManifestCacheKeyFields("setting manifests cache", "fresh GenerateManifests response", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs) // Otherwise, no error occurred, so ensure the manifest generation error data in the cache entry is reset before we cache the value manifestGenCacheEntry := cache.CachedManifestResponse{ @@ -752,7 +860,7 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, } manifestGenResult.Revision = commitSHA manifestGenResult.VerifyResult = opContext.verificationResult - err = s.cache.SetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, &manifestGenCacheEntry) + err = s.cache.SetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, &manifestGenCacheEntry, refSourceCommitSHAs) if err != nil { log.Warnf("manifest cache set error %s/%s: %v", appSourceCopy.String(), cacheKey, err) } @@ -765,11 +873,11 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, // - If the cache is not empty, but the cache value is an error AND that generation error has expired // and returns true otherwise. // If true is returned, either the second or third parameter (but not both) will contain a value from the cache (a ManifestResponse, or error, respectively) -func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRequest, firstInvocation bool) (bool, *apiclient.ManifestResponse, error) { - cache.LogDebugManifestCacheKeyFields("getting manifests cache", "GenerateManifest API call", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName) +func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRequest, refSourceCommitSHAs cache.ResolvedRevisions, firstInvocation bool) (bool, *apiclient.ManifestResponse, error) { + cache.LogDebugManifestCacheKeyFields("getting manifests cache", "GenerateManifest API call", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs) res := cache.CachedManifestResponse{} - err := s.cache.GetManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, &res) + err := s.cache.GetManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, &res, refSourceCommitSHAs) if err == nil { // The cache contains an existing value @@ -787,10 +895,10 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe // After X minutes, reset the cache and retry the operation (e.g. perhaps the error is ephemeral and has passed) if elapsedTimeInMinutes >= s.initConstants.PauseGenerationOnFailureForMinutes { - cache.LogDebugManifestCacheKeyFields("deleting manifests cache", "manifest hash did not match or cached response is empty", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName) + cache.LogDebugManifestCacheKeyFields("deleting manifests cache", "manifest hash did not match or cached response is empty", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs) // We can now try again, so reset the cache state and run the operation below - err = s.cache.DeleteManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName) + err = s.cache.DeleteManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs) if err != nil { log.Warnf("manifest cache set error %s/%s: %v", q.ApplicationSource.String(), cacheKey, err) } @@ -803,10 +911,10 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe if s.initConstants.PauseGenerationOnFailureForRequests > 0 && res.NumberOfCachedResponsesReturned > 0 { if res.NumberOfCachedResponsesReturned >= s.initConstants.PauseGenerationOnFailureForRequests { - cache.LogDebugManifestCacheKeyFields("deleting manifests cache", "reset after paused generation count", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName) + cache.LogDebugManifestCacheKeyFields("deleting manifests cache", "reset after paused generation count", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs) // We can now try again, so reset the error cache state and run the operation below - err = s.cache.DeleteManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName) + err = s.cache.DeleteManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs) if err != nil { log.Warnf("manifest cache set error %s/%s: %v", q.ApplicationSource.String(), cacheKey, err) } @@ -821,12 +929,12 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe cachedErrorResponse := fmt.Errorf(cachedManifestGenerationPrefix+": %s", res.MostRecentError) if firstInvocation { - cache.LogDebugManifestCacheKeyFields("setting manifests cache", "update error count", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName) + cache.LogDebugManifestCacheKeyFields("setting manifests cache", "update error count", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs) // Increment the number of returned cached responses and push that new value to the cache // (if we have not already done so previously in this function) res.NumberOfCachedResponsesReturned++ - err = s.cache.SetManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, &res) + err = s.cache.SetManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, &res, refSourceCommitSHAs) if err != nil { log.Warnf("manifest cache set error %s/%s: %v", q.ApplicationSource.String(), cacheKey, err) } @@ -846,7 +954,7 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe return true, res.ManifestResponse, nil } - if err != reposervercache.ErrCacheMiss { + if err != cache.ErrCacheMiss { log.Warnf("manifest cache error %s: %v", q.ApplicationSource.String(), err) } else { log.Infof("manifest cache miss: %s/%s", q.ApplicationSource.String(), cacheKey) @@ -855,12 +963,52 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe return false, nil, nil } -func getHelmRepos(repositories []*v1alpha1.Repository) []helm.HelmRepository { - repos := make([]helm.HelmRepository, 0) +func getHelmRepos(appPath string, repositories []*v1alpha1.Repository, helmRepoCreds []*v1alpha1.RepoCreds) ([]helm.HelmRepository, error) { + dependencies, err := getHelmDependencyRepos(appPath) + if err != nil { + return nil, fmt.Errorf("error retrieving helm dependency repos: %w", err) + } + reposByName := make(map[string]*v1alpha1.Repository) + reposByUrl := make(map[string]*v1alpha1.Repository) for _, repo := range repositories { + reposByUrl[repo.Repo] = repo + if repo.Name != "" { + reposByName[repo.Name] = repo + } + } + + repos := make([]helm.HelmRepository, 0) + for _, dep := range dependencies { + // find matching repo credentials by URL or name + repo, ok := reposByUrl[dep.Repo] + if !ok && dep.Name != "" { + repo, ok = reposByName[dep.Name] + } + if !ok { + // if no matching repo credentials found, use the repo creds from the credential list + repo = &v1alpha1.Repository{Repo: dep.Repo, Name: dep.Name, EnableOCI: dep.EnableOCI} + if repositoryCredential := getRepoCredential(helmRepoCreds, dep.Repo); repositoryCredential != nil { + repo.EnableOCI = repositoryCredential.EnableOCI + repo.Password = repositoryCredential.Password + repo.Username = repositoryCredential.Username + repo.SSHPrivateKey = repositoryCredential.SSHPrivateKey + repo.TLSClientCertData = repositoryCredential.TLSClientCertData + repo.TLSClientCertKey = repositoryCredential.TLSClientCertKey + } else if repo.EnableOCI { + // finally if repo is OCI and no credentials found, use the first OCI credential matching by hostname + // see https://github.com/argoproj/argo-cd/issues/14636 + for _, cred := range repositories { + if depURL, err := url.Parse("oci://" + dep.Repo); err == nil && cred.EnableOCI && depURL.Host == cred.Repo { + repo.Username = cred.Username + repo.Password = cred.Password + break + } + } + } + } repos = append(repos, helm.HelmRepository{Name: repo.Name, Repo: repo.Repo, Creds: repo.GetHelmCreds(), EnableOci: repo.EnableOCI}) } - return repos + return repos, nil } type dependencies struct { @@ -875,18 +1023,27 @@ func getHelmDependencyRepos(appPath string) ([]*v1alpha1.Repository, error) { repos := make([]*v1alpha1.Repository, 0) f, err := os.ReadFile(filepath.Join(appPath, "Chart.yaml")) if err != nil { - return nil, err + return nil, fmt.Errorf("error reading helm chart from %s: %w", filepath.Join(appPath, "Chart.yaml"), err) } d := &dependencies{} if err = yaml.Unmarshal(f, d); err != nil { - return nil, err + return nil, fmt.Errorf("error unmarshalling the helm chart while getting helm dependency repos: %w", err) } for _, r := range d.Dependencies { - if u, err := url.Parse(r.Repository); err == nil && (u.Scheme == "https" || u.Scheme == "oci") { + if strings.HasPrefix(r.Repository, "@") { + repos = append(repos, &v1alpha1.Repository{ + Name: r.Repository[1:], + }) + } else if strings.HasPrefix(r.Repository, "alias:") { + repos = append(repos, &v1alpha1.Repository{ + Name: strings.TrimPrefix(r.Repository, "alias:"), + }) + } else if u, err := url.Parse(r.Repository); err == nil && (u.Scheme == "https" || u.Scheme == "oci") { repo := &v1alpha1.Repository{ - Repo: r.Repository, + // trimming oci:// prefix since it is currently not supported by Argo CD (OCI repos just have no scheme) + Repo: strings.TrimPrefix(r.Repository, "oci://"), Name: sanitizeRepoName(r.Repository), EnableOCI: u.Scheme == "oci", } @@ -901,15 +1058,6 @@ func sanitizeRepoName(repoName string) string { return strings.ReplaceAll(repoName, "/", "-") } -func repoExists(repo string, repos []*v1alpha1.Repository) bool { - for _, r := range repos { - if strings.TrimPrefix(repo, ociPrefix) == strings.TrimPrefix(r.Repo, ociPrefix) { - return true - } - } - return false -} - func isConcurrencyAllowed(appPath string) bool { if _, err := os.Stat(path.Join(appPath, allowConcurrencyFile)); err == nil { return true @@ -927,9 +1075,9 @@ func runHelmBuild(appPath string, h helm.Helm) error { manifestGenerateLock.Lock(appPath) defer manifestGenerateLock.Unlock(appPath) - // the `helm dependency build` is potentially time consuming 1~2 seconds - // marker file is used to check if command already run to avoid running it again unnecessary - // file is removed when repository re-initialized (e.g. when another commit is processed) + // the `helm dependency build` is potentially a time-consuming 1~2 seconds, + // a marker file is used to check if command already run to avoid running it again unnecessarily + // the file is removed when repository is re-initialized (e.g. when another commit is processed) markerFile := path.Join(appPath, helmDepUpMarkerFile) _, err := os.Stat(markerFile) if err == nil { @@ -940,39 +1088,18 @@ func runHelmBuild(appPath string, h helm.Helm) error { err = h.DependencyBuild() if err != nil { - return err + return fmt.Errorf("error building helm chart dependencies: %w", err) } return os.WriteFile(markerFile, []byte("marker"), 0644) } -func populateRequestRepos(appPath string, q *apiclient.ManifestRequest) error { - repos, err := getHelmDependencyRepos(appPath) - if err != nil { - return err - } - - for _, r := range repos { - if !repoExists(r.Repo, q.Repos) { - repositoryCredential := getRepoCredential(q.HelmRepoCreds, r.Repo) - if repositoryCredential != nil { - if repositoryCredential.EnableOCI { - r.Repo = strings.TrimPrefix(r.Repo, ociPrefix) - } - r.EnableOCI = repositoryCredential.EnableOCI - r.Password = repositoryCredential.Password - r.Username = repositoryCredential.Username - r.SSHPrivateKey = repositoryCredential.SSHPrivateKey - r.TLSClientCertData = repositoryCredential.TLSClientCertData - r.TLSClientCertKey = repositoryCredential.TLSClientCertKey - } - q.Repos = append(q.Repos, r) - } - } - return nil +func isSourcePermitted(url string, repos []string) bool { + p := v1alpha1.AppProject{Spec: v1alpha1.AppProjectSpec{SourceRepos: repos}} + return p.IsSourcePermitted(v1alpha1.ApplicationSource{RepoURL: url}) } func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclient.ManifestRequest, isLocal bool, gitRepoPaths io.TempPaths) ([]*unstructured.Unstructured, error) { - concurrencyAllowed := isConcurrencyAllowed(appPath) + concurrencyAllowed := helmConcurrencyDefault || isConcurrencyAllowed(appPath) if !concurrencyAllowed { manifestGenerateLock.Lock(appPath) defer manifestGenerateLock.Unlock(appPath) @@ -982,7 +1109,7 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie // contain any underscore characters and must not exceed 53 characters. // We are not interested in the fully qualified application name while // templating, thus, we just use the name part of the identifier. - appName, _ := argo.ParseAppInstanceName(q.AppName, "") + appName, _ := argo.ParseInstanceName(q.AppName, "") templateOpts := &helm.TemplateOpts{ Name: appName, @@ -1007,15 +1134,15 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie resolvedValueFiles, err := getResolvedValueFiles(appPath, repoRoot, env, q.GetValuesFileSchemes(), appHelm.ValueFiles, q.RefSources, gitRepoPaths, appHelm.IgnoreMissingValueFiles) if err != nil { - return nil, err + return nil, fmt.Errorf("error resolving helm value files: %w", err) } templateOpts.Values = resolvedValueFiles - if appHelm.Values != "" { + if !appHelm.ValuesIsEmpty() { rand, err := uuid.NewRandom() if err != nil { - return nil, err + return nil, fmt.Errorf("error generating random filename for Helm values file: %w", err) } p := path.Join(os.TempDir(), rand.String()) defer func() { @@ -1024,9 +1151,9 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie _ = os.RemoveAll(p) } }() - err = os.WriteFile(p, []byte(appHelm.Values), 0644) + err = os.WriteFile(p, appHelm.ValuesYAML(), 0644) if err != nil { - return nil, err + return nil, fmt.Errorf("error writing helm values file: %w", err) } templateOpts.Values = append(templateOpts.Values, pathutil.ResolvedFilePath(p)) } @@ -1041,7 +1168,7 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie for _, p := range appHelm.FileParameters { resolvedPath, _, err := pathutil.ResolveValueFilePathOrUrl(appPath, repoRoot, env.Envsubst(p.Path), q.GetValuesFileSchemes()) if err != nil { - return nil, err + return nil, fmt.Errorf("error resolving helm value file path: %w", err) } templateOpts.SetFile[p.Name] = resolvedPath } @@ -1058,24 +1185,25 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie templateOpts.SetString[i] = env.Envsubst(j) } - if err := populateRequestRepos(appPath, q); err != nil { - return nil, fmt.Errorf("failed parsing dependencies: %v", err) - } - var proxy string if q.Repo != nil { proxy = q.Repo.Proxy } - h, err := helm.NewHelmApp(appPath, getHelmRepos(q.Repos), isLocal, version, proxy, passCredentials) + helmRepos, err := getHelmRepos(appPath, q.Repos, q.HelmRepoCreds) if err != nil { - return nil, err + return nil, fmt.Errorf("error getting helm repos: %w", err) + } + + h, err := helm.NewHelmApp(appPath, helmRepos, isLocal, version, proxy, passCredentials) + if err != nil { + return nil, fmt.Errorf("error initializing helm app object: %w", err) } defer h.Dispose() err = h.Init() if err != nil { - return nil, err + return nil, fmt.Errorf("error initializing helm app: %w", err) } out, err := h.Template(templateOpts) @@ -1091,6 +1219,24 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie } if err != nil { + var reposNotPermitted []string + // We do a sanity check here to give a nicer error message in case any of the Helm repositories are not permitted by + // the AppProject which the application is a part of + for _, repo := range helmRepos { + msg := err.Error() + + chartCannotBeReached := strings.Contains(msg, "is not a valid chart repository or cannot be reached") + couldNotDownloadChart := strings.Contains(msg, "could not download") + + if (chartCannotBeReached || couldNotDownloadChart) && !isSourcePermitted(repo.Repo, q.ProjectSourceRepos) { + reposNotPermitted = append(reposNotPermitted, repo.Repo) + } + } + + if len(reposNotPermitted) > 0 { + return nil, status.Errorf(codes.PermissionDenied, "helm repos %s are not permitted in project '%s'", strings.Join(reposNotPermitted, ", "), q.ProjectName) + } + return nil, err } @@ -1123,13 +1269,13 @@ func getResolvedValueFiles( // If the $-prefixed path appears to reference another source, do env substitution _after_ resolving that source. resolvedPath, err = getResolvedRefValueFile(rawValueFile, env, allowedValueFilesSchemas, referencedSource.Repo.Repo, gitRepoPaths) if err != nil { - return nil, err + return nil, fmt.Errorf("error resolving value file path: %w", err) } } else { // This will resolve val to an absolute path (or an URL) resolvedPath, isRemote, err = pathutil.ResolveValueFilePathOrUrl(appPath, repoRoot, env.Envsubst(rawValueFile), allowedValueFilesSchemas) if err != nil { - return nil, err + return nil, fmt.Errorf("error resolving value file path: %w", err) } } @@ -1166,7 +1312,7 @@ func getResolvedRefValueFile( // Resolve the path relative to the referenced repo and block any attempt at traversal. resolvedPath, _, err := pathutil.ResolveValueFilePathOrUrl(repoPath, repoPath, env.Envsubst(substitutedPath), allowedValueFilesSchemas) if err != nil { - return "", err + return "", fmt.Errorf("error resolving value file path: %w", err) } return resolvedPath, nil } @@ -1225,19 +1371,19 @@ func WithCMPTarExcludedGlobs(excludedGlobs []string) GenerateManifestOpt { func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string, q *apiclient.ManifestRequest, isLocal bool, gitCredsStore git.CredsStore, maxCombinedManifestQuantity resource.Quantity, gitRepoPaths io.TempPaths, opts ...GenerateManifestOpt) (*apiclient.ManifestResponse, error) { opt := newGenerateManifestOpt(opts...) var targetObjs []*unstructured.Unstructured - var dest *v1alpha1.ApplicationDestination resourceTracking := argo.NewResourceTracking() - appSourceType, err := GetAppSourceType(ctx, q.ApplicationSource, appPath, q.AppName, q.EnabledSourceTypes, opt.cmpTarExcludedGlobs) + env := newEnv(q, revision) + + appSourceType, err := GetAppSourceType(ctx, q.ApplicationSource, appPath, repoRoot, q.AppName, q.EnabledSourceTypes, opt.cmpTarExcludedGlobs, env.Environ()) if err != nil { - return nil, err + return nil, fmt.Errorf("error getting app source type: %w", err) } repoURL := "" if q.Repo != nil { repoURL = q.Repo.Repo } - env := newEnv(q, revision) switch appSourceType { case v1alpha1.ApplicationSourceTypeHelm: @@ -1247,31 +1393,17 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string, if q.KustomizeOptions != nil { kustomizeBinary = q.KustomizeOptions.BinaryPath } - k := kustomize.NewKustomizeApp(appPath, q.Repo.GetGitCreds(gitCredsStore), repoURL, kustomizeBinary) + k := kustomize.NewKustomizeApp(repoRoot, appPath, q.Repo.GetGitCreds(gitCredsStore), repoURL, kustomizeBinary) targetObjs, _, err = k.Build(q.ApplicationSource.Kustomize, q.KustomizeOptions, env) case v1alpha1.ApplicationSourceTypePlugin: - var plugin *v1alpha1.ConfigManagementPlugin - if q.ApplicationSource.Plugin != nil && q.ApplicationSource.Plugin.Name != "" { - plugin = findPlugin(q.Plugins, q.ApplicationSource.Plugin.Name) + pluginName := "" + if q.ApplicationSource.Plugin != nil { + pluginName = q.ApplicationSource.Plugin.Name } - if plugin != nil { - // argocd-cm deprecated plugin is being used - targetObjs, err = runConfigManagementPlugin(appPath, repoRoot, env, q, q.Repo.GetGitCreds(gitCredsStore), plugin) - log.WithFields(map[string]interface{}{ - "application": q.AppName, - "plugin": q.ApplicationSource.Plugin.Name, - }).Warnf(common.ConfigMapPluginDeprecationWarning) - } else { - // if the named plugin was not found in argocd-cm try sidecar plugin - pluginName := "" - if q.ApplicationSource.Plugin != nil { - pluginName = q.ApplicationSource.Plugin.Name - } - // if pluginName is provided it has to be `-` or just `` if plugin version is empty - targetObjs, err = runConfigManagementPluginSidecars(ctx, appPath, repoRoot, pluginName, env, q, q.Repo.GetGitCreds(gitCredsStore), opt.cmpTarDoneCh, opt.cmpTarExcludedGlobs) - if err != nil { - err = fmt.Errorf("plugin sidecar failed. %s", err.Error()) - } + // if pluginName is provided it has to be `-` or just `` if plugin version is empty + targetObjs, err = runConfigManagementPluginSidecars(ctx, appPath, repoRoot, pluginName, env, q, opt.cmpTarDoneCh, opt.cmpTarExcludedGlobs) + if err != nil { + err = fmt.Errorf("plugin sidecar failed. %s", err.Error()) } case v1alpha1.ApplicationSourceTypeDirectory: var directory *v1alpha1.ApplicationSourceDirectory @@ -1314,7 +1446,7 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string, if q.AppLabelKey != "" && q.AppName != "" && !kube.IsCRD(target) { err = resourceTracking.SetAppInstance(target, q.AppLabelKey, q.AppName, q.Namespace, v1alpha1.TrackingMethod(q.TrackingMethod)) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to set app instance tracking info on manifest: %w", err) } } manifestStr, err := json.Marshal(target.Object) @@ -1325,28 +1457,38 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string, } } - res := apiclient.ManifestResponse{ + return &apiclient.ManifestResponse{ Manifests: manifests, SourceType: string(appSourceType), - } - if dest != nil { - res.Namespace = dest.Namespace - res.Server = dest.Server - } - return &res, nil + }, nil } func newEnv(q *apiclient.ManifestRequest, revision string) *v1alpha1.Env { + shortRevision := revision + if len(shortRevision) > 7 { + shortRevision = shortRevision[:7] + } return &v1alpha1.Env{ &v1alpha1.EnvEntry{Name: "ARGOCD_APP_NAME", Value: q.AppName}, &v1alpha1.EnvEntry{Name: "ARGOCD_APP_NAMESPACE", Value: q.Namespace}, &v1alpha1.EnvEntry{Name: "ARGOCD_APP_REVISION", Value: revision}, + &v1alpha1.EnvEntry{Name: "ARGOCD_APP_REVISION_SHORT", Value: shortRevision}, &v1alpha1.EnvEntry{Name: "ARGOCD_APP_SOURCE_REPO_URL", Value: q.Repo.Repo}, &v1alpha1.EnvEntry{Name: "ARGOCD_APP_SOURCE_PATH", Value: q.ApplicationSource.Path}, &v1alpha1.EnvEntry{Name: "ARGOCD_APP_SOURCE_TARGET_REVISION", Value: q.ApplicationSource.TargetRevision}, } } +func newEnvRepoQuery(q *apiclient.RepoServerAppDetailsQuery, revision string) *v1alpha1.Env { + return &v1alpha1.Env{ + &v1alpha1.EnvEntry{Name: "ARGOCD_APP_NAME", Value: q.AppName}, + &v1alpha1.EnvEntry{Name: "ARGOCD_APP_REVISION", Value: revision}, + &v1alpha1.EnvEntry{Name: "ARGOCD_APP_SOURCE_REPO_URL", Value: q.Repo.Repo}, + &v1alpha1.EnvEntry{Name: "ARGOCD_APP_SOURCE_PATH", Value: q.Source.Path}, + &v1alpha1.EnvEntry{Name: "ARGOCD_APP_SOURCE_TARGET_REVISION", Value: q.Source.TargetRevision}, + } +} + // mergeSourceParameters merges parameter overrides from one or more files in // the Git repo into the given ApplicationSource objects. // @@ -1406,8 +1548,8 @@ func mergeSourceParameters(source *v1alpha1.ApplicationSource, path, appName str } // GetAppSourceType returns explicit application source type or examines a directory and determines its application source type -func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, path, appName string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (v1alpha1.ApplicationSourceType, error) { - err := mergeSourceParameters(source, path, appName) +func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, appPath, repoPath, appName string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string, env []string) (v1alpha1.ApplicationSourceType, error) { + err := mergeSourceParameters(source, appPath, appName) if err != nil { return "", fmt.Errorf("error while parsing source parameters: %v", err) } @@ -1423,9 +1565,9 @@ func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, p } return *appSourceType, nil } - appType, err := discovery.AppType(ctx, path, enableGenerateManifests, tarExcludedGlobs) + appType, err := discovery.AppType(ctx, appPath, repoPath, enableGenerateManifests, tarExcludedGlobs, env) if err != nil { - return "", err + return "", fmt.Errorf("error getting app source type: %v", err) } return v1alpha1.ApplicationSourceType(appType), nil } @@ -1733,82 +1875,17 @@ func makeJsonnetVm(appPath string, repoRoot string, sourceJsonnet v1alpha1.Appli return vm, nil } -func runCommand(command v1alpha1.Command, path string, env []string) (string, error) { - if len(command.Command) == 0 { - return "", fmt.Errorf("Command is empty") - } - cmd := exec.Command(command.Command[0], append(command.Command[1:], command.Args...)...) - cmd.Env = env - cmd.Dir = path - return executil.Run(cmd) -} - -func findPlugin(plugins []*v1alpha1.ConfigManagementPlugin, name string) *v1alpha1.ConfigManagementPlugin { - for _, plugin := range plugins { - if plugin.Name == name { - return plugin - } - } - return nil -} - -func runConfigManagementPlugin(appPath, repoRoot string, envVars *v1alpha1.Env, q *apiclient.ManifestRequest, creds git.Creds, plugin *v1alpha1.ConfigManagementPlugin) ([]*unstructured.Unstructured, error) { - // Plugins can request to lock the complete repository when they need to - // use git client operations. - if plugin.LockRepo { - manifestGenerateLock.Lock(repoRoot) - defer manifestGenerateLock.Unlock(repoRoot) - } else { - concurrencyAllowed := isConcurrencyAllowed(appPath) - if !concurrencyAllowed { - manifestGenerateLock.Lock(appPath) - defer manifestGenerateLock.Unlock(appPath) - } - } - - env, err := getPluginEnvs(envVars, q, creds, false) - if err != nil { - return nil, err - } - - if plugin.Init != nil { - _, err := runCommand(*plugin.Init, appPath, env) - if err != nil { - return nil, err - } - } - out, err := runCommand(plugin.Generate, appPath, env) - if err != nil { - return nil, err - } - return kube.SplitYAML([]byte(out)) -} - -func getPluginEnvs(env *v1alpha1.Env, q *apiclient.ManifestRequest, creds git.Creds, remote bool) ([]string, error) { +func getPluginEnvs(env *v1alpha1.Env, q *apiclient.ManifestRequest) ([]string, error) { envVars := env.Environ() envVars = append(envVars, "KUBE_VERSION="+text.SemVer(q.KubeVersion)) envVars = append(envVars, "KUBE_API_VERSIONS="+strings.Join(q.ApiVersions, ",")) - return getPluginParamEnvs(envVars, q.ApplicationSource.Plugin, creds, remote) + return getPluginParamEnvs(envVars, q.ApplicationSource.Plugin) } // getPluginParamEnvs gets environment variables for plugin parameter announcement generation. -func getPluginParamEnvs(envVars []string, plugin *v1alpha1.ApplicationSourcePlugin, creds git.Creds, remote bool) ([]string, error) { +func getPluginParamEnvs(envVars []string, plugin *v1alpha1.ApplicationSourcePlugin) ([]string, error) { env := envVars - // Local plugins need also to have access to the local environment variables. - // Remote sidecar plugins will use the environment in the sidecar - // container. - if !remote { - env = append(os.Environ(), envVars...) - } - if creds != nil { - closer, environ, err := creds.Environ() - if err != nil { - return nil, err - } - defer func() { _ = closer.Close() }() - env = append(env, environ...) - } parsedEnv := make(v1alpha1.Env, len(env)) for i, v := range env { @@ -1835,15 +1912,15 @@ func getPluginParamEnvs(envVars []string, plugin *v1alpha1.ApplicationSourcePlug return env, nil } -func runConfigManagementPluginSidecars(ctx context.Context, appPath, repoPath, pluginName string, envVars *v1alpha1.Env, q *apiclient.ManifestRequest, creds git.Creds, tarDoneCh chan<- bool, tarExcludedGlobs []string) ([]*unstructured.Unstructured, error) { +func runConfigManagementPluginSidecars(ctx context.Context, appPath, repoPath, pluginName string, envVars *v1alpha1.Env, q *apiclient.ManifestRequest, tarDoneCh chan<- bool, tarExcludedGlobs []string) ([]*unstructured.Unstructured, error) { // compute variables. - env, err := getPluginEnvs(envVars, q, creds, true) + env, err := getPluginEnvs(envVars, q) if err != nil { return nil, err } - // detect config management plugin server (sidecar) - conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, pluginName, env, tarExcludedGlobs) + // detect config management plugin server + conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, repoPath, pluginName, env, tarExcludedGlobs) if err != nil { return nil, err } @@ -1900,7 +1977,9 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD return err } - appSourceType, err := GetAppSourceType(ctx, q.Source, opContext.appPath, q.AppName, q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs) + env := newEnvRepoQuery(q, revision) + + appSourceType, err := GetAppSourceType(ctx, q.Source, opContext.appPath, repoRoot, q.AppName, q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs, env.Environ()) if err != nil { return err } @@ -1913,7 +1992,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD return err } case v1alpha1.ApplicationSourceTypeKustomize: - if err := populateKustomizeAppDetails(res, q, opContext.appPath, commitSHA, s.gitCredsStore); err != nil { + if err := populateKustomizeAppDetails(res, q, repoRoot, opContext.appPath, commitSHA, s.gitCredsStore); err != nil { return err } case v1alpha1.ApplicationSourceTypePlugin: @@ -1921,25 +2000,25 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD return fmt.Errorf("failed to populate plugin app details: %w", err) } } - _ = s.cache.SetAppDetails(revision, q.Source, q.RefSources, res, v1alpha1.TrackingMethod(q.TrackingMethod)) + _ = s.cache.SetAppDetails(revision, q.Source, q.RefSources, res, v1alpha1.TrackingMethod(q.TrackingMethod), nil) return nil } settings := operationSettings{allowConcurrent: q.Source.AllowsConcurrentProcessing(), noCache: q.NoCache, noRevisionCache: q.NoCache || q.NoRevisionCache} - err := s.runRepoOperation(ctx, q.Source.TargetRevision, q.Repo, q.Source, false, cacheFn, operation, settings, false) + err := s.runRepoOperation(ctx, q.Source.TargetRevision, q.Repo, q.Source, false, cacheFn, operation, settings, false, nil) return res, err } -func (s *Service) createGetAppDetailsCacheHandler(res *apiclient.RepoAppDetailsResponse, q *apiclient.RepoServerAppDetailsQuery) func(revision string, _ bool) (bool, error) { - return func(revision string, _ bool) (bool, error) { - err := s.cache.GetAppDetails(revision, q.Source, q.RefSources, res, v1alpha1.TrackingMethod(q.TrackingMethod)) +func (s *Service) createGetAppDetailsCacheHandler(res *apiclient.RepoAppDetailsResponse, q *apiclient.RepoServerAppDetailsQuery) func(revision string, _ cache.ResolvedRevisions, _ bool) (bool, error) { + return func(revision string, _ cache.ResolvedRevisions, _ bool) (bool, error) { + err := s.cache.GetAppDetails(revision, q.Source, q.RefSources, res, v1alpha1.TrackingMethod(q.TrackingMethod), nil) if err == nil { log.Infof("app details cache hit: %s/%s", revision, q.Source.Path) return true, nil } - if err != reposervercache.ErrCacheMiss { + if err != cache.ErrCacheMiss { log.Warnf("app details cache error %s: %v", revision, q.Source) } else { log.Infof("app details cache miss: %s/%s", revision, q.Source) @@ -1950,12 +2029,13 @@ func (s *Service) createGetAppDetailsCacheHandler(res *apiclient.RepoAppDetailsR func populateHelmAppDetails(res *apiclient.RepoAppDetailsResponse, appPath string, repoRoot string, q *apiclient.RepoServerAppDetailsQuery, gitRepoPaths io.TempPaths) error { var selectedValueFiles []string + var availableValueFiles []string if q.Source.Helm != nil { selectedValueFiles = q.Source.Helm.ValueFiles } - availableValueFiles, err := findHelmValueFilesInPath(appPath) + err := filepath.Walk(appPath, walkHelmValueFilesInPath(appPath, &availableValueFiles)) if err != nil { return err } @@ -1969,7 +2049,11 @@ func populateHelmAppDetails(res *apiclient.RepoAppDetailsResponse, appPath strin } passCredentials = q.Source.Helm.PassCredentials } - h, err := helm.NewHelmApp(appPath, getHelmRepos(q.Repos), false, version, q.Repo.Proxy, passCredentials) + helmRepos, err := getHelmRepos(appPath, q.Repos, nil) + if err != nil { + return err + } + h, err := helm.NewHelmApp(appPath, helmRepos, false, version, q.Repo.Proxy, passCredentials) if err != nil { return err } @@ -2007,7 +2091,7 @@ func populateHelmAppDetails(res *apiclient.RepoAppDetailsResponse, appPath strin for _, v := range fileParameters(q) { res.Helm.FileParameters = append(res.Helm.FileParameters, &v1alpha1.HelmFileParameter{ Name: v.Name, - Path: v.Path, //filepath.Join(appPath, v.Path), + Path: v.Path, // filepath.Join(appPath, v.Path), }) } return nil @@ -2020,7 +2104,7 @@ func loadFileIntoIfExists(path pathutil.ResolvedFilePath, destination *string) e if err == nil && !info.IsDir() { bytes, err := os.ReadFile(stringPath) if err != nil { - return err + return fmt.Errorf("error reading file from %s: %w", stringPath, err) } *destination = string(bytes) } @@ -2028,35 +2112,34 @@ func loadFileIntoIfExists(path pathutil.ResolvedFilePath, destination *string) e return nil } -func findHelmValueFilesInPath(path string) ([]string, error) { - var result []string - - files, err := os.ReadDir(path) - if err != nil { - return result, err - } +func walkHelmValueFilesInPath(root string, valueFiles *[]string) filepath.WalkFunc { + return func(path string, info os.FileInfo, err error) error { - for _, f := range files { - if f.IsDir() { - continue + if err != nil { + return fmt.Errorf("error reading helm values file from %s: %w", path, err) } - filename := f.Name() - fileNameExt := strings.ToLower(filepath.Ext(filename)) + + filename := info.Name() + fileNameExt := strings.ToLower(filepath.Ext(path)) if strings.Contains(filename, "values") && (fileNameExt == ".yaml" || fileNameExt == ".yml") { - result = append(result, filename) + relPath, err := filepath.Rel(root, path) + if err != nil { + return fmt.Errorf("error traversing path from %s to %s: %w", root, path, err) + } + *valueFiles = append(*valueFiles, relPath) } - } - return result, nil + return nil + } } -func populateKustomizeAppDetails(res *apiclient.RepoAppDetailsResponse, q *apiclient.RepoServerAppDetailsQuery, appPath string, reversion string, credsStore git.CredsStore) error { +func populateKustomizeAppDetails(res *apiclient.RepoAppDetailsResponse, q *apiclient.RepoServerAppDetailsQuery, repoRoot string, appPath string, reversion string, credsStore git.CredsStore) error { res.Kustomize = &apiclient.KustomizeAppSpec{} kustomizeBinary := "" if q.KustomizeOptions != nil { kustomizeBinary = q.KustomizeOptions.BinaryPath } - k := kustomize.NewKustomizeApp(appPath, q.Repo.GetGitCreds(credsStore), q.Repo.Repo, kustomizeBinary) + k := kustomize.NewKustomizeApp(repoRoot, appPath, q.Repo.GetGitCreds(credsStore), q.Repo.Repo, kustomizeBinary) fakeManifestRequest := apiclient.ManifestRequest{ AppName: q.AppName, Namespace: "", // FIXME: omit it for now @@ -2075,8 +2158,6 @@ func populateKustomizeAppDetails(res *apiclient.RepoAppDetailsResponse, q *apicl func populatePluginAppDetails(ctx context.Context, res *apiclient.RepoAppDetailsResponse, appPath string, repoPath string, q *apiclient.RepoServerAppDetailsQuery, store git.CredsStore, tarExcludedGlobs []string) error { res.Plugin = &apiclient.PluginAppSpec{} - creds := q.Repo.GetGitCreds(store) - envVars := []string{ fmt.Sprintf("ARGOCD_APP_NAME=%s", q.AppName), fmt.Sprintf("ARGOCD_APP_SOURCE_REPO_URL=%s", q.Repo.Repo), @@ -2084,7 +2165,7 @@ func populatePluginAppDetails(ctx context.Context, res *apiclient.RepoAppDetails fmt.Sprintf("ARGOCD_APP_SOURCE_TARGET_REVISION=%s", q.Source.TargetRevision), } - env, err := getPluginParamEnvs(envVars, q.Source.Plugin, creds, true) + env, err := getPluginParamEnvs(envVars, q.Source.Plugin) if err != nil { return fmt.Errorf("failed to get env vars for plugin: %w", err) } @@ -2094,23 +2175,23 @@ func populatePluginAppDetails(ctx context.Context, res *apiclient.RepoAppDetails pluginName = q.Source.Plugin.Name } // detect config management plugin server (sidecar) - conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, pluginName, env, tarExcludedGlobs) + conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, repoPath, pluginName, env, tarExcludedGlobs) if err != nil { return fmt.Errorf("failed to detect CMP for app: %w", err) } defer io.Close(conn) - generateManifestStream, err := cmpClient.GetParametersAnnouncement(ctx, grpc_retry.Disable()) + parametersAnnouncementStream, err := cmpClient.GetParametersAnnouncement(ctx, grpc_retry.Disable()) if err != nil { - return fmt.Errorf("error getting generateManifestStream: %w", err) + return fmt.Errorf("error getting parametersAnnouncementStream: %w", err) } - err = cmp.SendRepoStream(generateManifestStream.Context(), appPath, repoPath, generateManifestStream, env, tarExcludedGlobs) + err = cmp.SendRepoStream(parametersAnnouncementStream.Context(), appPath, repoPath, parametersAnnouncementStream, env, tarExcludedGlobs) if err != nil { return fmt.Errorf("error sending file to cmp-server: %s", err) } - announcement, err := generateManifestStream.CloseAndRecv() + announcement, err := parametersAnnouncementStream.CloseAndRecv() if err != nil { return fmt.Errorf("failed to get parameter anouncement: %w", err) } @@ -2142,7 +2223,7 @@ func (s *Service) GetRevisionMetadata(ctx context.Context, q *apiclient.RepoServ return metadata, nil } } else { - if err != reposervercache.ErrCacheMiss { + if err != cache.ErrCacheMiss { log.Warnf("revision metadata cache error %s/%s: %v", q.Repo.Repo, q.Revision, err) } else { log.Infof("revision metadata cache miss: %s/%s", q.Repo.Repo, q.Revision) @@ -2162,7 +2243,7 @@ func (s *Service) GetRevisionMetadata(ctx context.Context, q *apiclient.RepoServ }) if err != nil { - return nil, err + return nil, fmt.Errorf("error acquiring repo lock: %w", err) } defer io.Close(closer) @@ -2198,6 +2279,45 @@ func (s *Service) GetRevisionMetadata(ctx context.Context, q *apiclient.RepoServ return metadata, nil } +// GetRevisionChartDetails returns the helm chart details of a given version +func (s *Service) GetRevisionChartDetails(ctx context.Context, q *apiclient.RepoServerRevisionChartDetailsRequest) (*v1alpha1.ChartDetails, error) { + details, err := s.cache.GetRevisionChartDetails(q.Repo.Repo, q.Name, q.Revision) + if err == nil { + log.Infof("revision chart details cache hit: %s/%s/%s", q.Repo.Repo, q.Name, q.Revision) + return details, nil + } else { + if err == cache.ErrCacheMiss { + log.Infof("revision metadata cache miss: %s/%s/%s", q.Repo.Repo, q.Name, q.Revision) + } else { + log.Warnf("revision metadata cache error %s/%s/%s: %v", q.Repo.Repo, q.Name, q.Revision, err) + } + } + helmClient, revision, err := s.newHelmClientResolveRevision(q.Repo, q.Revision, q.Name, true) + if err != nil { + return nil, fmt.Errorf("helm client error: %v", err) + } + chartPath, closer, err := helmClient.ExtractChart(q.Name, revision, false, s.initConstants.HelmManifestMaxExtractedSize, s.initConstants.DisableHelmManifestMaxExtractedSize) + if err != nil { + return nil, fmt.Errorf("error extracting chart: %v", err) + } + defer io.Close(closer) + helmCmd, err := helm.NewCmdWithVersion(chartPath, helm.HelmV3, q.Repo.EnableOCI, q.Repo.Proxy) + if err != nil { + return nil, fmt.Errorf("error creating helm cmd: %v", err) + } + defer helmCmd.Close() + helmDetails, err := helmCmd.InspectChart() + if err != nil { + return nil, fmt.Errorf("error inspecting chart: %v", err) + } + details, err = getChartDetails(helmDetails) + if err != nil { + return nil, fmt.Errorf("error getting chart details: %v", err) + } + _ = s.cache.SetRevisionChartDetails(q.Repo.Repo, q.Name, q.Revision, details) + return details, nil +} + func fileParameters(q *apiclient.RepoServerAppDetailsQuery) []v1alpha1.HelmFileParameter { if q.Source.Helm == nil { return nil @@ -2252,7 +2372,7 @@ func (s *Service) newHelmClientResolveRevision(repo *v1alpha1.Repository, revisi return helmClient, version.String(), nil } - index, err := helmClient.GetIndex(noRevisionCache) + index, err := helmClient.GetIndex(noRevisionCache, s.initConstants.HelmRegistryMaxIndexSize) if err != nil { return nil, "", err } @@ -2293,7 +2413,11 @@ func directoryPermissionInitializer(rootPath string) goio.Closer { // nolint:unparam func (s *Service) checkoutRevision(gitClient git.Client, revision string, submoduleEnabled bool) (goio.Closer, error) { closer := s.gitRepoInitializer(gitClient.Root()) - return closer, checkoutRevision(gitClient, revision, submoduleEnabled) + err := checkoutRevision(gitClient, revision, submoduleEnabled) + if err != nil { + s.metricsServer.IncGitFetchFail(gitClient.Root(), revision) + } + return closer, err } func checkoutRevision(gitClient git.Client, revision string, submoduleEnabled bool) error { @@ -2330,7 +2454,7 @@ func checkoutRevision(gitClient git.Client, revision string, submoduleEnabled bo } func (s *Service) GetHelmCharts(ctx context.Context, q *apiclient.HelmChartsRequest) (*apiclient.HelmChartsResponse, error) { - index, err := s.newHelmClient(q.Repo.Repo, q.Repo.GetHelmCreds(), q.Repo.EnableOCI, q.Repo.Proxy, helm.WithChartPaths(s.chartPaths)).GetIndex(true) + index, err := s.newHelmClient(q.Repo.Repo, q.Repo.GetHelmCreds(), q.Repo.EnableOCI, q.Repo.Proxy, helm.WithChartPaths(s.chartPaths)).GetIndex(true, s.initConstants.HelmRegistryMaxIndexSize) if err != nil { return nil, err } @@ -2365,7 +2489,7 @@ func (s *Service) TestRepository(ctx context.Context, q *apiclient.TestRepositor _, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).TestHelmOCI() return err } else { - _, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).GetIndex(false) + _, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).GetIndex(false, s.initConstants.HelmRegistryMaxIndexSize) return err } }, @@ -2412,3 +2536,142 @@ func (s *Service) ResolveRevision(ctx context.Context, q *apiclient.ResolveRevis }, nil } } + +func (s *Service) GetGitFiles(_ context.Context, request *apiclient.GitFilesRequest) (*apiclient.GitFilesResponse, error) { + repo := request.GetRepo() + revision := request.GetRevision() + gitPath := request.GetPath() + noRevisionCache := request.GetNoRevisionCache() + enableNewGitFileGlobbing := request.GetNewGitFileGlobbingEnabled() + if gitPath == "" { + gitPath = "." + } + + if repo == nil { + return nil, status.Error(codes.InvalidArgument, "must pass a valid repo") + } + + gitClient, revision, err := s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, !noRevisionCache)) + if err != nil { + return nil, status.Errorf(codes.Internal, "unable to resolve git revision %s: %v", revision, err) + } + + // check the cache and return the results if present + if cachedFiles, err := s.cache.GetGitFiles(repo.Repo, revision, gitPath); err == nil { + log.Debugf("cache hit for repo: %s revision: %s pattern: %s", repo.Repo, revision, gitPath) + return &apiclient.GitFilesResponse{ + Map: cachedFiles, + }, nil + } + + s.metricsServer.IncPendingRepoRequest(repo.Repo) + defer s.metricsServer.DecPendingRepoRequest(repo.Repo) + + // cache miss, generate the results + closer, err := s.repoLock.Lock(gitClient.Root(), revision, true, func() (goio.Closer, error) { + return s.checkoutRevision(gitClient, revision, request.GetSubmoduleEnabled()) + }) + if err != nil { + return nil, status.Errorf(codes.Internal, "unable to checkout git repo %s with revision %s pattern %s: %v", repo.Repo, revision, gitPath, err) + } + defer io.Close(closer) + + gitFiles, err := gitClient.LsFiles(gitPath, enableNewGitFileGlobbing) + if err != nil { + return nil, status.Errorf(codes.Internal, "unable to list files. repo %s with revision %s pattern %s: %v", repo.Repo, revision, gitPath, err) + } + log.Debugf("listed %d git files from %s under %s", len(gitFiles), repo.Repo, gitPath) + + res := make(map[string][]byte) + for _, filePath := range gitFiles { + fileContents, err := os.ReadFile(filepath.Join(gitClient.Root(), filePath)) + if err != nil { + return nil, status.Errorf(codes.Internal, "unable to read files. repo %s with revision %s pattern %s: %v", repo.Repo, revision, gitPath, err) + } + res[filePath] = fileContents + } + + err = s.cache.SetGitFiles(repo.Repo, revision, gitPath, res) + if err != nil { + log.Warnf("error caching git files for repo %s with revision %s pattern %s: %v", repo.Repo, revision, gitPath, err) + } + + return &apiclient.GitFilesResponse{ + Map: res, + }, nil +} + +func (s *Service) GetGitDirectories(_ context.Context, request *apiclient.GitDirectoriesRequest) (*apiclient.GitDirectoriesResponse, error) { + repo := request.GetRepo() + revision := request.GetRevision() + noRevisionCache := request.GetNoRevisionCache() + if repo == nil { + return nil, status.Error(codes.InvalidArgument, "must pass a valid repo") + } + + gitClient, revision, err := s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, !noRevisionCache)) + if err != nil { + return nil, status.Errorf(codes.Internal, "unable to resolve git revision %s: %v", revision, err) + } + + // check the cache and return the results if present + if cachedPaths, err := s.cache.GetGitDirectories(repo.Repo, revision); err == nil { + log.Debugf("cache hit for repo: %s revision: %s", repo.Repo, revision) + return &apiclient.GitDirectoriesResponse{ + Paths: cachedPaths, + }, nil + } + + s.metricsServer.IncPendingRepoRequest(repo.Repo) + defer s.metricsServer.DecPendingRepoRequest(repo.Repo) + + // cache miss, generate the results + closer, err := s.repoLock.Lock(gitClient.Root(), revision, true, func() (goio.Closer, error) { + return s.checkoutRevision(gitClient, revision, request.GetSubmoduleEnabled()) + }) + if err != nil { + return nil, status.Errorf(codes.Internal, "unable to checkout git repo %s with revision %s: %v", repo.Repo, revision, err) + } + defer io.Close(closer) + + repoRoot := gitClient.Root() + var paths []string + if err := filepath.WalkDir(repoRoot, func(path string, entry fs.DirEntry, fnErr error) error { + if fnErr != nil { + return fmt.Errorf("error walking the file tree: %w", fnErr) + } + if !entry.IsDir() { // Skip files: directories only + return nil + } + + fname := entry.Name() + if strings.HasPrefix(fname, ".") { // Skip all folders starts with "." + return filepath.SkipDir + } + + relativePath, err := filepath.Rel(repoRoot, path) + if err != nil { + return fmt.Errorf("error constructing relative repo path: %w", err) + } + + if relativePath == "." { // Exclude '.' from results + return nil + } + + paths = append(paths, relativePath) + + return nil + }); err != nil { + return nil, err + } + + log.Debugf("found %d git paths from %s", len(paths), repo.Repo) + err = s.cache.SetGitDirectories(repo.Repo, revision, paths) + if err != nil { + log.Warnf("error caching git directories for repo %s with revision %s: %v", repo.Repo, revision, err) + } + + return &apiclient.GitDirectoriesResponse{ + Paths: paths, + }, nil +} diff --git a/reposerver/repository/repository.proto b/reposerver/repository/repository.proto index c5212265d33b7..de061122e2586 100644 --- a/reposerver/repository/repository.proto +++ b/reposerver/repository/repository.proto @@ -18,6 +18,7 @@ message ManifestRequest { string namespace = 8; github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSource applicationSource = 10; repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Repository repos = 11; + // Deprecated: use sidecar plugins instead. repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ConfigManagementPlugin plugins = 12; github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.KustomizeOptions kustomizeOptions = 13; string kubeVersion = 14; @@ -31,6 +32,10 @@ message ManifestRequest { github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.HelmOptions helmOptions = 21; bool hasMultipleSources = 22; map refSources = 23; + // This is used to surface "source not permitted" errors for Helm repositories + repeated string projectSourceRepos = 24; + // This is used to surface "source not permitted" errors for Helm repositories + string projectName = 25; } message ManifestRequestWithFiles { @@ -89,13 +94,13 @@ message ManifestResponse { } message ListRefsRequest { - github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Repository repo = 1; + github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Repository repo = 1; } // A subset of the repository's named refs message Refs { - repeated string branches = 1; - repeated string tags = 2; + repeated string branches = 1; + repeated string tags = 2; } // ListAppsRequest requests a repository directory structure @@ -136,11 +141,11 @@ message RepoServerAppDetailsQuery { // RepoAppDetailsResponse application details message RepoAppDetailsResponse { - string type = 1; - HelmAppSpec helm = 3; - KustomizeAppSpec kustomize = 4; - DirectoryAppSpec directory = 5; - PluginAppSpec plugin = 6; + string type = 1; + HelmAppSpec helm = 3; + KustomizeAppSpec kustomize = 4; + DirectoryAppSpec directory = 5; + PluginAppSpec plugin = 6; } message RepoServerRevisionMetadataRequest { @@ -152,22 +157,31 @@ message RepoServerRevisionMetadataRequest { bool checkSignature = 3; } +message RepoServerRevisionChartDetailsRequest { + // the repo + github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Repository repo = 1; + // the chart + string name = 2; + // the revision within the chart + string revision = 3; +} + // HelmAppSpec contains helm app name in source repo message HelmAppSpec { - string name = 1; - repeated string valueFiles = 3; - // the output of `helm inspect values` - repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.HelmParameter parameters = 4; - // the contents of values.yaml - string values = 5; + string name = 1; + repeated string valueFiles = 3; + // the output of `helm inspect values` + repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.HelmParameter parameters = 4; + // the contents of values.yaml + string values = 5; // helm file parameters repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.HelmFileParameter fileParameters = 6; } // KustomizeAppSpec contains kustomize images message KustomizeAppSpec { - // images is a list of available images. - repeated string images = 3; + // images is a list of available images. + repeated string images = 3; } // DirectoryAppSpec contains directory @@ -216,6 +230,32 @@ message HelmChartsResponse { repeated HelmChart items = 1; } +message GitFilesRequest { + github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Repository repo = 1; + bool submoduleEnabled = 2; + string revision = 3; + string path = 4; + bool NewGitFileGlobbingEnabled = 5; + bool noRevisionCache = 6; +} + +message GitFilesResponse { + // Map consisting of path of the path to its contents in bytes + map map = 1; +} + +message GitDirectoriesRequest { + github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Repository repo = 1; + bool submoduleEnabled = 2; + string revision = 3; + bool noRevisionCache = 4; +} + +message GitDirectoriesResponse { + // A set of directory paths + repeated string paths = 1; +} + // ManifestService service RepoServerService { @@ -234,7 +274,7 @@ service RepoServerService { // Returns a valid revision rpc ResolveRevision(ResolveRevisionRequest) returns (ResolveRevisionResponse) { } - + // Returns a list of refs (e.g. branches and tags) in the repo rpc ListRefs(ListRefsRequest) returns (Refs) { } @@ -254,8 +294,20 @@ service RepoServerService { // Get the meta-data (author, date, tags, message) for a specific revision of the repo rpc GetRevisionMetadata(RepoServerRevisionMetadataRequest) returns (github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.RevisionMetadata) { } + + // Get the chart details (author, date, tags, message) for a specific revision of the repo + rpc GetRevisionChartDetails(RepoServerRevisionChartDetailsRequest) returns (github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ChartDetails) { + } // GetHelmCharts returns list of helm charts in the specified repository rpc GetHelmCharts(HelmChartsRequest) returns (HelmChartsResponse) { } + + // GetGitFiles returns a set of file paths and their contents for the given repo + rpc GetGitFiles(GitFilesRequest) returns (GitFilesResponse) { + } + + // GetGitDirectories returns a set of directory paths for the given repo + rpc GetGitDirectories(GitDirectoriesRequest) returns (GitDirectoriesResponse) { + } } diff --git a/reposerver/repository/repository_test.go b/reposerver/repository/repository_test.go index d8f1df05f44c3..d48f50a832eb0 100644 --- a/reposerver/repository/repository_test.go +++ b/reposerver/repository/repository_test.go @@ -1,6 +1,7 @@ package repository import ( + "bytes" "context" "encoding/json" "errors" @@ -14,27 +15,31 @@ import ( "regexp" "sort" "strings" + "sync" "testing" "time" + cacheutil "github.com/argoproj/argo-cd/v2/util/cache" log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/api/resource" - "github.com/ghodss/yaml" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" v1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/yaml" + "github.com/argoproj/argo-cd/v2/common" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/reposerver/cache" + repositorymocks "github.com/argoproj/argo-cd/v2/reposerver/cache/mocks" "github.com/argoproj/argo-cd/v2/reposerver/metrics" fileutil "github.com/argoproj/argo-cd/v2/test/fixture/path" "github.com/argoproj/argo-cd/v2/util/argo" - cacheutil "github.com/argoproj/argo-cd/v2/util/cache" dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" "github.com/argoproj/argo-cd/v2/util/git" gitmocks "github.com/argoproj/argo-cd/v2/util/git/mocks" @@ -51,18 +56,60 @@ gpg: Good signature from "GitHub (web-flow commit signing) " type clientFunc func(*gitmocks.Client, *helmmocks.Client, *iomocks.TempPaths) -func newServiceWithMocks(root string, signed bool) (*Service, *gitmocks.Client) { +type repoCacheMocks struct { + mock.Mock + cacheutilCache *cacheutil.Cache + cache *cache.Cache + mockCache *repositorymocks.MockRepoCache +} + +type newGitRepoHelmChartOptions struct { + chartName string + chartVersion string + // valuesFiles is a map of the values file name to the key/value pairs to be written to the file + valuesFiles map[string]map[string]string +} + +type newGitRepoOptions struct { + path string + createPath bool + remote string + addEmptyCommit bool + helmChartOptions newGitRepoHelmChartOptions +} + +func newCacheMocks() *repoCacheMocks { + return newCacheMocksWithOpts(1*time.Minute, 1*time.Minute, 10*time.Second) +} + +func newCacheMocksWithOpts(repoCacheExpiration, revisionCacheExpiration, revisionCacheLockTimeout time.Duration) *repoCacheMocks { + mockRepoCache := repositorymocks.NewMockRepoCache(&repositorymocks.MockCacheOptions{ + RepoCacheExpiration: 1 * time.Minute, + RevisionCacheExpiration: 1 * time.Minute, + ReadDelay: 0, + WriteDelay: 0, + }) + cacheutilCache := cacheutil.NewCache(mockRepoCache.RedisClient) + return &repoCacheMocks{ + cacheutilCache: cacheutilCache, + cache: cache.NewCache(cacheutilCache, repoCacheExpiration, revisionCacheExpiration, revisionCacheLockTimeout), + mockCache: mockRepoCache, + } +} + +func newServiceWithMocks(t *testing.T, root string, signed bool) (*Service, *gitmocks.Client, *repoCacheMocks) { root, err := filepath.Abs(root) if err != nil { panic(err) } - return newServiceWithOpt(func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { + return newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { gitClient.On("Init").Return(nil) gitClient.On("Fetch", mock.Anything).Return(nil) gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) gitClient.On("LsRemote", mock.Anything).Return(mock.Anything, nil) gitClient.On("CommitSHA").Return(mock.Anything, nil) gitClient.On("Root").Return(root) + gitClient.On("IsAnnotatedTag").Return(false) if signed { gitClient.On("VerifyCommitSignature", mock.Anything).Return(testSignature, nil) } else { @@ -72,7 +119,7 @@ func newServiceWithMocks(root string, signed bool) (*Service, *gitmocks.Client) chart := "my-chart" oobChart := "out-of-bounds-chart" version := "1.1.0" - helmClient.On("GetIndex", true).Return(&helm.Index{Entries: map[string]helm.Entries{ + helmClient.On("GetIndex", mock.AnythingOfType("bool"), mock.Anything).Return(&helm.Index{Entries: map[string]helm.Entries{ chart: {{Version: "1.0.0"}, {Version: version}}, oobChart: {{Version: "1.0.0"}, {Version: version}}, }}, nil) @@ -88,18 +135,16 @@ func newServiceWithMocks(root string, signed bool) (*Service, *gitmocks.Client) }, root) } -func newServiceWithOpt(cf clientFunc, root string) (*Service, *gitmocks.Client) { +func newServiceWithOpt(t *testing.T, cf clientFunc, root string) (*Service, *gitmocks.Client, *repoCacheMocks) { helmClient := &helmmocks.Client{} gitClient := &gitmocks.Client{} paths := &iomocks.TempPaths{} cf(gitClient, helmClient, paths) - service := NewService(metrics.NewMetricsServer(), cache.NewCache( - cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Minute)), - 1*time.Minute, - 1*time.Minute, - ), RepoServerInitConstants{ParallelismLimit: 1}, argo.NewResourceTracking(), &git.NoopCredsStore{}, root) + cacheMocks := newCacheMocks() + t.Cleanup(cacheMocks.mockCache.StopRedisCallback) + service := NewService(metrics.NewMetricsServer(), cacheMocks.cache, RepoServerInitConstants{ParallelismLimit: 1}, argo.NewResourceTracking(), &git.NoopCredsStore{}, root) - service.newGitClient = func(rawRepoURL string, root string, creds git.Creds, insecure bool, enableLfs bool, prosy string, opts ...git.ClientOpts) (client git.Client, e error) { + service.newGitClient = func(rawRepoURL string, root string, creds git.Creds, insecure bool, enableLfs bool, proxy string, opts ...git.ClientOpts) (client git.Client, e error) { return gitClient, nil } service.newHelmClient = func(repoURL string, creds helm.Creds, enableOci bool, proxy string, opts ...helm.ClientOpts) helm.Client { @@ -109,20 +154,20 @@ func newServiceWithOpt(cf clientFunc, root string) (*Service, *gitmocks.Client) return io.NopCloser } service.gitRepoPaths = paths - return service, gitClient + return service, gitClient, cacheMocks } -func newService(root string) *Service { - service, _ := newServiceWithMocks(root, false) +func newService(t *testing.T, root string) *Service { + service, _, _ := newServiceWithMocks(t, root, false) return service } -func newServiceWithSignature(root string) *Service { - service, _ := newServiceWithMocks(root, true) +func newServiceWithSignature(t *testing.T, root string) *Service { + service, _, _ := newServiceWithMocks(t, root, true) return service } -func newServiceWithCommitSHA(root, revision string) *Service { +func newServiceWithCommitSHA(t *testing.T, root, revision string) *Service { var revisionErr error commitSHARegex := regexp.MustCompile("^[0-9A-Fa-f]{40}$") @@ -130,7 +175,7 @@ func newServiceWithCommitSHA(root, revision string) *Service { revisionErr = errors.New("not a commit SHA") } - service, gitClient := newServiceWithOpt(func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { + service, gitClient, _ := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { gitClient.On("Init").Return(nil) gitClient.On("Fetch", mock.Anything).Return(nil) gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) @@ -149,13 +194,18 @@ func newServiceWithCommitSHA(root, revision string) *Service { } func TestGenerateYamlManifestInDir(t *testing.T) { - service := newService("../../manifests/base") + service := newService(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} - q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src} + q := apiclient.ManifestRequest{ + Repo: &argoappv1.Repository{}, + ApplicationSource: &src, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, + } // update this value if we add/remove manifests - const countOfManifests = 49 + const countOfManifests = 48 res1, err := service.GenerateManifest(context.Background(), &q) @@ -218,7 +268,8 @@ func Test_GenerateManifests_NoOutOfBoundsAccess(t *testing.T) { mustNotContain = testCaseCopy.mustNotContain } - q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{}} + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{}, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} res, err := GenerateManifests(context.Background(), repoDir, "", "", &q, false, &git.NoopCredsStore{}, resource.MustParse("0"), nil) require.Error(t, err) assert.NotContains(t, err.Error(), mustNotContain) @@ -233,23 +284,27 @@ func TestGenerateManifests_MissingSymlinkDestination(t *testing.T) { err := os.Symlink("/obviously/does/not/exist", path.Join(repoDir, "test.yaml")) require.NoError(t, err) - q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{}} + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{}, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} _, err = GenerateManifests(context.Background(), repoDir, "", "", &q, false, &git.NoopCredsStore{}, resource.MustParse("0"), nil) require.NoError(t, err) } func TestGenerateManifests_K8SAPIResetCache(t *testing.T) { - service := newService("../../manifests/base") + service := newService(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} q := apiclient.ManifestRequest{ - KubeVersion: "v1.16.0", - Repo: &argoappv1.Repository{}, ApplicationSource: &src, + KubeVersion: "v1.16.0", + Repo: &argoappv1.Repository{}, + ApplicationSource: &src, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, } cachedFakeResponse := &apiclient.ManifestResponse{Manifests: []string{"Fake"}} - err := service.cache.SetManifests(mock.Anything, &src, q.RefSources, &q, "", "", "", "", &cache.CachedManifestResponse{ManifestResponse: cachedFakeResponse}) + err := service.cache.SetManifests(mock.Anything, &src, q.RefSources, &q, "", "", "", "", &cache.CachedManifestResponse{ManifestResponse: cachedFakeResponse}, nil) assert.NoError(t, err) res, err := service.GenerateManifest(context.Background(), &q) @@ -264,26 +319,160 @@ func TestGenerateManifests_K8SAPIResetCache(t *testing.T) { } func TestGenerateManifests_EmptyCache(t *testing.T) { - service := newService("../../manifests/base") + service, gitMocks, mockCache := newServiceWithMocks(t, "../../manifests/base", false) src := argoappv1.ApplicationSource{Path: "."} q := apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, ApplicationSource: &src, + Repo: &argoappv1.Repository{}, + ApplicationSource: &src, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, } - err := service.cache.SetManifests(mock.Anything, &src, q.RefSources, &q, "", "", "", "", &cache.CachedManifestResponse{ManifestResponse: nil}) + err := service.cache.SetManifests(mock.Anything, &src, q.RefSources, &q, "", "", "", "", &cache.CachedManifestResponse{ManifestResponse: nil}, nil) assert.NoError(t, err) res, err := service.GenerateManifest(context.Background(), &q) assert.NoError(t, err) assert.True(t, len(res.Manifests) > 0) + mockCache.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ + ExternalSets: 2, + ExternalGets: 2, + ExternalDeletes: 1}) + gitMocks.AssertCalled(t, "LsRemote", mock.Anything) + gitMocks.AssertCalled(t, "Fetch", mock.Anything) +} + +// Test that when Generate manifest is called with a source that is ref only it does not try to generate manifests or hit the manifest cache +// but it does resolve and cache the revision +func TestGenerateManifest_RefOnlyShortCircuit(t *testing.T) { + lsremoteCalled := false + dir := t.TempDir() + repopath := fmt.Sprintf("%s/tmprepo", dir) + repoRemote := fmt.Sprintf("file://%s", repopath) + cacheMocks := newCacheMocks() + t.Cleanup(cacheMocks.mockCache.StopRedisCallback) + service := NewService(metrics.NewMetricsServer(), cacheMocks.cache, RepoServerInitConstants{ParallelismLimit: 1}, argo.NewResourceTracking(), &git.NoopCredsStore{}, repopath) + service.newGitClient = func(rawRepoURL string, root string, creds git.Creds, insecure bool, enableLfs bool, proxy string, opts ...git.ClientOpts) (client git.Client, e error) { + opts = append(opts, git.WithEventHandlers(git.EventHandlers{ + // Primary check, we want to make sure ls-remote is not called when the item is in cache + OnLsRemote: func(repo string) func() { + return func() { + lsremoteCalled = true + } + }, + OnFetch: func(repo string) func() { + return func() { + assert.Fail(t, "Fetch should not be called from GenerateManifest when the source is ref only") + } + }, + })) + gitClient, err := git.NewClientExt(rawRepoURL, root, creds, insecure, enableLfs, proxy, opts...) + return gitClient, err + } + revision := initGitRepo(t, newGitRepoOptions{ + path: repopath, + createPath: true, + remote: repoRemote, + addEmptyCommit: true, + }) + src := argoappv1.ApplicationSource{RepoURL: repoRemote, TargetRevision: "HEAD", Ref: "test-ref"} + repo := &argoappv1.Repository{ + Repo: repoRemote, + } + q := apiclient.ManifestRequest{ + Repo: repo, + Revision: "HEAD", + HasMultipleSources: true, + ApplicationSource: &src, + ProjectName: "default", + ProjectSourceRepos: []string{"*"}, + } + _, err := service.GenerateManifest(context.Background(), &q) + assert.NoError(t, err) + cacheMocks.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ + ExternalSets: 2, + ExternalGets: 2}) + assert.True(t, lsremoteCalled, "ls-remote should be called when the source is ref only") + var revisions [][2]string + assert.NoError(t, cacheMocks.cacheutilCache.GetItem(fmt.Sprintf("git-refs|%s", repoRemote), &revisions)) + assert.ElementsMatch(t, [][2]string{{"refs/heads/main", revision}, {"HEAD", "ref: refs/heads/main"}}, revisions) +} + +// Test that calling manifest generation on source helm reference helm files that when the revision is cached it does not call ls-remote +func TestGenerateManifestsHelmWithRefs_CachedNoLsRemote(t *testing.T) { + dir := t.TempDir() + repopath := fmt.Sprintf("%s/tmprepo", dir) + cacheMocks := newCacheMocks() + t.Cleanup(func() { + cacheMocks.mockCache.StopRedisCallback() + err := filepath.WalkDir(dir, + func(path string, di fs.DirEntry, err error) error { + if err == nil { + return os.Chmod(path, 0777) + } + return err + }) + if err != nil { + t.Fatal(err) + } + }) + service := NewService(metrics.NewMetricsServer(), cacheMocks.cache, RepoServerInitConstants{ParallelismLimit: 1}, argo.NewResourceTracking(), &git.NoopCredsStore{}, repopath) + var gitClient git.Client + var err error + service.newGitClient = func(rawRepoURL string, root string, creds git.Creds, insecure bool, enableLfs bool, proxy string, opts ...git.ClientOpts) (client git.Client, e error) { + opts = append(opts, git.WithEventHandlers(git.EventHandlers{ + // Primary check, we want to make sure ls-remote is not called when the item is in cache + OnLsRemote: func(repo string) func() { + return func() { + assert.Fail(t, "LsRemote should not be called when the item is in cache") + } + }, + })) + gitClient, err = git.NewClientExt(rawRepoURL, root, creds, insecure, enableLfs, proxy, opts...) + return gitClient, err + } + repoRemote := fmt.Sprintf("file://%s", repopath) + revision := initGitRepo(t, newGitRepoOptions{ + path: repopath, + createPath: true, + remote: repoRemote, + helmChartOptions: newGitRepoHelmChartOptions{ + chartName: "my-chart", + chartVersion: "v1.0.0", + valuesFiles: map[string]map[string]string{"test.yaml": {"testval": "test"}}}, + }) + src := argoappv1.ApplicationSource{RepoURL: repoRemote, Path: ".", TargetRevision: "HEAD", Helm: &argoappv1.ApplicationSourceHelm{ + ValueFiles: []string{"$ref/test.yaml"}, + }} + repo := &argoappv1.Repository{ + Repo: repoRemote, + } + q := apiclient.ManifestRequest{ + Repo: repo, + Revision: "HEAD", + HasMultipleSources: true, + ApplicationSource: &src, + ProjectName: "default", + ProjectSourceRepos: []string{"*"}, + RefSources: map[string]*argoappv1.RefTarget{"$ref": {TargetRevision: "HEAD", Repo: *repo}}, + } + err = cacheMocks.cacheutilCache.SetItem(fmt.Sprintf("git-refs|%s", repoRemote), [][2]string{{"HEAD", revision}}, nil) + assert.NoError(t, err) + _, err = service.GenerateManifest(context.Background(), &q) + assert.NoError(t, err) + cacheMocks.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ + ExternalSets: 2, + ExternalGets: 5}) } // ensure we can use a semver constraint range (>= 1.0.0) and get back the correct chart (1.0.0) func TestHelmManifestFromChartRepo(t *testing.T) { - service := newService(".") + root := t.TempDir() + service, gitMocks, mockCache := newServiceWithMocks(t, root, false) source := &argoappv1.ApplicationSource{Chart: "my-chart", TargetRevision: ">= 1.0.0"} - request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true} + request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} response, err := service.GenerateManifest(context.Background(), request) assert.NoError(t, err) assert.NotNil(t, response) @@ -294,10 +483,14 @@ func TestHelmManifestFromChartRepo(t *testing.T) { Revision: "1.1.0", SourceType: "Helm", }, response) + mockCache.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ + ExternalSets: 1, + ExternalGets: 0}) + gitMocks.AssertNotCalled(t, "LsRemote", mock.Anything) } func TestHelmChartReferencingExternalValues(t *testing.T) { - service := newService(".") + service := newService(t, ".") spec := argoappv1.ApplicationSpec{ Sources: []argoappv1.ApplicationSource{ {RepoURL: "https://helm.example.com", Chart: "my-chart", TargetRevision: ">= 1.0.0", Helm: &argoappv1.ApplicationSourceHelm{ @@ -312,7 +505,8 @@ func TestHelmChartReferencingExternalValues(t *testing.T) { }, nil) refSources, err := argo.GetRefSources(context.Background(), spec, repoDB) require.NoError(t, err) - request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &spec.Sources[0], NoCache: true, RefSources: refSources, HasMultipleSources: true} + request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &spec.Sources[0], NoCache: true, RefSources: refSources, HasMultipleSources: true, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} response, err := service.GenerateManifest(context.Background(), request) assert.NoError(t, err) assert.NotNil(t, response) @@ -326,7 +520,7 @@ func TestHelmChartReferencingExternalValues(t *testing.T) { } func TestHelmChartReferencingExternalValues_OutOfBounds_Symlink(t *testing.T) { - service := newService(".") + service := newService(t, ".") err := os.Mkdir("testdata/oob-symlink", 0755) require.NoError(t, err) t.Cleanup(func() { @@ -360,11 +554,12 @@ func TestHelmChartReferencingExternalValues_OutOfBounds_Symlink(t *testing.T) { } func TestGenerateManifestsUseExactRevision(t *testing.T) { - service, gitClient := newServiceWithMocks(".", false) + service, gitClient, _ := newServiceWithMocks(t, ".", false) src := argoappv1.ApplicationSource{Path: "./testdata/recurse", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}} - q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, Revision: "abc"} + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, Revision: "abc", ProjectName: "something", + ProjectSourceRepos: []string{"*"}} res1, err := service.GenerateManifest(context.Background(), &q) assert.Nil(t, err) @@ -373,11 +568,12 @@ func TestGenerateManifestsUseExactRevision(t *testing.T) { } func TestRecurseManifestsInDir(t *testing.T) { - service := newService(".") + service := newService(t, ".") src := argoappv1.ApplicationSource{Path: "./testdata/recurse", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}} - q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src} + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} res1, err := service.GenerateManifest(context.Background(), &q) assert.Nil(t, err) @@ -385,7 +581,7 @@ func TestRecurseManifestsInDir(t *testing.T) { } func TestInvalidManifestsInDir(t *testing.T) { - service := newService(".") + service := newService(t, ".") src := argoappv1.ApplicationSource{Path: "./testdata/invalid-manifests", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}} @@ -395,8 +591,30 @@ func TestInvalidManifestsInDir(t *testing.T) { assert.NotNil(t, err) } +func TestInvalidMetadata(t *testing.T) { + service := newService(t, ".") + + src := argoappv1.ApplicationSource{Path: "./testdata/invalid-metadata", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}} + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, AppLabelKey: "test", AppName: "invalid-metadata", TrackingMethod: "annotation+label"} + _, err := service.GenerateManifest(context.Background(), &q) + assert.Error(t, err) + assert.Contains(t, err.Error(), "contains non-string key in the map") +} + +func TestNilMetadataAccessors(t *testing.T) { + service := newService(t, ".") + expected := "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{\"argocd.argoproj.io/tracking-id\":\"nil-metadata-accessors:/ConfigMap:/my-map\"},\"labels\":{\"test\":\"nil-metadata-accessors\"},\"name\":\"my-map\"},\"stringData\":{\"foo\":\"bar\"}}" + + src := argoappv1.ApplicationSource{Path: "./testdata/nil-metadata-accessors", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}} + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, AppLabelKey: "test", AppName: "nil-metadata-accessors", TrackingMethod: "annotation+label"} + res, err := service.GenerateManifest(context.Background(), &q) + assert.NoError(t, err) + assert.Equal(t, len(res.Manifests), 1) + assert.Equal(t, expected, res.Manifests[0]) +} + func TestGenerateJsonnetManifestInDir(t *testing.T) { - service := newService(".") + service := newService(t, ".") q := apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -410,6 +628,8 @@ func TestGenerateJsonnetManifestInDir(t *testing.T) { }, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, } res1, err := service.GenerateManifest(context.Background(), &q) assert.Nil(t, err) @@ -417,7 +637,7 @@ func TestGenerateJsonnetManifestInDir(t *testing.T) { } func TestGenerateJsonnetManifestInRootDir(t *testing.T) { - service := newService("testdata/jsonnet-1") + service := newService(t, "testdata/jsonnet-1") q := apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -431,6 +651,8 @@ func TestGenerateJsonnetManifestInRootDir(t *testing.T) { }, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, } res1, err := service.GenerateManifest(context.Background(), &q) assert.Nil(t, err) @@ -438,7 +660,7 @@ func TestGenerateJsonnetManifestInRootDir(t *testing.T) { } func TestGenerateJsonnetLibOutside(t *testing.T) { - service := newService(".") + service := newService(t, ".") q := apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -450,6 +672,8 @@ func TestGenerateJsonnetLibOutside(t *testing.T) { }, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, } _, err := service.GenerateManifest(context.Background(), &q) require.Error(t, err) @@ -464,7 +688,7 @@ func TestManifestGenErrorCacheByNumRequests(t *testing.T) { assert.NotNil(t, manifestRequest) cachedManifestResponse := &cache.CachedManifestResponse{} - err := service.cache.GetManifests(mock.Anything, manifestRequest.ApplicationSource, manifestRequest.RefSources, manifestRequest, manifestRequest.Namespace, "", manifestRequest.AppLabelKey, manifestRequest.AppName, cachedManifestResponse) + err := service.cache.GetManifests(mock.Anything, manifestRequest.ApplicationSource, manifestRequest.RefSources, manifestRequest, manifestRequest.Namespace, "", manifestRequest.AppLabelKey, manifestRequest.AppName, cachedManifestResponse, nil) assert.Nil(t, err) return cachedManifestResponse } @@ -507,7 +731,7 @@ func TestManifestGenErrorCacheByNumRequests(t *testing.T) { for _, tt := range tests { testName := fmt.Sprintf("gen-attempts-%d-pause-%d-total-%d", tt.PauseGenerationAfterFailedGenerationAttempts, tt.PauseGenerationOnFailureForRequests, tt.TotalCacheInvocations) t.Run(testName, func(t *testing.T) { - service := newService(".") + service := newService(t, ".") service.initConstants = RepoServerInitConstants{ ParallelismLimit: 1, @@ -585,7 +809,7 @@ func TestManifestGenErrorCacheFileContentsChange(t *testing.T) { tmpDir := t.TempDir() - service := newService(tmpDir) + service := newService(t, tmpDir) service.initConstants = RepoServerInitConstants{ ParallelismLimit: 1, @@ -624,6 +848,8 @@ func TestManifestGenErrorCacheFileContentsChange(t *testing.T) { ApplicationSource: &argoappv1.ApplicationSource{ Path: ".", }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) fmt.Println("-", step, "-", res != nil, err != nil, errorExpected) @@ -653,7 +879,7 @@ func TestManifestGenErrorCacheByMinutesElapsed(t *testing.T) { for _, tt := range tests { testName := fmt.Sprintf("pause-time-%d", tt.PauseGenerationOnFailureForMinutes) t.Run(testName, func(t *testing.T) { - service := newService(".") + service := newService(t, ".") // Here we simulate the passage of time by overriding the now() function of Service currentTime := time.Now() @@ -723,7 +949,7 @@ func TestManifestGenErrorCacheByMinutesElapsed(t *testing.T) { func TestManifestGenErrorCacheRespectsNoCache(t *testing.T) { - service := newService(".") + service := newService(t, ".") service.initConstants = RepoServerInitConstants{ ParallelismLimit: 1, @@ -780,7 +1006,7 @@ func TestManifestGenErrorCacheRespectsNoCache(t *testing.T) { } func TestGenerateHelmWithValues(t *testing.T) { - service := newService("../../util/helm/testdata/redis") + service := newService(t, "../../util/helm/testdata/redis") res, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -788,10 +1014,12 @@ func TestGenerateHelmWithValues(t *testing.T) { ApplicationSource: &argoappv1.ApplicationSource{ Path: ".", Helm: &argoappv1.ApplicationSourceHelm{ - ValueFiles: []string{"values-production.yaml"}, - Values: `cluster: {slaveCount: 2}`, + ValueFiles: []string{"values-production.yaml"}, + ValuesObject: &runtime.RawExtension{Raw: []byte(`cluster: {slaveCount: 2}`)}, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.NoError(t, err) @@ -815,7 +1043,7 @@ func TestGenerateHelmWithValues(t *testing.T) { } func TestHelmWithMissingValueFiles(t *testing.T) { - service := newService("../../util/helm/testdata/redis") + service := newService(t, "../../util/helm/testdata/redis") missingValuesFile := "values-prod-overrides.yaml" req := &apiclient.ManifestRequest{ @@ -827,6 +1055,8 @@ func TestHelmWithMissingValueFiles(t *testing.T) { ValueFiles: []string{"values-production.yaml", missingValuesFile}, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, } // Should fail since we're passing a non-existent values file, and error should indicate that @@ -841,7 +1071,7 @@ func TestHelmWithMissingValueFiles(t *testing.T) { } func TestGenerateHelmWithEnvVars(t *testing.T) { - service := newService("../../util/helm/testdata/redis") + service := newService(t, "../../util/helm/testdata/redis") res, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -852,6 +1082,8 @@ func TestGenerateHelmWithEnvVars(t *testing.T) { ValueFiles: []string{"values-$ARGOCD_APP_NAME.yaml"}, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.NoError(t, err) @@ -874,36 +1106,40 @@ func TestGenerateHelmWithEnvVars(t *testing.T) { } // The requested value file (`../minio/values.yaml`) is outside the app path (`./util/helm/testdata/redis`), however -// since the requested value is sill under the repo directory (`~/go/src/github.com/argoproj/argo-cd`), it is allowed +// since the requested value is still under the repo directory (`~/go/src/github.com/argoproj/argo-cd`), it is allowed func TestGenerateHelmWithValuesDirectoryTraversal(t *testing.T) { - service := newService("../../util/helm/testdata") + service := newService(t, "../../util/helm/testdata") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", ApplicationSource: &argoappv1.ApplicationSource{ Path: "./redis", Helm: &argoappv1.ApplicationSourceHelm{ - ValueFiles: []string{"../minio/values.yaml"}, - Values: `cluster: {slaveCount: 2}`, + ValueFiles: []string{"../minio/values.yaml"}, + ValuesObject: &runtime.RawExtension{Raw: []byte(`cluster: {slaveCount: 2}`)}, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.NoError(t, err) // Test the case where the path is "." - service = newService("./testdata") + service = newService(t, "./testdata") _, err = service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", ApplicationSource: &argoappv1.ApplicationSource{ Path: "./my-chart", }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.NoError(t, err) } func TestChartRepoWithOutOfBoundsSymlink(t *testing.T) { - service := newService(".") + service := newService(t, ".") source := &argoappv1.ApplicationSource{Chart: "out-of-bounds-chart", TargetRevision: ">= 1.0.0"} request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true} _, err := service.GenerateManifest(context.Background(), request) @@ -913,7 +1149,7 @@ func TestChartRepoWithOutOfBoundsSymlink(t *testing.T) { // This is a Helm first-class app with a values file inside the repo directory // (`~/go/src/github.com/argoproj/argo-cd/reposerver/repository`), so it is allowed func TestHelmManifestFromChartRepoWithValueFile(t *testing.T) { - service := newService(".") + service := newService(t, ".") source := &argoappv1.ApplicationSource{ Chart: "my-chart", TargetRevision: ">= 1.0.0", @@ -921,7 +1157,12 @@ func TestHelmManifestFromChartRepoWithValueFile(t *testing.T) { ValueFiles: []string{"./my-chart-values.yaml"}, }, } - request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true} + request := &apiclient.ManifestRequest{ + Repo: &argoappv1.Repository{}, + ApplicationSource: source, + NoCache: true, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}} response, err := service.GenerateManifest(context.Background(), request) assert.NoError(t, err) assert.NotNil(t, response) @@ -937,7 +1178,7 @@ func TestHelmManifestFromChartRepoWithValueFile(t *testing.T) { // This is a Helm first-class app with a values file outside the repo directory // (`~/go/src/github.com/argoproj/argo-cd/reposerver/repository`), so it is not allowed func TestHelmManifestFromChartRepoWithValueFileOutsideRepo(t *testing.T) { - service := newService(".") + service := newService(t, ".") source := &argoappv1.ApplicationSource{ Chart: "my-chart", TargetRevision: ">= 1.0.0", @@ -952,7 +1193,7 @@ func TestHelmManifestFromChartRepoWithValueFileOutsideRepo(t *testing.T) { func TestHelmManifestFromChartRepoWithValueFileLinks(t *testing.T) { t.Run("Valid symlink", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") source := &argoappv1.ApplicationSource{ Chart: "my-chart", TargetRevision: ">= 1.0.0", @@ -960,14 +1201,15 @@ func TestHelmManifestFromChartRepoWithValueFileLinks(t *testing.T) { ValueFiles: []string{"my-chart-link.yaml"}, }, } - request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true} + request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} _, err := service.GenerateManifest(context.Background(), request) assert.NoError(t, err) }) } func TestGenerateHelmWithURL(t *testing.T) { - service := newService("../../util/helm/testdata/redis") + service := newService(t, "../../util/helm/testdata/redis") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -975,11 +1217,13 @@ func TestGenerateHelmWithURL(t *testing.T) { ApplicationSource: &argoappv1.ApplicationSource{ Path: ".", Helm: &argoappv1.ApplicationSourceHelm{ - ValueFiles: []string{"https://raw.githubusercontent.com/argoproj/argocd-example-apps/master/helm-guestbook/values.yaml"}, - Values: `cluster: {slaveCount: 2}`, + ValueFiles: []string{"https://raw.githubusercontent.com/argoproj/argocd-example-apps/master/helm-guestbook/values.yaml"}, + ValuesObject: &runtime.RawExtension{Raw: []byte(`cluster: {slaveCount: 2}`)}, }, }, - HelmOptions: &argoappv1.HelmOptions{ValuesFileSchemes: []string{"https"}}, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, + HelmOptions: &argoappv1.HelmOptions{ValuesFileSchemes: []string{"https"}}, }) assert.NoError(t, err) } @@ -988,90 +1232,100 @@ func TestGenerateHelmWithURL(t *testing.T) { // (`~/go/src/github.com/argoproj/argo-cd/util/helm/testdata/redis`), so it is blocked func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { t.Run("Values file with relative path pointing outside repo root", func(t *testing.T) { - service := newService("../../util/helm/testdata/redis") + service := newService(t, "../../util/helm/testdata/redis") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", ApplicationSource: &argoappv1.ApplicationSource{ Path: ".", Helm: &argoappv1.ApplicationSourceHelm{ - ValueFiles: []string{"../minio/values.yaml"}, - Values: `cluster: {slaveCount: 2}`, + ValueFiles: []string{"../minio/values.yaml"}, + ValuesObject: &runtime.RawExtension{Raw: []byte(`cluster: {slaveCount: 2}`)}, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.Error(t, err) assert.Contains(t, err.Error(), "outside repository root") }) t.Run("Values file with relative path pointing inside repo root", func(t *testing.T) { - service := newService("./testdata") + service := newService(t, "./testdata") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", ApplicationSource: &argoappv1.ApplicationSource{ Path: "./my-chart", Helm: &argoappv1.ApplicationSourceHelm{ - ValueFiles: []string{"../my-chart/my-chart-values.yaml"}, - Values: `cluster: {slaveCount: 2}`, + ValueFiles: []string{"../my-chart/my-chart-values.yaml"}, + ValuesObject: &runtime.RawExtension{Raw: []byte(`cluster: {slaveCount: 2}`)}, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.NoError(t, err) }) t.Run("Values file with absolute path stays within repo root", func(t *testing.T) { - service := newService("./testdata") + service := newService(t, "./testdata") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", ApplicationSource: &argoappv1.ApplicationSource{ Path: "./my-chart", Helm: &argoappv1.ApplicationSourceHelm{ - ValueFiles: []string{"/my-chart/my-chart-values.yaml"}, - Values: `cluster: {slaveCount: 2}`, + ValueFiles: []string{"/my-chart/my-chart-values.yaml"}, + ValuesObject: &runtime.RawExtension{Raw: []byte(`cluster: {slaveCount: 2}`)}, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.NoError(t, err) }) t.Run("Values file with absolute path using back-references outside repo root", func(t *testing.T) { - service := newService("./testdata") + service := newService(t, "./testdata") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", ApplicationSource: &argoappv1.ApplicationSource{ Path: "./my-chart", Helm: &argoappv1.ApplicationSourceHelm{ - ValueFiles: []string{"/../../../my-chart-values.yaml"}, - Values: `cluster: {slaveCount: 2}`, + ValueFiles: []string{"/../../../my-chart-values.yaml"}, + ValuesObject: &runtime.RawExtension{Raw: []byte(`cluster: {slaveCount: 2}`)}, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.Error(t, err) assert.Contains(t, err.Error(), "outside repository root") }) t.Run("Remote values file from forbidden protocol", func(t *testing.T) { - service := newService("./testdata") + service := newService(t, "./testdata") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", ApplicationSource: &argoappv1.ApplicationSource{ Path: "./my-chart", Helm: &argoappv1.ApplicationSourceHelm{ - ValueFiles: []string{"file://../../../../my-chart-values.yaml"}, - Values: `cluster: {slaveCount: 2}`, + ValueFiles: []string{"file://../../../../my-chart-values.yaml"}, + ValuesObject: &runtime.RawExtension{Raw: []byte(`cluster: {slaveCount: 2}`)}, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.Error(t, err) assert.Contains(t, err.Error(), "is not allowed") }) t.Run("Remote values file from custom allowed protocol", func(t *testing.T) { - service := newService("./testdata") + service := newService(t, "./testdata") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", @@ -1081,7 +1335,9 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { ValueFiles: []string{"s3://my-bucket/my-chart-values.yaml"}, }, }, - HelmOptions: &argoappv1.HelmOptions{ValuesFileSchemes: []string{"s3"}}, + HelmOptions: &argoappv1.HelmOptions{ValuesFileSchemes: []string{"s3"}}, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.Error(t, err) assert.Contains(t, err.Error(), "s3://my-bucket/my-chart-values.yaml: no such file or directory") @@ -1090,7 +1346,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { // File parameter should not allow traversal outside of the repository root func TestGenerateHelmWithAbsoluteFileParameter(t *testing.T) { - service := newService("../..") + service := newService(t, "../..") file, err := os.CreateTemp("", "external-secret.txt") assert.NoError(t, err) @@ -1112,53 +1368,59 @@ func TestGenerateHelmWithAbsoluteFileParameter(t *testing.T) { ApplicationSource: &argoappv1.ApplicationSource{ Path: "./util/helm/testdata/redis", Helm: &argoappv1.ApplicationSourceHelm{ - ValueFiles: []string{"values-production.yaml"}, - Values: `cluster: {slaveCount: 2}`, + ValueFiles: []string{"values-production.yaml"}, + ValuesObject: &runtime.RawExtension{Raw: []byte(`cluster: {slaveCount: 2}`)}, FileParameters: []argoappv1.HelmFileParameter{{ Name: "passwordContent", Path: externalSecretPath, }}, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.Error(t, err) } // The requested file parameter (`../external/external-secret.txt`) is outside the app path -// (`./util/helm/testdata/redis`), however since the requested value is sill under the repo +// (`./util/helm/testdata/redis`), however since the requested value is still under the repo // directory (`~/go/src/github.com/argoproj/argo-cd`), it is allowed. It is used as a means of // providing direct content to a helm chart via a specific key. func TestGenerateHelmWithFileParameter(t *testing.T) { - service := newService("../../util/helm/testdata") + service := newService(t, "../../util/helm/testdata") - _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ + res, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", ApplicationSource: &argoappv1.ApplicationSource{ Path: "./redis", Helm: &argoappv1.ApplicationSourceHelm{ - ValueFiles: []string{"values-production.yaml"}, - Values: `cluster: {slaveCount: 2}`, - FileParameters: []argoappv1.HelmFileParameter{ - argoappv1.HelmFileParameter{ - Name: "passwordContent", - Path: "../external/external-secret.txt", - }, - }, + ValueFiles: []string{"values-production.yaml"}, + Values: `cluster: {slaveCount: 10}`, + ValuesObject: &runtime.RawExtension{Raw: []byte(`cluster: {slaveCount: 2}`)}, + FileParameters: []argoappv1.HelmFileParameter{{ + Name: "passwordContent", + Path: "../external/external-secret.txt", + }}, }, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.NoError(t, err) + assert.Contains(t, res.Manifests[6], `"replicas":2`, "ValuesObject should override Values") } func TestGenerateNullList(t *testing.T) { - service := newService(".") + service := newService(t, ".") t.Run("null list", func(t *testing.T) { res1, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, - ApplicationSource: &argoappv1.ApplicationSource{Path: "./testdata/null-list"}, - NoCache: true, + Repo: &argoappv1.Repository{}, + ApplicationSource: &argoappv1.ApplicationSource{Path: "./testdata/null-list"}, + NoCache: true, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.Nil(t, err) assert.Equal(t, len(res1.Manifests), 1) @@ -1167,9 +1429,11 @@ func TestGenerateNullList(t *testing.T) { t.Run("empty list", func(t *testing.T) { res1, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, - ApplicationSource: &argoappv1.ApplicationSource{Path: "./testdata/empty-list"}, - NoCache: true, + Repo: &argoappv1.Repository{}, + ApplicationSource: &argoappv1.ApplicationSource{Path: "./testdata/empty-list"}, + NoCache: true, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.Nil(t, err) assert.Equal(t, len(res1.Manifests), 1) @@ -1178,9 +1442,11 @@ func TestGenerateNullList(t *testing.T) { t.Run("weird list", func(t *testing.T) { res1, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, - ApplicationSource: &argoappv1.ApplicationSource{Path: "./testdata/weird-list"}, - NoCache: true, + Repo: &argoappv1.Repository{}, + ApplicationSource: &argoappv1.ApplicationSource{Path: "./testdata/weird-list"}, + NoCache: true, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.Nil(t, err) assert.Len(t, res1.Manifests, 2) @@ -1188,66 +1454,25 @@ func TestGenerateNullList(t *testing.T) { } func TestIdentifyAppSourceTypeByAppDirWithKustomizations(t *testing.T) { - sourceType, err := GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yaml", "testapp", map[string]bool{}, []string{}) + sourceType, err := GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yaml", "./testdata", "testapp", map[string]bool{}, []string{}, []string{}) assert.Nil(t, err) assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType) - sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yml", "testapp", map[string]bool{}, []string{}) + sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yml", "./testdata", "testapp", map[string]bool{}, []string{}, []string{}) assert.Nil(t, err) assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType) - sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/Kustomization", "testapp", map[string]bool{}, []string{}) + sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/Kustomization", "./testdata", "testapp", map[string]bool{}, []string{}, []string{}) assert.Nil(t, err) assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType) } -func TestRunCustomTool(t *testing.T) { - service := newService(".") - - res, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ - AppName: "test-app", - Namespace: "test-namespace", - ApplicationSource: &argoappv1.ApplicationSource{ - Plugin: &argoappv1.ApplicationSourcePlugin{ - Name: "test", - Env: argoappv1.Env{ - { - Name: "TEST_REVISION", - Value: "prefix-$ARGOCD_APP_REVISION", - }, - }, - }, - }, - Plugins: []*argoappv1.ConfigManagementPlugin{{ - Name: "test", - Generate: argoappv1.Command{ - Command: []string{"sh", "-c"}, - Args: []string{`echo "{\"kind\": \"FakeObject\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"GIT_ASKPASS\": \"$GIT_ASKPASS\", \"GIT_USERNAME\": \"$GIT_USERNAME\", \"GIT_PASSWORD\": \"$GIT_PASSWORD\"}, \"labels\": {\"revision\": \"$ARGOCD_ENV_TEST_REVISION\"}}}"`}, - }, - }}, - Repo: &argoappv1.Repository{ - Username: "foo", Password: "bar", - }, - }) - - assert.NoError(t, err) - assert.Equal(t, 1, len(res.Manifests)) - - obj := &unstructured.Unstructured{} - assert.NoError(t, json.Unmarshal([]byte(res.Manifests[0]), obj)) - - assert.Equal(t, obj.GetName(), "test-app") - assert.Equal(t, obj.GetNamespace(), "test-namespace") - assert.Empty(t, obj.GetAnnotations()["GIT_USERNAME"]) - assert.Empty(t, obj.GetAnnotations()["GIT_PASSWORD"]) - // Git client is mocked, so the revision is always mock.Anything - assert.Equal(t, map[string]string{"revision": "prefix-mock.Anything"}, obj.GetLabels()) -} - func TestGenerateFromUTF16(t *testing.T) { q := apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, - ApplicationSource: &argoappv1.ApplicationSource{}, + Repo: &argoappv1.Repository{}, + ApplicationSource: &argoappv1.ApplicationSource{}, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, } res1, err := GenerateManifests(context.Background(), "./testdata/utf-16", "/", "", &q, false, &git.NoopCredsStore{}, resource.MustParse("0"), nil) assert.Nil(t, err) @@ -1255,7 +1480,7 @@ func TestGenerateFromUTF16(t *testing.T) { } func TestListApps(t *testing.T) { - service := newService("./testdata") + service := newService(t, "./testdata") res, err := service.ListApps(context.Background(), &apiclient.ListAppsRequest{Repo: &argoappv1.Repository{}}) assert.NoError(t, err) @@ -1276,12 +1501,14 @@ func TestListApps(t *testing.T) { "oci-dependencies": "Helm", "out-of-bounds-values-file-link": "Helm", "values-files": "Helm", + "helm-with-dependencies": "Helm", + "helm-with-dependencies-alias": "Helm", } assert.Equal(t, expectedApps, res.Apps) } func TestGetAppDetailsHelm(t *testing.T) { - service := newService("../../util/helm/testdata/dependency") + service := newService(t, "../../util/helm/testdata/dependency") res, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1296,8 +1523,26 @@ func TestGetAppDetailsHelm(t *testing.T) { assert.Equal(t, "Helm", res.Type) assert.EqualValues(t, []string{"values-production.yaml", "values.yaml"}, res.Helm.ValueFiles) } + +func TestGetAppDetailsHelmUsesCache(t *testing.T) { + service := newService(t, "../../util/helm/testdata/dependency") + + res, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ + Repo: &argoappv1.Repository{}, + Source: &argoappv1.ApplicationSource{ + Path: ".", + }, + }) + + assert.NoError(t, err) + assert.NotNil(t, res.Helm) + + assert.Equal(t, "Helm", res.Type) + assert.EqualValues(t, []string{"values-production.yaml", "values.yaml"}, res.Helm.ValueFiles) +} + func TestGetAppDetailsHelm_WithNoValuesFile(t *testing.T) { - service := newService("../../util/helm/testdata/api-versions") + service := newService(t, "../../util/helm/testdata/api-versions") res, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1315,7 +1560,7 @@ func TestGetAppDetailsHelm_WithNoValuesFile(t *testing.T) { } func TestGetAppDetailsKustomize(t *testing.T) { - service := newService("../../util/kustomize/testdata/kustomization_yaml") + service := newService(t, "../../util/kustomize/testdata/kustomization_yaml") res, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1328,11 +1573,11 @@ func TestGetAppDetailsKustomize(t *testing.T) { assert.Equal(t, "Kustomize", res.Type) assert.NotNil(t, res.Kustomize) - assert.EqualValues(t, []string{"nginx:1.15.4", "k8s.gcr.io/nginx-slim:0.8"}, res.Kustomize.Images) + assert.EqualValues(t, []string{"nginx:1.15.4", "registry.k8s.io/nginx-slim:0.8"}, res.Kustomize.Images) } func TestGetHelmCharts(t *testing.T) { - service := newService("../..") + service := newService(t, "../..") res, err := service.GetHelmCharts(context.Background(), &apiclient.HelmChartsRequest{Repo: &argoappv1.Repository{}}) // fix flakiness @@ -1353,7 +1598,7 @@ func TestGetHelmCharts(t *testing.T) { } func TestGetRevisionMetadata(t *testing.T) { - service, gitClient := newServiceWithMocks("../..", false) + service, gitClient, _ := newServiceWithMocks(t, "../..", false) now := time.Now() gitClient.On("RevisionMetadata", mock.Anything).Return(&git.RevisionMetadata{ @@ -1421,10 +1666,16 @@ func TestGetRevisionMetadata(t *testing.T) { func TestGetSignatureVerificationResult(t *testing.T) { // Commit with signature and verification requested { - service := newServiceWithSignature("../../manifests/base") + service := newServiceWithSignature(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} - q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, VerifySignature: true} + q := apiclient.ManifestRequest{ + Repo: &argoappv1.Repository{}, + ApplicationSource: &src, + VerifySignature: true, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, + } res, err := service.GenerateManifest(context.Background(), &q) assert.NoError(t, err) @@ -1432,10 +1683,11 @@ func TestGetSignatureVerificationResult(t *testing.T) { } // Commit with signature and verification not requested { - service := newServiceWithSignature("../../manifests/base") + service := newServiceWithSignature(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} - q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src} + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} res, err := service.GenerateManifest(context.Background(), &q) assert.NoError(t, err) @@ -1443,10 +1695,11 @@ func TestGetSignatureVerificationResult(t *testing.T) { } // Commit without signature and verification requested { - service := newService("../../manifests/base") + service := newService(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} - q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, VerifySignature: true} + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, VerifySignature: true, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} res, err := service.GenerateManifest(context.Background(), &q) assert.NoError(t, err) @@ -1454,10 +1707,11 @@ func TestGetSignatureVerificationResult(t *testing.T) { } // Commit without signature and verification not requested { - service := newService("../../manifests/base") + service := newService(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} - q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, VerifySignature: true} + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, VerifySignature: true, ProjectName: "something", + ProjectSourceRepos: []string{"*"}} res, err := service.GenerateManifest(context.Background(), &q) assert.NoError(t, err) @@ -1470,6 +1724,7 @@ func Test_newEnv(t *testing.T) { &argoappv1.EnvEntry{Name: "ARGOCD_APP_NAME", Value: "my-app-name"}, &argoappv1.EnvEntry{Name: "ARGOCD_APP_NAMESPACE", Value: "my-namespace"}, &argoappv1.EnvEntry{Name: "ARGOCD_APP_REVISION", Value: "my-revision"}, + &argoappv1.EnvEntry{Name: "ARGOCD_APP_REVISION_SHORT", Value: "my-revi"}, &argoappv1.EnvEntry{Name: "ARGOCD_APP_SOURCE_REPO_URL", Value: "https://github.com/my-org/my-repo"}, &argoappv1.EnvEntry{Name: "ARGOCD_APP_SOURCE_PATH", Value: "my-path"}, &argoappv1.EnvEntry{Name: "ARGOCD_APP_SOURCE_TARGET_REVISION", Value: "my-target-revision"}, @@ -1485,7 +1740,7 @@ func Test_newEnv(t *testing.T) { } func TestService_newHelmClientResolveRevision(t *testing.T) { - service := newService(".") + service := newService(t, ".") t.Run("EmptyRevision", func(t *testing.T) { _, _, err := service.newHelmClientResolveRevision(&argoappv1.Repository{}, "", "", true) @@ -1499,7 +1754,7 @@ func TestService_newHelmClientResolveRevision(t *testing.T) { func TestGetAppDetailsWithAppParameterFile(t *testing.T) { t.Run("No app name set and app specific file exists", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "multi", func(t *testing.T, path string) { details, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1512,7 +1767,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) { }) }) t.Run("No app specific override", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "single-global", func(t *testing.T, path string) { details, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1526,7 +1781,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) { }) }) t.Run("Only app specific override", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "single-app-only", func(t *testing.T, path string) { details, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1540,7 +1795,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) { }) }) t.Run("App specific override", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "multi", func(t *testing.T, path string) { details, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1554,7 +1809,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) { }) }) t.Run("App specific overrides containing non-mergeable field", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "multi", func(t *testing.T, path string) { details, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1568,7 +1823,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) { }) }) t.Run("Broken app-specific overrides", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "multi", func(t *testing.T, path string) { _, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1610,12 +1865,14 @@ func runWithTempTestdata(t *testing.T, path string, runner func(t *testing.T, pa func TestGenerateManifestsWithAppParameterFile(t *testing.T) { t.Run("Single global override", func(t *testing.T) { runWithTempTestdata(t, "single-global", func(t *testing.T, path string) { - service := newService(".") + service := newService(t, ".") manifests, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{ Path: path, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) require.NoError(t, err) resourceByKindName := make(map[string]*unstructured.Unstructured) @@ -1639,12 +1896,14 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { t.Run("Single global override Helm", func(t *testing.T) { runWithTempTestdata(t, "single-global-helm", func(t *testing.T, path string) { - service := newService(".") + service := newService(t, ".") manifests, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{ Path: path, }, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) require.NoError(t, err) resourceByKindName := make(map[string]*unstructured.Unstructured) @@ -1667,14 +1926,16 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { }) t.Run("Application specific override", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "single-app-only", func(t *testing.T, path string) { manifests, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{ Path: path, }, - AppName: "testapp", + AppName: "testapp", + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) require.NoError(t, err) resourceByKindName := make(map[string]*unstructured.Unstructured) @@ -1696,15 +1957,38 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { }) }) + t.Run("Multi-source with source as ref only does not generate manifests", func(t *testing.T) { + service := newService(t, ".") + runWithTempTestdata(t, "single-app-only", func(t *testing.T, path string) { + manifests, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ + Repo: &argoappv1.Repository{}, + ApplicationSource: &argoappv1.ApplicationSource{ + Path: "", + Chart: "", + Ref: "test", + }, + AppName: "testapp-multi-ref-only", + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, + HasMultipleSources: true, + }) + assert.NoError(t, err) + assert.Empty(t, manifests.Manifests) + assert.NotEmpty(t, manifests.Revision) + }) + }) + t.Run("Application specific override for other app", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "single-app-only", func(t *testing.T, path string) { manifests, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{ Path: path, }, - AppName: "testapp2", + AppName: "testapp2", + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) require.NoError(t, err) resourceByKindName := make(map[string]*unstructured.Unstructured) @@ -1727,23 +2011,25 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { }) t.Run("Override info does not appear in cache key", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "single-global", func(t *testing.T, path string) { source := &argoappv1.ApplicationSource{ Path: path, } sourceCopy := source.DeepCopy() // make a copy in case GenerateManifest mutates it. _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ - Repo: &argoappv1.Repository{}, - ApplicationSource: sourceCopy, - AppName: "test", + Repo: &argoappv1.Repository{}, + ApplicationSource: sourceCopy, + AppName: "test", + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }) assert.NoError(t, err) res := &cache.CachedManifestResponse{} // Try to pull from the cache with a `source` that does not include any overrides. Overrides should not be // part of the cache key, because you can't get the overrides without a repo operation. And avoiding repo // operations is the point of the cache. - err = service.cache.GetManifests(mock.Anything, source, argoappv1.RefTargetRevisionMapping{}, &argoappv1.ClusterInfo{}, "", "", "", "test", res) + err = service.cache.GetManifests(mock.Anything, source, argoappv1.RefTargetRevisionMapping{}, &argoappv1.ClusterInfo{}, "", "", "", "test", res, nil) assert.NoError(t, err) }) }) @@ -1770,10 +2056,12 @@ func TestGenerateManifestWithAnnotatedAndRegularGitTagHashes(t *testing.T) { ApplicationSource: &argoappv1.ApplicationSource{ TargetRevision: regularGitTagHash, }, - NoCache: true, + NoCache: true, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }, wantError: false, - service: newServiceWithCommitSHA(".", regularGitTagHash), + service: newServiceWithCommitSHA(t, ".", regularGitTagHash), }, { @@ -1784,10 +2072,12 @@ func TestGenerateManifestWithAnnotatedAndRegularGitTagHashes(t *testing.T) { ApplicationSource: &argoappv1.ApplicationSource{ TargetRevision: annotatedGitTaghash, }, - NoCache: true, + NoCache: true, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }, wantError: false, - service: newServiceWithCommitSHA(".", annotatedGitTaghash), + service: newServiceWithCommitSHA(t, ".", annotatedGitTaghash), }, { @@ -1798,10 +2088,12 @@ func TestGenerateManifestWithAnnotatedAndRegularGitTagHashes(t *testing.T) { ApplicationSource: &argoappv1.ApplicationSource{ TargetRevision: invalidGitTaghash, }, - NoCache: true, + NoCache: true, + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, }, wantError: true, - service: newServiceWithCommitSHA(".", invalidGitTaghash), + service: newServiceWithCommitSHA(t, ".", invalidGitTaghash), }, } for _, tt := range tests { @@ -1823,6 +2115,44 @@ func TestGenerateManifestWithAnnotatedAndRegularGitTagHashes(t *testing.T) { } } +func TestGenerateManifestWithAnnotatedTagsAndMultiSourceApp(t *testing.T) { + annotatedGitTaghash := "95249be61b028d566c29d47b19e65c5603388a41" + + service := newServiceWithCommitSHA(t, ".", annotatedGitTaghash) + + refSources := map[string]*argoappv1.RefTarget{} + + refSources["$global"] = &argoappv1.RefTarget{ + TargetRevision: annotatedGitTaghash, + } + + refSources["$default"] = &argoappv1.RefTarget{ + TargetRevision: annotatedGitTaghash, + } + + manifestRequest := &apiclient.ManifestRequest{ + Repo: &argoappv1.Repository{}, + ApplicationSource: &argoappv1.ApplicationSource{ + TargetRevision: annotatedGitTaghash, + Helm: &argoappv1.ApplicationSourceHelm{ + ValueFiles: []string{"$global/values.yaml", "$default/secrets.yaml"}, + }, + }, + HasMultipleSources: true, + NoCache: true, + RefSources: refSources, + } + + response, err := service.GenerateManifest(context.Background(), manifestRequest) + if err != nil { + t.Errorf("unexpected %s", err) + } + + if response.Revision != annotatedGitTaghash { + t.Errorf("returned SHA %s is different from expected annotated tag %s", response.Revision, annotatedGitTaghash) + } +} + func TestFindResources(t *testing.T) { testCases := []struct { name string @@ -2373,7 +2703,7 @@ func Test_findManifests(t *testing.T) { } func TestTestRepoOCI(t *testing.T) { - service := newService(".") + service := newService(t, ".") _, err := service.TestRepository(context.Background(), &apiclient.TestRepositoryRequest{ Repo: &argoappv1.Repository{ Repo: "https://demo.goharbor.io", @@ -2398,7 +2728,7 @@ func Test_getHelmDependencyRepos(t *testing.T) { func TestResolveRevision(t *testing.T) { - service := newService(".") + service := newService(t, ".") repo := &argoappv1.Repository{Repo: "https://github.com/argoproj/argo-cd"} app := &argoappv1.Application{Spec: argoappv1.ApplicationSpec{Source: &argoappv1.ApplicationSource{}}} resolveRevisionResponse, err := service.ResolveRevision(context.Background(), &apiclient.ResolveRevisionRequest{ @@ -2420,7 +2750,7 @@ func TestResolveRevision(t *testing.T) { func TestResolveRevisionNegativeScenarios(t *testing.T) { - service := newService(".") + service := newService(t, ".") repo := &argoappv1.Repository{Repo: "https://github.com/argoproj/argo-cd"} app := &argoappv1.Application{Spec: argoappv1.ApplicationSpec{Source: &argoappv1.ApplicationSource{}}} resolveRevisionResponse, err := service.ResolveRevision(context.Background(), &apiclient.ResolveRevisionRequest{ @@ -2467,19 +2797,57 @@ func TestDirectoryPermissionInitializer(t *testing.T) { require.Error(t, err) } -func initGitRepo(repoPath string, remote string) error { - if err := os.Mkdir(repoPath, 0755); err != nil { - return err +func addHelmToGitRepo(t *testing.T, options newGitRepoOptions) { + err := os.WriteFile(filepath.Join(options.path, "Chart.yaml"), []byte("name: test\nversion: v1.0.0"), 0777) + assert.NoError(t, err) + for valuesFileName, values := range options.helmChartOptions.valuesFiles { + valuesFileContents, err := yaml.Marshal(values) + assert.NoError(t, err) + err = os.WriteFile(filepath.Join(options.path, valuesFileName), valuesFileContents, 0777) + assert.NoError(t, err) + } + assert.NoError(t, err) + cmd := exec.Command("git", "add", "-A") + cmd.Dir = options.path + assert.NoError(t, cmd.Run()) + cmd = exec.Command("git", "commit", "-m", "Initial commit") + cmd.Dir = options.path + assert.NoError(t, cmd.Run()) +} + +func initGitRepo(t *testing.T, options newGitRepoOptions) (revision string) { + if options.createPath { + assert.NoError(t, os.Mkdir(options.path, 0755)) + } + + cmd := exec.Command("git", "init", "-b", "main", options.path) + cmd.Dir = options.path + assert.NoError(t, cmd.Run()) + + if options.remote != "" { + cmd = exec.Command("git", "remote", "add", "origin", options.path) + cmd.Dir = options.path + assert.NoError(t, cmd.Run()) } - cmd := exec.Command("git", "init", repoPath) - cmd.Dir = repoPath - if err := cmd.Run(); err != nil { - return err + commitAdded := options.addEmptyCommit || options.helmChartOptions.chartName != "" + if options.addEmptyCommit { + cmd = exec.Command("git", "commit", "-m", "Initial commit", "--allow-empty") + cmd.Dir = options.path + assert.NoError(t, cmd.Run()) + } else if options.helmChartOptions.chartName != "" { + addHelmToGitRepo(t, options) } - cmd = exec.Command("git", "remote", "add", "origin", remote) - cmd.Dir = repoPath - return cmd.Run() + + if commitAdded { + var revB bytes.Buffer + cmd = exec.Command("git", "rev-parse", "HEAD", options.path) + cmd.Dir = options.path + cmd.Stdout = &revB + assert.NoError(t, cmd.Run()) + revision = strings.Split(revB.String(), "\n")[0] + } + return revision } func TestInit(t *testing.T) { @@ -2492,16 +2860,16 @@ func TestInit(t *testing.T) { }) repoPath := path.Join(dir, "repo1") - require.NoError(t, initGitRepo(repoPath, "https://github.com/argo-cd/test-repo1")) + initGitRepo(t, newGitRepoOptions{path: repoPath, remote: "https://github.com/argo-cd/test-repo1", createPath: true, addEmptyCommit: false}) - service := newService(".") + service := newService(t, ".") service.rootDir = dir require.NoError(t, service.Init()) _, err := os.ReadDir(dir) require.Error(t, err) - require.NoError(t, initGitRepo(path.Join(dir, "repo2"), "https://github.com/argo-cd/test-repo2")) + initGitRepo(t, newGitRepoOptions{path: path.Join(dir, "repo2"), remote: "https://github.com/argo-cd/test-repo2", createPath: true, addEmptyCommit: false}) } // TestCheckoutRevisionCanGetNonstandardRefs shows that we can fetch a revision that points to a non-standard ref. In @@ -2551,16 +2919,27 @@ func runGit(t *testing.T, workDir string, args ...string) string { return stringOut } -func Test_findHelmValueFilesInPath(t *testing.T) { +func Test_walkHelmValueFilesInPath(t *testing.T) { t.Run("does not exist", func(t *testing.T) { - files, err := findHelmValueFilesInPath("/obviously/does/not/exist") + var files []string + root := "/obviously/does/not/exist" + err := filepath.Walk(root, walkHelmValueFilesInPath(root, &files)) assert.Error(t, err) assert.Empty(t, files) }) t.Run("values files", func(t *testing.T) { - files, err := findHelmValueFilesInPath("./testdata/values-files") + var files []string + root := "./testdata/values-files" + err := filepath.Walk(root, walkHelmValueFilesInPath(root, &files)) assert.NoError(t, err) - assert.Len(t, files, 4) + assert.Len(t, files, 5) + }) + t.Run("unrelated root", func(t *testing.T) { + var files []string + root := "./testdata/values-files" + unrelated_root := "/different/root/path" + err := filepath.Walk(root, walkHelmValueFilesInPath(unrelated_root, &files)) + assert.Error(t, err) }) } @@ -2578,7 +2957,7 @@ func Test_populateHelmAppDetails(t *testing.T) { err = populateHelmAppDetails(&res, appPath, appPath, &q, emptyTempPaths) require.NoError(t, err) assert.Len(t, res.Helm.Parameters, 3) - assert.Len(t, res.Helm.ValueFiles, 4) + assert.Len(t, res.Helm.ValueFiles, 5) } func Test_populateHelmAppDetails_values_symlinks(t *testing.T) { @@ -2602,19 +2981,51 @@ func Test_populateHelmAppDetails_values_symlinks(t *testing.T) { }) } -func TestOCIDependencies(t *testing.T) { +func TestGetHelmRepos_OCIDependencies(t *testing.T) { src := argoappv1.ApplicationSource{Path: "."} q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, HelmRepoCreds: []*argoappv1.RepoCreds{ {URL: "example.com", Username: "test", Password: "test", EnableOCI: true}, }} - err := populateRequestRepos("./testdata/oci-dependencies", &q) + helmRepos, err := getHelmRepos("./testdata/oci-dependencies", q.Repos, q.HelmRepoCreds) + assert.Nil(t, err) + + assert.Equal(t, len(helmRepos), 1) + assert.Equal(t, helmRepos[0].Username, "test") + assert.Equal(t, helmRepos[0].EnableOci, true) + assert.Equal(t, helmRepos[0].Repo, "example.com/myrepo") +} + +func TestGetHelmRepo_NamedRepos(t *testing.T) { + src := argoappv1.ApplicationSource{Path: "."} + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, Repos: []*argoappv1.Repository{{ + Name: "custom-repo", + Repo: "https://example.com", + Username: "test", + }}} + + helmRepos, err := getHelmRepos("./testdata/helm-with-dependencies", q.Repos, q.HelmRepoCreds) + assert.Nil(t, err) + + assert.Equal(t, len(helmRepos), 1) + assert.Equal(t, helmRepos[0].Username, "test") + assert.Equal(t, helmRepos[0].Repo, "https://example.com") +} + +func TestGetHelmRepo_NamedReposAlias(t *testing.T) { + src := argoappv1.ApplicationSource{Path: "."} + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, Repos: []*argoappv1.Repository{{ + Name: "custom-repo-alias", + Repo: "https://example.com", + Username: "test-alias", + }}} + + helmRepos, err := getHelmRepos("./testdata/helm-with-dependencies-alias", q.Repos, q.HelmRepoCreds) assert.Nil(t, err) - assert.Equal(t, len(q.Repos), 1) - assert.Equal(t, q.Repos[0].Username, "test") - assert.Equal(t, q.Repos[0].EnableOCI, true) - assert.Equal(t, q.Repos[0].Repo, "example.com") + assert.Equal(t, len(helmRepos), 1) + assert.Equal(t, helmRepos[0].Username, "test-alias") + assert.Equal(t, helmRepos[0].Repo, "https://example.com") } func Test_getResolvedValueFiles(t *testing.T) { @@ -2772,3 +3183,374 @@ func Test_getResolvedValueFiles(t *testing.T) { }) } } +func TestErrorGetGitDirectories(t *testing.T) { + type fields struct { + service *Service + } + type args struct { + ctx context.Context + request *apiclient.GitDirectoriesRequest + } + tests := []struct { + name string + fields fields + args args + want *apiclient.GitDirectoriesResponse + wantErr assert.ErrorAssertionFunc + }{ + {name: "InvalidRepo", fields: fields{service: newService(t, ".")}, args: args{ + ctx: context.TODO(), + request: &apiclient.GitDirectoriesRequest{ + Repo: nil, + SubmoduleEnabled: false, + Revision: "HEAD", + }, + }, want: nil, wantErr: assert.Error}, + {name: "InvalidResolveRevision", fields: fields{service: func() *Service { + s, _, _ := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { + gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) + gitClient.On("LsRemote", mock.Anything).Return("", fmt.Errorf("ah error")) + paths.On("GetPath", mock.Anything).Return(".", nil) + paths.On("GetPathIfExists", mock.Anything).Return(".", nil) + }, ".") + return s + }()}, args: args{ + ctx: context.TODO(), + request: &apiclient.GitDirectoriesRequest{ + Repo: &argoappv1.Repository{Repo: "not-a-valid-url"}, + SubmoduleEnabled: false, + Revision: "sadfsadf", + }, + }, want: nil, wantErr: assert.Error}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := tt.fields.service + got, err := s.GetGitDirectories(tt.args.ctx, tt.args.request) + if !tt.wantErr(t, err, fmt.Sprintf("GetGitDirectories(%v, %v)", tt.args.ctx, tt.args.request)) { + return + } + assert.Equalf(t, tt.want, got, "GetGitDirectories(%v, %v)", tt.args.ctx, tt.args.request) + }) + } +} + +func TestGetGitDirectories(t *testing.T) { + // test not using the cache + root := "./testdata/git-files-dirs" + s, _, cacheMocks := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { + gitClient.On("Init").Return(nil) + gitClient.On("Fetch", mock.Anything).Return(nil) + gitClient.On("Checkout", mock.Anything, mock.Anything).Once().Return(nil) + gitClient.On("LsRemote", "HEAD").Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil) + gitClient.On("Root").Return(root) + paths.On("GetPath", mock.Anything).Return(root, nil) + paths.On("GetPathIfExists", mock.Anything).Return(root, nil) + }, root) + dirRequest := &apiclient.GitDirectoriesRequest{ + Repo: &argoappv1.Repository{Repo: "a-url.com"}, + SubmoduleEnabled: false, + Revision: "HEAD", + } + directories, err := s.GetGitDirectories(context.TODO(), dirRequest) + assert.Nil(t, err) + assert.ElementsMatch(t, directories.GetPaths(), []string{"app", "app/bar", "app/foo/bar", "somedir", "app/foo"}) + + // do the same request again to use the cache + // we only allow CheckOut to be called once in the mock + directories, err = s.GetGitDirectories(context.TODO(), dirRequest) + assert.Nil(t, err) + assert.ElementsMatch(t, []string{"app", "app/bar", "app/foo/bar", "somedir", "app/foo"}, directories.GetPaths()) + cacheMocks.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ + ExternalSets: 1, + ExternalGets: 2, + }) +} + +func TestErrorGetGitFiles(t *testing.T) { + type fields struct { + service *Service + } + type args struct { + ctx context.Context + request *apiclient.GitFilesRequest + } + tests := []struct { + name string + fields fields + args args + want *apiclient.GitFilesResponse + wantErr assert.ErrorAssertionFunc + }{ + {name: "InvalidRepo", fields: fields{service: newService(t, ".")}, args: args{ + ctx: context.TODO(), + request: &apiclient.GitFilesRequest{ + Repo: nil, + SubmoduleEnabled: false, + Revision: "HEAD", + }, + }, want: nil, wantErr: assert.Error}, + {name: "InvalidResolveRevision", fields: fields{service: func() *Service { + s, _, _ := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { + gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) + gitClient.On("LsRemote", mock.Anything).Return("", fmt.Errorf("ah error")) + paths.On("GetPath", mock.Anything).Return(".", nil) + paths.On("GetPathIfExists", mock.Anything).Return(".", nil) + }, ".") + return s + }()}, args: args{ + ctx: context.TODO(), + request: &apiclient.GitFilesRequest{ + Repo: &argoappv1.Repository{Repo: "not-a-valid-url"}, + SubmoduleEnabled: false, + Revision: "sadfsadf", + }, + }, want: nil, wantErr: assert.Error}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := tt.fields.service + got, err := s.GetGitFiles(tt.args.ctx, tt.args.request) + if !tt.wantErr(t, err, fmt.Sprintf("GetGitFiles(%v, %v)", tt.args.ctx, tt.args.request)) { + return + } + assert.Equalf(t, tt.want, got, "GetGitFiles(%v, %v)", tt.args.ctx, tt.args.request) + }) + } +} + +func TestGetGitFiles(t *testing.T) { + // test not using the cache + files := []string{"./testdata/git-files-dirs/somedir/config.yaml", + "./testdata/git-files-dirs/config.yaml", "./testdata/git-files-dirs/config.yaml", "./testdata/git-files-dirs/app/foo/bar/config.yaml"} + root := "" + s, _, cacheMocks := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { + gitClient.On("Init").Return(nil) + gitClient.On("Fetch", mock.Anything).Return(nil) + gitClient.On("Checkout", mock.Anything, mock.Anything).Once().Return(nil) + gitClient.On("LsRemote", "HEAD").Return("632039659e542ed7de0c170a4fcc1c571b288fc0", nil) + gitClient.On("Root").Return(root) + gitClient.On("LsFiles", mock.Anything, mock.Anything).Once().Return(files, nil) + paths.On("GetPath", mock.Anything).Return(root, nil) + paths.On("GetPathIfExists", mock.Anything).Return(root, nil) + }, root) + filesRequest := &apiclient.GitFilesRequest{ + Repo: &argoappv1.Repository{Repo: "a-url.com"}, + SubmoduleEnabled: false, + Revision: "HEAD", + } + + // expected map + expected := make(map[string][]byte) + for _, filePath := range files { + fileContents, err := os.ReadFile(filePath) + assert.Nil(t, err) + expected[filePath] = fileContents + } + + fileResponse, err := s.GetGitFiles(context.TODO(), filesRequest) + assert.Nil(t, err) + assert.Equal(t, fileResponse.GetMap(), expected) + + // do the same request again to use the cache + // we only allow LsFiles to be called once in the mock + fileResponse, err = s.GetGitFiles(context.TODO(), filesRequest) + assert.Nil(t, err) + assert.Equal(t, expected, fileResponse.GetMap()) + cacheMocks.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ + ExternalSets: 1, + ExternalGets: 2, + }) +} + +func Test_getRepoSanitizerRegex(t *testing.T) { + r := getRepoSanitizerRegex("/tmp/_argocd-repo") + msg := r.ReplaceAllString("error message containing /tmp/_argocd-repo/SENSITIVE and other stuff", "") + assert.Equal(t, "error message containing and other stuff", msg) + msg = r.ReplaceAllString("error message containing /tmp/_argocd-repo/SENSITIVE/with/trailing/path and other stuff", "") + assert.Equal(t, "error message containing /with/trailing/path and other stuff", msg) +} + +func TestGetRefs_CacheWithLockDisabled(t *testing.T) { + // Test that when the lock is disabled the default behavior still works correctly + // Also shows the current issue with the git requests due to cache misses + dir := t.TempDir() + initGitRepo(t, newGitRepoOptions{ + path: dir, + createPath: false, + remote: "", + addEmptyCommit: true, + }) + // Test in-memory and redis + cacheMocks := newCacheMocksWithOpts(1*time.Minute, 1*time.Minute, 0) + t.Cleanup(cacheMocks.mockCache.StopRedisCallback) + var wg sync.WaitGroup + numberOfCallers := 10 + for i := 0; i < numberOfCallers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + client, err := git.NewClient(fmt.Sprintf("file://%s", dir), git.NopCreds{}, true, false, "", git.WithCache(cacheMocks.cache, true)) + require.NoError(t, err) + refs, err := client.LsRefs() + assert.NoError(t, err) + assert.NotNil(t, refs) + assert.NotEqual(t, 0, len(refs.Branches), "Expected branches to be populated") + assert.NotEmpty(t, refs.Branches[0]) + }() + } + wg.Wait() + // Unlock should not have been called + cacheMocks.mockCache.AssertNumberOfCalls(t, "UnlockGitReferences", 0) + // Lock should not have been called + cacheMocks.mockCache.AssertNumberOfCalls(t, "TryLockGitRefCache", 0) +} + +func TestGetRefs_CacheDisabled(t *testing.T) { + // Test that default get refs with cache disabled does not call GetOrLockGitReferences + dir := t.TempDir() + initGitRepo(t, newGitRepoOptions{ + path: dir, + createPath: false, + remote: "", + addEmptyCommit: true, + }) + cacheMocks := newCacheMocks() + t.Cleanup(cacheMocks.mockCache.StopRedisCallback) + client, err := git.NewClient(fmt.Sprintf("file://%s", dir), git.NopCreds{}, true, false, "", git.WithCache(cacheMocks.cache, false)) + require.NoError(t, err) + refs, err := client.LsRefs() + assert.NoError(t, err) + assert.NotNil(t, refs) + assert.NotEqual(t, 0, len(refs.Branches), "Expected branches to be populated") + assert.NotEmpty(t, refs.Branches[0]) + // Unlock should not have been called + cacheMocks.mockCache.AssertNumberOfCalls(t, "UnlockGitReferences", 0) + cacheMocks.mockCache.AssertNumberOfCalls(t, "GetOrLockGitReferences", 0) +} + +func TestGetRefs_CacheWithLock(t *testing.T) { + // Test that there is only one call to SetGitReferences for the same repo which is done after the ls-remote + dir := t.TempDir() + initGitRepo(t, newGitRepoOptions{ + path: dir, + createPath: false, + remote: "", + addEmptyCommit: true, + }) + cacheMocks := newCacheMocks() + t.Cleanup(cacheMocks.mockCache.StopRedisCallback) + var wg sync.WaitGroup + numberOfCallers := 10 + for i := 0; i < numberOfCallers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + client, err := git.NewClient(fmt.Sprintf("file://%s", dir), git.NopCreds{}, true, false, "", git.WithCache(cacheMocks.cache, true)) + require.NoError(t, err) + refs, err := client.LsRefs() + assert.NoError(t, err) + assert.NotNil(t, refs) + assert.NotEqual(t, 0, len(refs.Branches), "Expected branches to be populated") + assert.NotEmpty(t, refs.Branches[0]) + }() + } + wg.Wait() + // Unlock should not have been called + cacheMocks.mockCache.AssertNumberOfCalls(t, "UnlockGitReferences", 0) + cacheMocks.mockCache.AssertNumberOfCalls(t, "GetOrLockGitReferences", 0) +} + +func TestGetRefs_CacheUnlockedOnUpdateFailed(t *testing.T) { + // Worst case the ttl on the lock expires and the lock is removed + // however if the holder of the lock fails to update the cache the caller should remove the lock + // to allow other callers to attempt to update the cache as quickly as possible + dir := t.TempDir() + initGitRepo(t, newGitRepoOptions{ + path: dir, + createPath: false, + remote: "", + addEmptyCommit: true, + }) + cacheMocks := newCacheMocks() + t.Cleanup(cacheMocks.mockCache.StopRedisCallback) + repoUrl := fmt.Sprintf("file://%s", dir) + client, err := git.NewClient(repoUrl, git.NopCreds{}, true, false, "", git.WithCache(cacheMocks.cache, true)) + require.NoError(t, err) + refs, err := client.LsRefs() + assert.NoError(t, err) + assert.NotNil(t, refs) + assert.NotEqual(t, 0, len(refs.Branches), "Expected branches to be populated") + assert.NotEmpty(t, refs.Branches[0]) + var output [][2]string + err = cacheMocks.cacheutilCache.GetItem(fmt.Sprintf("git-refs|%s|%s", repoUrl, common.CacheVersion), &output) + assert.Error(t, err, "Should be a cache miss") + assert.Equal(t, 0, len(output), "Expected cache to be empty for key") + cacheMocks.mockCache.AssertNumberOfCalls(t, "UnlockGitReferences", 0) + cacheMocks.mockCache.AssertNumberOfCalls(t, "GetOrLockGitReferences", 0) +} + +func TestGetRefs_CacheLockTryLockGitRefCacheError(t *testing.T) { + // Worst case the ttl on the lock expires and the lock is removed + // however if the holder of the lock fails to update the cache the caller should remove the lock + // to allow other callers to attempt to update the cache as quickly as possible + dir := t.TempDir() + initGitRepo(t, newGitRepoOptions{ + path: dir, + createPath: false, + remote: "", + addEmptyCommit: true, + }) + cacheMocks := newCacheMocks() + t.Cleanup(cacheMocks.mockCache.StopRedisCallback) + repoUrl := fmt.Sprintf("file://%s", dir) + // buf := bytes.Buffer{} + // log.SetOutput(&buf) + client, err := git.NewClient(repoUrl, git.NopCreds{}, true, false, "", git.WithCache(cacheMocks.cache, true)) + require.NoError(t, err) + refs, err := client.LsRefs() + assert.NoError(t, err) + assert.NotNil(t, refs) +} + +func TestGetRevisionChartDetails(t *testing.T) { + t.Run("Test revision semvar", func(t *testing.T) { + root := t.TempDir() + service := newService(t, root) + _, err := service.GetRevisionChartDetails(context.Background(), &apiclient.RepoServerRevisionChartDetailsRequest{ + Repo: &v1alpha1.Repository{ + Repo: fmt.Sprintf("file://%s", root), + Name: "test-repo-name", + Type: "helm", + }, + Name: "test-name", + Revision: "test-revision", + }) + assert.ErrorContains(t, err, "invalid revision") + }) + + t.Run("Test GetRevisionChartDetails", func(t *testing.T) { + root := t.TempDir() + service := newService(t, root) + repoUrl := fmt.Sprintf("file://%s", root) + err := service.cache.SetRevisionChartDetails(repoUrl, "my-chart", "1.1.0", &argoappv1.ChartDetails{ + Description: "test-description", + Home: "test-home", + Maintainers: []string{"test-maintainer"}, + }) + assert.NoError(t, err) + chartDetails, err := service.GetRevisionChartDetails(context.Background(), &apiclient.RepoServerRevisionChartDetailsRequest{ + Repo: &v1alpha1.Repository{ + Repo: fmt.Sprintf("file://%s", root), + Name: "test-repo-name", + Type: "helm", + }, + Name: "my-chart", + Revision: "1.1.0", + }) + assert.NoError(t, err) + assert.Equal(t, "test-description", chartDetails.Description) + assert.Equal(t, "test-home", chartDetails.Home) + assert.Equal(t, []string{"test-maintainer"}, chartDetails.Maintainers) + }) +} diff --git a/reposerver/repository/testdata/git-files-dirs/app/bar/.hidden/.keep b/reposerver/repository/testdata/git-files-dirs/app/bar/.hidden/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/reposerver/repository/testdata/git-files-dirs/app/bar/.keep b/reposerver/repository/testdata/git-files-dirs/app/bar/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/reposerver/repository/testdata/git-files-dirs/app/foo/bar/config.yaml b/reposerver/repository/testdata/git-files-dirs/app/foo/bar/config.yaml new file mode 100644 index 0000000000000..433c6b94dc0cf --- /dev/null +++ b/reposerver/repository/testdata/git-files-dirs/app/foo/bar/config.yaml @@ -0,0 +1,2 @@ +version: 4.2.1a +name: fooaaaa \ No newline at end of file diff --git a/reposerver/repository/testdata/git-files-dirs/app/foo/config.yaml b/reposerver/repository/testdata/git-files-dirs/app/foo/config.yaml new file mode 100644 index 0000000000000..096b2f1a35b87 --- /dev/null +++ b/reposerver/repository/testdata/git-files-dirs/app/foo/config.yaml @@ -0,0 +1,2 @@ +version: 4.2.1 +name: foo \ No newline at end of file diff --git a/reposerver/repository/testdata/git-files-dirs/config.yaml b/reposerver/repository/testdata/git-files-dirs/config.yaml new file mode 100644 index 0000000000000..5c0feb3c03c8a --- /dev/null +++ b/reposerver/repository/testdata/git-files-dirs/config.yaml @@ -0,0 +1,2 @@ +version: 4.2.1.5 +name: foooooo \ No newline at end of file diff --git a/reposerver/repository/testdata/git-files-dirs/somedir/config.yaml b/reposerver/repository/testdata/git-files-dirs/somedir/config.yaml new file mode 100644 index 0000000000000..c66361bb14a46 --- /dev/null +++ b/reposerver/repository/testdata/git-files-dirs/somedir/config.yaml @@ -0,0 +1,2 @@ +version: 2.1 +name: fo \ No newline at end of file diff --git a/reposerver/repository/testdata/helm-with-dependencies-alias/Chart.yaml b/reposerver/repository/testdata/helm-with-dependencies-alias/Chart.yaml new file mode 100644 index 0000000000000..8a38d551070c7 --- /dev/null +++ b/reposerver/repository/testdata/helm-with-dependencies-alias/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: helm-with-dependencies-alias +version: v1.0.0 +dependencies: + - name: helm + repository: "alias:custom-repo-alias" + version: v1.0.0 diff --git a/reposerver/repository/testdata/helm-with-dependencies/Chart.yaml b/reposerver/repository/testdata/helm-with-dependencies/Chart.yaml new file mode 100644 index 0000000000000..4b616a6eb8494 --- /dev/null +++ b/reposerver/repository/testdata/helm-with-dependencies/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: helm-with-dependencies +version: v1.0.0 +dependencies: + - name: helm + repository: "@custom-repo" + version: v1.0.0 diff --git a/reposerver/repository/testdata/invalid-metadata/bad.yaml b/reposerver/repository/testdata/invalid-metadata/bad.yaml new file mode 100644 index 0000000000000..83f48a40dc334 --- /dev/null +++ b/reposerver/repository/testdata/invalid-metadata/bad.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-map-annotation + annotations: + invalid: true +stringData: + foo: bar +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-map-label + labels: + invalid: true +stringData: + foo: bar diff --git a/reposerver/repository/testdata/nil-metadata-accessors/nil-metadata-accessors.yaml b/reposerver/repository/testdata/nil-metadata-accessors/nil-metadata-accessors.yaml new file mode 100644 index 0000000000000..53979de769c01 --- /dev/null +++ b/reposerver/repository/testdata/nil-metadata-accessors/nil-metadata-accessors.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-map + annotations: + labels: +stringData: + foo: bar diff --git a/reposerver/repository/testdata/oci-dependencies/Chart.yaml b/reposerver/repository/testdata/oci-dependencies/Chart.yaml index 3b39781ed6257..1674ae17c5516 100644 --- a/reposerver/repository/testdata/oci-dependencies/Chart.yaml +++ b/reposerver/repository/testdata/oci-dependencies/Chart.yaml @@ -2,5 +2,5 @@ name: my-chart version: 1.1.0 dependencies: - name: my-dependency - repository: oci://example.com + repository: oci://example.com/myrepo version: '*' \ No newline at end of file diff --git a/reposerver/repository/testdata/values-files/dir/values.yaml b/reposerver/repository/testdata/values-files/dir/values.yaml new file mode 100644 index 0000000000000..55262d50ff71c --- /dev/null +++ b/reposerver/repository/testdata/values-files/dir/values.yaml @@ -0,0 +1 @@ +values: yaml diff --git a/reposerver/repository/types.go b/reposerver/repository/types.go new file mode 100644 index 0000000000000..3e45a5bf3a1cf --- /dev/null +++ b/reposerver/repository/types.go @@ -0,0 +1,14 @@ +package repository + +// Chart see: https://helm.sh/docs/topics/charts/ for more details +type Chart struct { + Description string `yaml:"description,omitempty"` + Home string `yaml:"home,omitempty"` + Maintainers []Maintainer `yaml:"maintainers,omitempty"` +} + +type Maintainer struct { + Name string `yaml:"name,omitempty"` + Email string `yaml:"email,omitempty"` + Url string `yaml:"url,omitempty"` +} diff --git a/reposerver/server.go b/reposerver/server.go index 9576604751dfc..e1d611801c3ec 100644 --- a/reposerver/server.go +++ b/reposerver/server.go @@ -90,7 +90,7 @@ func NewServer(metricsServer *metrics.MetricsServer, cache *reposervercache.Cach grpc.MaxSendMsgSize(apiclient.MaxGRPCMessageSize), grpc.KeepaliveEnforcementPolicy( keepalive.EnforcementPolicy{ - MinTime: common.GRPCKeepAliveEnforcementMinimum, + MinTime: common.GetGRPCKeepAliveEnforcementMinimum(), }, ), } @@ -102,7 +102,7 @@ func NewServer(metricsServer *metrics.MetricsServer, cache *reposervercache.Cach } repoService := repository.NewService(metricsServer, cache, initConstants, argo.NewResourceTracking(), gitCredsStore, filepath.Join(os.TempDir(), "_argocd-repo")) if err := repoService.Init(); err != nil { - return nil, err + return nil, fmt.Errorf("failed to initialize the repo service: %w", err) } return &ArgoCDRepoServer{ diff --git a/resource_customizations/apps.kruise.io/AdvancedCronJob/health.lua b/resource_customizations/apps.kruise.io/AdvancedCronJob/health.lua new file mode 100644 index 0000000000000..1e68d862722e1 --- /dev/null +++ b/resource_customizations/apps.kruise.io/AdvancedCronJob/health.lua @@ -0,0 +1,36 @@ +hs = { status = "Progressing", message = "AdvancedCronJobs has active jobs" } +-- Extract lastScheduleTime and convert to time objects +lastScheduleTime = nil + +if obj.status.lastScheduleTime ~= nil then + local year, month, day, hour, min, sec = string.match(obj.status.lastScheduleTime, "(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z") + lastScheduleTime = os.time({year=year, month=month, day=day, hour=hour, min=min, sec=sec}) +end + + +if lastScheduleTime == nil and obj.spec.paused == true then + hs.status = "Suspended" + hs.message = "AdvancedCronJob is Paused" + return hs +end + +-- AdvancedCronJobs are progressing if they have any object in the "active" state +if obj.status.active ~= nil and #obj.status.active > 0 then + hs.status = "Progressing" + hs.message = "AdvancedCronJobs has active jobs" + return hs +end +-- AdvancedCronJobs are Degraded if they don't have lastScheduleTime +if lastScheduleTime == nil then + hs.status = "Degraded" + hs.message = "AdvancedCronJobs has not run successfully" + return hs +end +-- AdvancedCronJobs are healthy if they have lastScheduleTime +if lastScheduleTime ~= nil then + hs.status = "Healthy" + hs.message = "AdvancedCronJobs has run successfully" + return hs +end + +return hs diff --git a/resource_customizations/apps.kruise.io/AdvancedCronJob/health_test.yaml b/resource_customizations/apps.kruise.io/AdvancedCronJob/health_test.yaml new file mode 100644 index 0000000000000..939c701955abb --- /dev/null +++ b/resource_customizations/apps.kruise.io/AdvancedCronJob/health_test.yaml @@ -0,0 +1,17 @@ +tests: + - healthStatus: + status: Healthy + message: AdvancedCronJobs has run successfully + inputPath: testdata/lastScheduleTime.yaml + - healthStatus: + status: Degraded + message: AdvancedCronJobs has not run successfully + inputPath: testdata/notScheduled.yaml + - healthStatus: + status: Progressing + message: AdvancedCronJobs has active jobs + inputPath: testdata/activeJobs.yaml + - healthStatus: + status: Suspended + message: AdvancedCronJob is Paused + inputPath: testdata/suspended.yaml diff --git a/resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/activeJobs.yaml b/resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/activeJobs.yaml new file mode 100644 index 0000000000000..5748143874d5e --- /dev/null +++ b/resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/activeJobs.yaml @@ -0,0 +1,30 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: AdvancedCronJob +metadata: + name: acj-test +spec: + schedule: "*/1 * * * *" + template: + broadcastJobTemplate: + spec: + template: + spec: + containers: + - name: pi + image: perl + command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] + restartPolicy: Never + completionPolicy: + type: Always + ttlSecondsAfterFinished: 30 + +status: + active: + - apiVersion: apps.kruise.io/v1alpha1 + kind: BroadcastJob + name: acj-test-1694882400 + namespace: default + resourceVersion: '4012' + uid: 2b08a429-a43b-4382-8e5d-3db0c72b5b13 + lastScheduleTime: '2023-09-16T16:40:00Z' + type: BroadcastJob diff --git a/resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/lastScheduleTime.yaml b/resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/lastScheduleTime.yaml new file mode 100644 index 0000000000000..bf48bdba777dc --- /dev/null +++ b/resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/lastScheduleTime.yaml @@ -0,0 +1,23 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: AdvancedCronJob +metadata: + name: acj-test +spec: + schedule: "*/1 * * * *" + template: + broadcastJobTemplate: + spec: + template: + spec: + containers: + - name: pi + image: perl + command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] + restartPolicy: Never + completionPolicy: + type: Always + ttlSecondsAfterFinished: 30 + +status: + lastScheduleTime: "2023-09-16T16:29:00Z" + type: BroadcastJob diff --git a/resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/notScheduled.yaml b/resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/notScheduled.yaml new file mode 100644 index 0000000000000..cc8a9dd436d80 --- /dev/null +++ b/resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/notScheduled.yaml @@ -0,0 +1,22 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: AdvancedCronJob +metadata: + name: acj-test +spec: + schedule: "*/1 * * * *" + template: + broadcastJobTemplate: + spec: + template: + spec: + containers: + - name: pi + image: perl + command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] + restartPolicy: Never + completionPolicy: + type: Always + ttlSecondsAfterFinished: 30 + +status: + lastScheduleTime: null diff --git a/resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/suspended.yaml b/resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/suspended.yaml new file mode 100644 index 0000000000000..dc79f1b41218b --- /dev/null +++ b/resource_customizations/apps.kruise.io/AdvancedCronJob/testdata/suspended.yaml @@ -0,0 +1,23 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: AdvancedCronJob +metadata: + name: acj-test +spec: + schedule: "*/1 * * * *" + template: + broadcastJobTemplate: + spec: + template: + spec: + containers: + - name: pi + image: perl + command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] + restartPolicy: Never + completionPolicy: + type: Always + ttlSecondsAfterFinished: 30 + paused: true + +status: + type: BroadcastJob diff --git a/resource_customizations/apps.kruise.io/BroadcastJob/health.lua b/resource_customizations/apps.kruise.io/BroadcastJob/health.lua new file mode 100644 index 0000000000000..3b20ca8849975 --- /dev/null +++ b/resource_customizations/apps.kruise.io/BroadcastJob/health.lua @@ -0,0 +1,32 @@ +hs={ status= "Progressing", message= "BroadcastJob is still running" } + +if obj.status ~= nil then + +-- BroadcastJob are healthy if desired number and succeeded number is equal + if obj.status.desired == obj.status.succeeded and obj.status.phase == "completed" then + hs.status = "Healthy" + hs.message = "BroadcastJob is completed successfully" + return hs + end +-- BroadcastJob are progressing if active is not equal to 0 + if obj.status.active ~= 0 and obj.status.phase == "running" then + hs.status = "Progressing" + hs.message = "BroadcastJob is still running" + return hs + end +-- BroadcastJob are progressing if failed is not equal to 0 + if obj.status.failed ~= 0 and obj.status.phase == "failed" then + hs.status = "Degraded" + hs.message = "BroadcastJob failed" + return hs + end + + if obj.status.phase == "paused" and obj.spec.paused == true then + hs.status = "Suspended" + hs.message = "BroadcastJob is Paused" + return hs + end + +end + +return hs diff --git a/resource_customizations/apps.kruise.io/BroadcastJob/health_test.yaml b/resource_customizations/apps.kruise.io/BroadcastJob/health_test.yaml new file mode 100644 index 0000000000000..e3e16e22bfeef --- /dev/null +++ b/resource_customizations/apps.kruise.io/BroadcastJob/health_test.yaml @@ -0,0 +1,17 @@ +tests: + - healthStatus: + status: Healthy + message: "BroadcastJob is completed successfully" + inputPath: testdata/succeeded.yaml + - healthStatus: + status: Degraded + message: "BroadcastJob failed" + inputPath: testdata/failed.yaml + - healthStatus: + status: Progressing + message: "BroadcastJob is still running" + inputPath: testdata/running.yaml + - healthStatus: + status: Suspended + message: "BroadcastJob is Paused" + inputPath: testdata/suspended.yaml diff --git a/resource_customizations/apps.kruise.io/BroadcastJob/testdata/failed.yaml b/resource_customizations/apps.kruise.io/BroadcastJob/testdata/failed.yaml new file mode 100644 index 0000000000000..88b85cae28189 --- /dev/null +++ b/resource_customizations/apps.kruise.io/BroadcastJob/testdata/failed.yaml @@ -0,0 +1,31 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: BroadcastJob +metadata: + name: failed-job +spec: + template: + spec: + containers: + - name: guestbook + image: openkruise/guestbook:v3 + command: ["exit", "1"] # a dummy command to fail + restartPolicy: Never + completionPolicy: + type: Always + ttlSecondsAfterFinished: 60 # the job will be deleted after 60 seconds + +status: + active: 0 + completionTime: '2023-09-17T14:31:38Z' + conditions: + - lastProbeTime: '2023-09-17T14:31:38Z' + lastTransitionTime: '2023-09-17T14:31:38Z' + message: failure policy is FailurePolicyTypeFailFast and failed pod is found + reason: Failed + status: 'True' + type: Failed + desired: 1 + failed: 1 + phase: failed + startTime: '2023-09-17T14:31:32Z' + succeeded: 0 diff --git a/resource_customizations/apps.kruise.io/BroadcastJob/testdata/running.yaml b/resource_customizations/apps.kruise.io/BroadcastJob/testdata/running.yaml new file mode 100644 index 0000000000000..f679fa3ee0d50 --- /dev/null +++ b/resource_customizations/apps.kruise.io/BroadcastJob/testdata/running.yaml @@ -0,0 +1,22 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: BroadcastJob +metadata: + name: download-image +spec: + template: + spec: + containers: + - name: guestbook + image: openkruise/guestbook:v3 + command: ["echo", "started"] # a dummy command to do nothing + restartPolicy: Never + completionPolicy: + type: Always + ttlSecondsAfterFinished: 60 # the job will be deleted after 60 seconds +status: + active: 1 + desired: 1 + failed: 0 + phase: running + startTime: '2023-09-17T14:43:30Z' + succeeded: 0 diff --git a/resource_customizations/apps.kruise.io/BroadcastJob/testdata/succeeded.yaml b/resource_customizations/apps.kruise.io/BroadcastJob/testdata/succeeded.yaml new file mode 100644 index 0000000000000..61746b20cd907 --- /dev/null +++ b/resource_customizations/apps.kruise.io/BroadcastJob/testdata/succeeded.yaml @@ -0,0 +1,31 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: BroadcastJob +metadata: + name: download-image +spec: + template: + spec: + containers: + - name: guestbook + image: openkruise/guestbook:v3 + command: ["echo", "started"] # a dummy command to do nothing + restartPolicy: Never + completionPolicy: + type: Always + ttlSecondsAfterFinished: 60 # the job will be deleted after 60 seconds +status: + active: 0 + completionTime: '2023-09-17T14:35:14Z' + conditions: + - lastProbeTime: '2023-09-17T14:35:14Z' + lastTransitionTime: '2023-09-17T14:35:14Z' + message: Job completed, 1 pods succeeded, 0 pods failed + reason: Complete + status: 'True' + type: Complete + desired: 1 + failed: 0 + phase: completed + startTime: '2023-09-17T14:35:07Z' + succeeded: 1 + diff --git a/resource_customizations/apps.kruise.io/BroadcastJob/testdata/suspended.yaml b/resource_customizations/apps.kruise.io/BroadcastJob/testdata/suspended.yaml new file mode 100644 index 0000000000000..60a9b587b8ec0 --- /dev/null +++ b/resource_customizations/apps.kruise.io/BroadcastJob/testdata/suspended.yaml @@ -0,0 +1,31 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: BroadcastJob +metadata: + name: download-image +spec: + template: + spec: + containers: + - name: guestbook + image: openkruise/guestbook:v3 + command: ["echo", "started"] # a dummy command to do nothing + restartPolicy: Never + paused: true + completionPolicy: + type: Always + ttlSecondsAfterFinished: 60 # the job will be deleted after 60 seconds +status: + active: 0 + completionTime: '2023-09-17T14:35:14Z' + conditions: + - lastProbeTime: '2023-09-17T14:35:14Z' + lastTransitionTime: '2023-09-17T14:35:14Z' + message: Job completed, 1 pods succeeded, 0 pods failed + reason: Complete + status: 'True' + type: Complete + desired: 1 + failed: 0 + phase: paused + startTime: '2023-09-17T14:35:07Z' + succeeded: 0 diff --git a/resource_customizations/apps.kruise.io/CloneSet/health.lua b/resource_customizations/apps.kruise.io/CloneSet/health.lua new file mode 100644 index 0000000000000..197ab7573dfe8 --- /dev/null +++ b/resource_customizations/apps.kruise.io/CloneSet/health.lua @@ -0,0 +1,33 @@ +hs={ status = "Progressing", message = "Waiting for initialization" } + +if obj.status ~= nil then + + if obj.metadata.generation == obj.status.observedGeneration then + + if obj.spec.updateStrategy.paused == true or not obj.status.updatedAvailableReplicas then + hs.status = "Suspended" + hs.message = "Cloneset is paused" + return hs + elseif obj.spec.updateStrategy.partition ~= 0 and obj.metadata.generation > 1 then + if obj.status.updatedReplicas >= obj.status.expectedUpdatedReplicas then + hs.status = "Suspended" + hs.message = "Cloneset needs manual intervention" + return hs + end + + elseif obj.status.updatedAvailableReplicas == obj.status.replicas then + hs.status = "Healthy" + hs.message = "All Cloneset workloads are ready and updated" + return hs + + else + if obj.status.updatedAvailableReplicas ~= obj.status.replicas then + hs.status = "Degraded" + hs.message = "Some replicas are not ready or available" + return hs + end + end + end +end + +return hs diff --git a/resource_customizations/apps.kruise.io/CloneSet/health_test.yaml b/resource_customizations/apps.kruise.io/CloneSet/health_test.yaml new file mode 100644 index 0000000000000..e740eca850778 --- /dev/null +++ b/resource_customizations/apps.kruise.io/CloneSet/health_test.yaml @@ -0,0 +1,21 @@ +tests: + - healthStatus: + status: Healthy + message: "All Cloneset workloads are ready and updated" + inputPath: testdata/healthy.yaml + - healthStatus: + status: Degraded + message: "Some replicas are not ready or available" + inputPath: testdata/degraded.yaml + - healthStatus: + status: Progressing + message: "Waiting for initialization" + inputPath: testdata/unknown.yaml + - healthStatus: + status: Suspended + message: "Cloneset is paused" + inputpath: testdata/suspended.yaml + - healthStatus: + status: Suspended + message: "Cloneset needs manual intervention" + inputpath: testdata/partition_suspended.yaml diff --git a/resource_customizations/apps.kruise.io/CloneSet/testdata/degraded.yaml b/resource_customizations/apps.kruise.io/CloneSet/testdata/degraded.yaml new file mode 100644 index 0000000000000..36e9a0d537c85 --- /dev/null +++ b/resource_customizations/apps.kruise.io/CloneSet/testdata/degraded.yaml @@ -0,0 +1,35 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: CloneSet +metadata: + name: cloneset-test + namespace: kruise + generation: 1 + labels: + app: sample +spec: + replicas: 2 + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - name: nginx + image: nginx:alpine + updateStrategy: + paused: false + +status: + observedGeneration: 1 + replicas: 2 + updatedReadyReplicas: 1 + updatedAvailableReplicas: 1 + conditions: + - lastTransitionTime: "2021-09-21T22:35:31Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: 'True' + type: FailedScale diff --git a/resource_customizations/apps.kruise.io/CloneSet/testdata/healthy.yaml b/resource_customizations/apps.kruise.io/CloneSet/testdata/healthy.yaml new file mode 100644 index 0000000000000..8a1935381e04e --- /dev/null +++ b/resource_customizations/apps.kruise.io/CloneSet/testdata/healthy.yaml @@ -0,0 +1,36 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: CloneSet +metadata: + name: cloneset-test + namespace: kruise + generation: 1 + labels: + app: sample +spec: + replicas: 1 + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - name: nginx + image: nginx:alpine + updateStrategy: + paused: false + + +status: + observedGeneration: 1 + replicas: 2 + updatedReadyReplicas: 2 + updatedAvailableReplicas: 2 + conditions: + - lastTransitionTime: "2021-09-21T22:35:31Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: 'True' + type: FailedScale diff --git a/resource_customizations/apps.kruise.io/CloneSet/testdata/partition_suspended.yaml b/resource_customizations/apps.kruise.io/CloneSet/testdata/partition_suspended.yaml new file mode 100644 index 0000000000000..674c5226b3072 --- /dev/null +++ b/resource_customizations/apps.kruise.io/CloneSet/testdata/partition_suspended.yaml @@ -0,0 +1,31 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: CloneSet +metadata: + name: cloneset-test + namespace: kruise + generation: 2 + labels: + app: sample +spec: + replicas: 5 + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - name: nginx + image: nginx:alpine + updateStrategy: + partition: 3 + +status: + observedGeneration: 2 + replicas: 5 + expectedUpdatedReplicas: 2 + updatedReadyReplicas: 1 + updatedAvailableReplicas: 1 + updatedReplicas: 3 diff --git a/resource_customizations/apps.kruise.io/CloneSet/testdata/suspended.yaml b/resource_customizations/apps.kruise.io/CloneSet/testdata/suspended.yaml new file mode 100644 index 0000000000000..9edfaca6a5149 --- /dev/null +++ b/resource_customizations/apps.kruise.io/CloneSet/testdata/suspended.yaml @@ -0,0 +1,35 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: CloneSet +metadata: + name: cloneset-test + namespace: kruise + generation: 2 + labels: + app: sample +spec: + replicas: 1 + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - name: nginx + image: nginx:alpine + updateStrategy: + paused: true + +status: + observedGeneration: 2 + replicas: 2 + updatedReadyReplicas: 2 + updatedAvailableReplicas: 2 + conditions: + - lastTransitionTime: "2021-09-21T22:35:31Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: 'True' + type: FailedScale diff --git a/resource_customizations/apps.kruise.io/CloneSet/testdata/unknown.yaml b/resource_customizations/apps.kruise.io/CloneSet/testdata/unknown.yaml new file mode 100644 index 0000000000000..c1ccdb22fc76e --- /dev/null +++ b/resource_customizations/apps.kruise.io/CloneSet/testdata/unknown.yaml @@ -0,0 +1,5 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: CloneSet +metadata: + name: cloneset-test + namespace: kruise diff --git a/resource_customizations/apps.kruise.io/DaemonSet/health.lua b/resource_customizations/apps.kruise.io/DaemonSet/health.lua new file mode 100644 index 0000000000000..7705bcc3325e5 --- /dev/null +++ b/resource_customizations/apps.kruise.io/DaemonSet/health.lua @@ -0,0 +1,35 @@ +hs={ status = "Progressing", message = "Waiting for initialization" } + +if obj.status ~= nil then + + if obj.metadata.generation == obj.status.observedGeneration then + + if obj.spec.updateStrategy.rollingUpdate.paused == true or not obj.status.updatedNumberScheduled then + hs.status = "Suspended" + hs.message = "Daemonset is paused" + return hs + elseif obj.spec.updateStrategy.rollingUpdate.partition ~= 0 and obj.metadata.generation > 1 then + if obj.status.updatedNumberScheduled > (obj.status.desiredNumberScheduled - obj.spec.updateStrategy.rollingUpdate.partition) then + hs.status = "Suspended" + hs.message = "Daemonset needs manual intervention" + return hs + end + + elseif (obj.status.updatedNumberScheduled == obj.status.desiredNumberScheduled) and (obj.status.numberAvailable == obj.status.desiredNumberScheduled) then + hs.status = "Healthy" + hs.message = "All Daemonset workloads are ready and updated" + return hs + + else + if (obj.status.updatedNumberScheduled == obj.status.desiredNumberScheduled) and (obj.status.numberUnavailable == obj.status.desiredNumberScheduled) then + hs.status = "Degraded" + hs.message = "Some pods are not ready or available" + return hs + end + end + + end + +end + +return hs diff --git a/resource_customizations/apps.kruise.io/DaemonSet/health_test.yaml b/resource_customizations/apps.kruise.io/DaemonSet/health_test.yaml new file mode 100644 index 0000000000000..0a8c8292672f3 --- /dev/null +++ b/resource_customizations/apps.kruise.io/DaemonSet/health_test.yaml @@ -0,0 +1,21 @@ +tests: + - healthStatus: + status: Healthy + message: "All Daemonset workloads are ready and updated" + inputPath: testdata/healthy.yaml + - healthStatus: + status: Degraded + message: "Some pods are not ready or available" + inputPath: testdata/degraded.yaml + - healthStatus: + status: Progressing + message: "Waiting for initialization" + inputPath: testdata/unknown.yaml + - healthStatus: + status: Suspended + message: "Daemonset is paused" + inputPath: testdata/suspended.yaml + - healthStatus: + status: Suspended + message: "Daemonset needs manual intervention" + inputPath: testdata/partition_suspended.yaml diff --git a/resource_customizations/apps.kruise.io/DaemonSet/testdata/degraded.yaml b/resource_customizations/apps.kruise.io/DaemonSet/testdata/degraded.yaml new file mode 100644 index 0000000000000..ed8cbc0b4699e --- /dev/null +++ b/resource_customizations/apps.kruise.io/DaemonSet/testdata/degraded.yaml @@ -0,0 +1,34 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: DaemonSet +metadata: + name: daemonset-test + namespace: kruise + generation: 1 + labels: + app: sample +spec: + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - name: nginx + image: nginx:alpine + updateStrategy: + rollingUpdate: + partition: 0 + paused: false + +status: + currentNumberScheduled: 1 + daemonSetHash: 5dffcdfcd7 + desiredNumberScheduled: 1 + numberUnavailable: 1 + numberMisscheduled: 0 + numberReady: 0 + observedGeneration: 1 + updatedNumberScheduled: 1 diff --git a/resource_customizations/apps.kruise.io/DaemonSet/testdata/healthy.yaml b/resource_customizations/apps.kruise.io/DaemonSet/testdata/healthy.yaml new file mode 100644 index 0000000000000..6224ebf35e164 --- /dev/null +++ b/resource_customizations/apps.kruise.io/DaemonSet/testdata/healthy.yaml @@ -0,0 +1,34 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: DaemonSet +metadata: + name: daemonset-test + namespace: kruise + generation: 1 + labels: + app: sample +spec: + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - name: nginx + image: nginx:alpine + updateStrategy: + rollingUpdate: + partition: 0 + paused: false + +status: + currentNumberScheduled: 1 + daemonSetHash: 5dffcdfcd7 + desiredNumberScheduled: 1 + numberAvailable: 1 + numberMisscheduled: 0 + numberReady: 1 + observedGeneration: 1 + updatedNumberScheduled: 1 diff --git a/resource_customizations/apps.kruise.io/DaemonSet/testdata/partition_suspended.yaml b/resource_customizations/apps.kruise.io/DaemonSet/testdata/partition_suspended.yaml new file mode 100644 index 0000000000000..4c0819cdc8703 --- /dev/null +++ b/resource_customizations/apps.kruise.io/DaemonSet/testdata/partition_suspended.yaml @@ -0,0 +1,33 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: DaemonSet +metadata: + name: daemonset-test + namespace: kruise + generation: 6 + labels: + app: sample +spec: + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - name: nginx + image: nginx:alpine + updateStrategy: + rollingUpdate: + partition: 4 + +status: + currentNumberScheduled: 1 + daemonSetHash: 5f8cdcdc65 + desiredNumberScheduled: 10 + numberAvailable: 10 + numberMisscheduled: 0 + numberReady: 10 + observedGeneration: 6 + updatedNumberScheduled: 7 diff --git a/resource_customizations/apps.kruise.io/DaemonSet/testdata/suspended.yaml b/resource_customizations/apps.kruise.io/DaemonSet/testdata/suspended.yaml new file mode 100644 index 0000000000000..fb705f5578176 --- /dev/null +++ b/resource_customizations/apps.kruise.io/DaemonSet/testdata/suspended.yaml @@ -0,0 +1,33 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: DaemonSet +metadata: + name: daemonset-test + namespace: kruise + generation: 1 + labels: + app: sample +spec: + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - name: nginx + image: nginx:alpine + updateStrategy: + rollingUpdate: + paused: true + +status: + currentNumberScheduled: 1 + daemonSetHash: 5dffcdfcd7 + desiredNumberScheduled: 1 + numberAvailable: 1 + numberMisscheduled: 0 + numberReady: 1 + observedGeneration: 1 + updatedNumberScheduled: 1 diff --git a/resource_customizations/apps.kruise.io/DaemonSet/testdata/unknown.yaml b/resource_customizations/apps.kruise.io/DaemonSet/testdata/unknown.yaml new file mode 100644 index 0000000000000..aa5791c52bc6c --- /dev/null +++ b/resource_customizations/apps.kruise.io/DaemonSet/testdata/unknown.yaml @@ -0,0 +1,5 @@ +apiVersion: apps.kruise.io/v1alpha1 +kind: DaemonSet +metadata: + name: daemonset-test + namespace: kruise diff --git a/resource_customizations/apps.kruise.io/StatefulSet/health.lua b/resource_customizations/apps.kruise.io/StatefulSet/health.lua new file mode 100644 index 0000000000000..47340452db2dc --- /dev/null +++ b/resource_customizations/apps.kruise.io/StatefulSet/health.lua @@ -0,0 +1,35 @@ +hs={ status = "Progressing", message = "Waiting for initialization" } + +if obj.status ~= nil then + + if obj.metadata.generation == obj.status.observedGeneration then + + if obj.spec.updateStrategy.rollingUpdate.paused == true or not obj.status.updatedAvailableReplicas then + hs.status = "Suspended" + hs.message = "Statefulset is paused" + return hs + elseif obj.spec.updateStrategy.rollingUpdate.partition ~= 0 and obj.metadata.generation > 1 then + if obj.status.updatedReplicas > (obj.status.replicas - obj.spec.updateStrategy.rollingUpdate.partition) then + hs.status = "Suspended" + hs.message = "Statefulset needs manual intervention" + return hs + end + + elseif obj.status.updatedAvailableReplicas == obj.status.replicas then + hs.status = "Healthy" + hs.message = "All Statefulset workloads are ready and updated" + return hs + + else + if obj.status.updatedAvailableReplicas ~= obj.status.replicas then + hs.status = "Degraded" + hs.message = "Some replicas are not ready or available" + return hs + end + end + + end + +end + +return hs diff --git a/resource_customizations/apps.kruise.io/StatefulSet/health_test.yaml b/resource_customizations/apps.kruise.io/StatefulSet/health_test.yaml new file mode 100644 index 0000000000000..6672b9f46d4f4 --- /dev/null +++ b/resource_customizations/apps.kruise.io/StatefulSet/health_test.yaml @@ -0,0 +1,21 @@ +tests: + - healthStatus: + status: Healthy + message: "All Statefulset workloads are ready and updated" + inputPath: testdata/healthy.yaml + - healthStatus: + status: Degraded + message: "Some replicas are not ready or available" + inputPath: testdata/degraded.yaml + - healthStatus: + status: Progressing + message: "Waiting for initialization" + inputPath: testdata/unknown.yaml + - healthStatus: + status: Suspended + message: "Statefulset is paused" + inputPath: testdata/suspended.yaml + - healthStatus: + status: Suspended + message: "Statefulset needs manual intervention" + inputPath: testdata/partition_suspended.yaml diff --git a/resource_customizations/apps.kruise.io/StatefulSet/testdata/degraded.yaml b/resource_customizations/apps.kruise.io/StatefulSet/testdata/degraded.yaml new file mode 100644 index 0000000000000..88e58914940fc --- /dev/null +++ b/resource_customizations/apps.kruise.io/StatefulSet/testdata/degraded.yaml @@ -0,0 +1,42 @@ +apiVersion: apps.kruise.io/v1beta1 +kind: StatefulSet +metadata: + name: statefulset-test + namespace: kruise + generation: 5 + labels: + app: sample +spec: + replicas: 2 + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - name: nginx + image: nginx:alpine + updateStrategy: + rollingUpdate: + maxUnavailable: 1 + minReadySeconds: 0 + paused: false + partition: 0 + podUpdatePolicy: ReCreate + type: RollingUpdate + +status: + observedGeneration: 5 + replicas: 2 + updatedAvailableReplicas: 1 + updatedReadyReplicas: 1 + conditions: + - lastTransitionTime: "2021-09-21T22:35:31Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: 'True' + type: FailedCreatePod + diff --git a/resource_customizations/apps.kruise.io/StatefulSet/testdata/healthy.yaml b/resource_customizations/apps.kruise.io/StatefulSet/testdata/healthy.yaml new file mode 100644 index 0000000000000..793de25d3da1c --- /dev/null +++ b/resource_customizations/apps.kruise.io/StatefulSet/testdata/healthy.yaml @@ -0,0 +1,41 @@ +apiVersion: apps.kruise.io/v1beta1 +kind: StatefulSet +metadata: + name: statefulset-test + namespace: kruise + generation: 2 + labels: + app: sample +spec: + replicas: 2 + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - name: nginx + image: nginx:alpine + updateStrategy: + rollingUpdate: + maxUnavailable: 1 + minReadySeconds: 0 + paused: false + partition: 0 + podUpdatePolicy: ReCreate + type: RollingUpdate + +status: + observedGeneration: 2 + replicas: 2 + updatedAvailableReplicas: 2 + updatedReadyReplicas: 2 + conditions: + - lastTransitionTime: "2021-09-21T22:35:31Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: 'False' + type: FailedCreatePod diff --git a/resource_customizations/apps.kruise.io/StatefulSet/testdata/partition_suspended.yaml b/resource_customizations/apps.kruise.io/StatefulSet/testdata/partition_suspended.yaml new file mode 100644 index 0000000000000..b09a7726bf5d7 --- /dev/null +++ b/resource_customizations/apps.kruise.io/StatefulSet/testdata/partition_suspended.yaml @@ -0,0 +1,36 @@ +apiVersion: apps.kruise.io/v1beta1 +kind: StatefulSet +metadata: + name: statefulset-test + namespace: kruise + generation: 3 + labels: + app: sample +spec: + replicas: 10 + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - image: nginx:mainline + updateStrategy: + rollingUpdate: + partition: 4 + +status: + availableReplicas: 10 + currentReplicas: 4 + currentRevision: statefulset-test-d4d4fb5bd + labelSelector: app=sample + observedGeneration: 3 + readyReplicas: 10 + replicas: 10 + updateRevision: statefulset-test-56dfb978d4 + updatedAvailableReplicas: 7 + updatedReadyReplicas: 7 + updatedReplicas: 7 diff --git a/resource_customizations/apps.kruise.io/StatefulSet/testdata/suspended.yaml b/resource_customizations/apps.kruise.io/StatefulSet/testdata/suspended.yaml new file mode 100644 index 0000000000000..42dae9cf5e322 --- /dev/null +++ b/resource_customizations/apps.kruise.io/StatefulSet/testdata/suspended.yaml @@ -0,0 +1,36 @@ +apiVersion: apps.kruise.io/v1beta1 +kind: StatefulSet +metadata: + name: statefulset-test + namespace: kruise + generation: 2 + labels: + app: sample +spec: + replicas: 2 + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - name: nginx + image: nginx:alpine + updateStrategy: + rollingUpdate: + paused: true + +status: + observedGeneration: 2 + replicas: 2 + updatedAvailableReplicas: 2 + updatedReadyReplicas: 2 + conditions: + - lastTransitionTime: "2021-09-21T22:35:31Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: 'False' + type: FailedCreatePod diff --git a/resource_customizations/apps.kruise.io/StatefulSet/testdata/unknown.yaml b/resource_customizations/apps.kruise.io/StatefulSet/testdata/unknown.yaml new file mode 100644 index 0000000000000..67d28de6dae64 --- /dev/null +++ b/resource_customizations/apps.kruise.io/StatefulSet/testdata/unknown.yaml @@ -0,0 +1,5 @@ +apiVersion: apps.kruise.io/v1beta1 +kind: StatefulSet +metadata: + name: statefulset-test + namespace: kruise diff --git a/resource_customizations/apps.openshift.io/DeploymentConfig/health.lua b/resource_customizations/apps.openshift.io/DeploymentConfig/health.lua index 3be1879532158..0e3faafe4730b 100644 --- a/resource_customizations/apps.openshift.io/DeploymentConfig/health.lua +++ b/resource_customizations/apps.openshift.io/DeploymentConfig/health.lua @@ -1,7 +1,7 @@ -health_check = {} +local health_check = {} if obj.status ~= nil then if obj.status.conditions ~= nil and obj.status.replicas ~= nil then - numTrue = 0 + local numTrue = 0 for i, condition in pairs(obj.status.conditions) do if (condition.type == "Available" or (condition.type == "Progressing" and condition.reason == "NewReplicationControllerAvailable")) and condition.status == "True" then numTrue = numTrue + 1 diff --git a/resource_customizations/apps/DaemonSet/actions/discovery.lua b/resource_customizations/apps/DaemonSet/actions/discovery.lua index 49557377a831b..dc7f10431bc70 100644 --- a/resource_customizations/apps/DaemonSet/actions/discovery.lua +++ b/resource_customizations/apps/DaemonSet/actions/discovery.lua @@ -1,3 +1,3 @@ -actions = {} +local actions = {} actions["restart"] = {} return actions diff --git a/resource_customizations/apps/DaemonSet/actions/testdata/daemonset-restarted.yaml b/resource_customizations/apps/DaemonSet/actions/testdata/daemonset-restarted.yaml index dc06ffda1cd21..e6ff0363952ef 100644 --- a/resource_customizations/apps/DaemonSet/actions/testdata/daemonset-restarted.yaml +++ b/resource_customizations/apps/DaemonSet/actions/testdata/daemonset-restarted.yaml @@ -25,7 +25,7 @@ spec: name: daemonset spec: containers: - - image: k8s.gcr.io/nginx-slim:0.8 + - image: registry.k8s.io/nginx-slim:0.8 imagePullPolicy: IfNotPresent name: nginx resources: {} diff --git a/resource_customizations/apps/DaemonSet/actions/testdata/daemonset.yaml b/resource_customizations/apps/DaemonSet/actions/testdata/daemonset.yaml index 4d58cbd7cfdf5..d293188455b87 100644 --- a/resource_customizations/apps/DaemonSet/actions/testdata/daemonset.yaml +++ b/resource_customizations/apps/DaemonSet/actions/testdata/daemonset.yaml @@ -23,7 +23,7 @@ spec: name: daemonset spec: containers: - - image: k8s.gcr.io/nginx-slim:0.8 + - image: registry.k8s.io/nginx-slim:0.8 imagePullPolicy: IfNotPresent name: nginx resources: {} diff --git a/resource_customizations/apps/Deployment/actions/discovery.lua b/resource_customizations/apps/Deployment/actions/discovery.lua index 52b52fba81a06..d090d587769c7 100644 --- a/resource_customizations/apps/Deployment/actions/discovery.lua +++ b/resource_customizations/apps/Deployment/actions/discovery.lua @@ -1,4 +1,4 @@ -actions = {} +local actions = {} actions["restart"] = {} local paused = false diff --git a/resource_customizations/apps/Deployment/actions/testdata/deployment-pause.yaml b/resource_customizations/apps/Deployment/actions/testdata/deployment-pause.yaml index 38cb1faf8498f..3ddbbe3e5cef2 100644 --- a/resource_customizations/apps/Deployment/actions/testdata/deployment-pause.yaml +++ b/resource_customizations/apps/Deployment/actions/testdata/deployment-pause.yaml @@ -4,6 +4,8 @@ metadata: annotations: deployment.kubernetes.io/revision: "1" creationTimestamp: "2021-09-21T22:35:20Z" + name: nginx-deploy + namespace: default generation: 2 spec: paused: true diff --git a/resource_customizations/apps/Deployment/actions/testdata/deployment-resume.yaml b/resource_customizations/apps/Deployment/actions/testdata/deployment-resume.yaml index ea8d3b14de51d..8ccb8dcab0802 100644 --- a/resource_customizations/apps/Deployment/actions/testdata/deployment-resume.yaml +++ b/resource_customizations/apps/Deployment/actions/testdata/deployment-resume.yaml @@ -5,6 +5,8 @@ metadata: deployment.kubernetes.io/revision: "1" creationTimestamp: "2021-09-21T22:35:20Z" generation: 3 + name: nginx-deploy + namespace: default spec: progressDeadlineSeconds: 600 replicas: 3 diff --git a/resource_customizations/apps/StatefulSet/actions/discovery.lua b/resource_customizations/apps/StatefulSet/actions/discovery.lua index 49557377a831b..dc7f10431bc70 100644 --- a/resource_customizations/apps/StatefulSet/actions/discovery.lua +++ b/resource_customizations/apps/StatefulSet/actions/discovery.lua @@ -1,3 +1,3 @@ -actions = {} +local actions = {} actions["restart"] = {} return actions diff --git a/resource_customizations/apps/StatefulSet/actions/testdata/statefulset-restarted.yaml b/resource_customizations/apps/StatefulSet/actions/testdata/statefulset-restarted.yaml index 5a3ba88edc6d5..44f902f4fa22b 100644 --- a/resource_customizations/apps/StatefulSet/actions/testdata/statefulset-restarted.yaml +++ b/resource_customizations/apps/StatefulSet/actions/testdata/statefulset-restarted.yaml @@ -26,7 +26,7 @@ spec: kubectl.kubernetes.io/restartedAt: "0001-01-01T00:00:00Z" spec: containers: - - image: k8s.gcr.io/nginx-slim:0.8 + - image: registry.k8s.io/nginx-slim:0.8 imagePullPolicy: IfNotPresent name: nginx resources: {} diff --git a/resource_customizations/apps/StatefulSet/actions/testdata/statefulset.yaml b/resource_customizations/apps/StatefulSet/actions/testdata/statefulset.yaml index 2dfd355e624fe..7804814a65e6c 100644 --- a/resource_customizations/apps/StatefulSet/actions/testdata/statefulset.yaml +++ b/resource_customizations/apps/StatefulSet/actions/testdata/statefulset.yaml @@ -24,7 +24,7 @@ spec: app: statefulset spec: containers: - - image: k8s.gcr.io/nginx-slim:0.8 + - image: registry.k8s.io/nginx-slim:0.8 imagePullPolicy: IfNotPresent name: nginx resources: {} diff --git a/resource_customizations/argoproj.io/AnalysisRun/actions/discovery.lua b/resource_customizations/argoproj.io/AnalysisRun/actions/discovery.lua index b98eaf0febd87..b5c2ef8ffaa73 100644 --- a/resource_customizations/argoproj.io/AnalysisRun/actions/discovery.lua +++ b/resource_customizations/argoproj.io/AnalysisRun/actions/discovery.lua @@ -1,4 +1,4 @@ -actions = {} +local actions = {} actions["terminate"] = {["disabled"] = (obj.spec.terminate or obj.status.phase == "Successful" or obj.status.phase == "Failed" or diff --git a/resource_customizations/argoproj.io/AnalysisRun/health.lua b/resource_customizations/argoproj.io/AnalysisRun/health.lua index ea2f53a98902d..9329b138e37af 100644 --- a/resource_customizations/argoproj.io/AnalysisRun/health.lua +++ b/resource_customizations/argoproj.io/AnalysisRun/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} function messageOrDefault(field, default) if field ~= nil then diff --git a/resource_customizations/argoproj.io/ApplicationSet/health.lua b/resource_customizations/argoproj.io/ApplicationSet/health.lua index 3292ebb67e51f..3a0cd19bc6202 100644 --- a/resource_customizations/argoproj.io/ApplicationSet/health.lua +++ b/resource_customizations/argoproj.io/ApplicationSet/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then diff --git a/resource_customizations/argoproj.io/CronWorkflow/actions/action_test.yaml b/resource_customizations/argoproj.io/CronWorkflow/actions/action_test.yaml new file mode 100644 index 0000000000000..7fce1c3b36cf6 --- /dev/null +++ b/resource_customizations/argoproj.io/CronWorkflow/actions/action_test.yaml @@ -0,0 +1,7 @@ +actionTests: +- action: create-workflow + inputPath: testdata/cronworkflow.yaml + expectedOutputPath: testdata/workflow.yaml +- action: create-workflow + inputPath: testdata/cronworkflow-without-label.yaml + expectedOutputPath: testdata/workflow-without-label.yaml diff --git a/resource_customizations/argoproj.io/CronWorkflow/actions/create-workflow/action.lua b/resource_customizations/argoproj.io/CronWorkflow/actions/create-workflow/action.lua new file mode 100644 index 0000000000000..c41da30887ebe --- /dev/null +++ b/resource_customizations/argoproj.io/CronWorkflow/actions/create-workflow/action.lua @@ -0,0 +1,82 @@ +local os = require("os") + +-- This action constructs a Workflow resource from a CronWorkflow resource, to enable creating a CronWorkflow instance +-- on demand. +-- It returns an array with a single member - a table with the operation to perform (create) and the Workflow resource. +-- It mimics the output of "argo submit --from=CronWorkflow/" command, declaratively. + +-- This code is written to mimic what the Argo Workflows API server does to create a Workflow from a CronWorkflow. +-- https://github.com/argoproj/argo-workflows/blob/873a58de7dd9dad76d5577b8c4294a58b52849b8/workflow/common/convert.go#L12 + +-- Deep-copying an object is a ChatGPT generated code. +-- Since empty tables are treated as empty arrays, the resulting k8s resource might be invalid (arrays instead of maps). +-- So empty tables are not cloned to the target object. +function deepCopy(object) + local lookup_table = {} + local function _copy(obj) + if type(obj) ~= "table" then + return obj + elseif lookup_table[obj] then + return lookup_table[obj] + elseif next(obj) == nil then + return nil + else + local new_table = {} + lookup_table[obj] = new_table + for key, value in pairs(obj) do + new_table[_copy(key)] = _copy(value) + end + return setmetatable(new_table, getmetatable(obj)) + end + end + return _copy(object) +end + +local workflow = {} +workflow.apiVersion = "argoproj.io/v1alpha1" +workflow.kind = "Workflow" + +workflow.metadata = {} +workflow.metadata.name = obj.metadata.name .. "-" ..os.date("!%Y%m%d%H%M") +workflow.metadata.namespace = obj.metadata.namespace +workflow.metadata.labels = {} +workflow.metadata.annotations = {} +if (obj.spec.workflowMetadata ~= nil) then + if (obj.spec.workflowMetadata.labels ~= nil) then + workflow.metadata.labels = deepCopy(obj.spec.workflowMetadata.labels) + end + if (obj.spec.workflowMetadata.annotations ~= nil) then + workflow.metadata.annotations = deepCopy(obj.spec.workflowMetadata.annotations) + end +end +workflow.metadata.labels["workflows.argoproj.io/cron-workflow"] = obj.metadata.name +if (obj.metadata.labels ~= nil and obj.metadata.labels["workflows.argoproj.io/controller-instanceid"] ~= nil) then + workflow.metadata.labels["workflows.argoproj.io/controller-instanceid"] = obj.metadata.labels["workflows.argoproj.io/controller-instanceid"] +end +workflow.metadata.annotations["workflows.argoproj.io/scheduled-time"] = os.date("!%Y-%m-%dT%d:%H:%MZ") + +workflow.finalizers = {} +-- add all finalizers from obj.spec.workflowMetadata.finalizers +if (obj.spec.workflowMetadata ~= nil and obj.spec.workflowMetadata.finalizers ~= nil) then + for i, finalizer in ipairs(obj.spec.workflowMetadata.finalizers) do + workflow.finalizers[i] = finalizer + end +end + +local ownerRef = {} +ownerRef.apiVersion = obj.apiVersion +ownerRef.kind = obj.kind +ownerRef.name = obj.metadata.name +ownerRef.uid = obj.metadata.uid +workflow.metadata.ownerReferences = {} +workflow.metadata.ownerReferences[1] = ownerRef + +workflow.spec = deepCopy(obj.spec.workflowSpec) + +local impactedResource = {} +impactedResource.operation = "create" +impactedResource.resource = workflow +local result = {} +result[1] = impactedResource + +return result diff --git a/resource_customizations/argoproj.io/CronWorkflow/actions/discovery.lua b/resource_customizations/argoproj.io/CronWorkflow/actions/discovery.lua new file mode 100644 index 0000000000000..9a76d96643218 --- /dev/null +++ b/resource_customizations/argoproj.io/CronWorkflow/actions/discovery.lua @@ -0,0 +1,6 @@ +local actions = {} +actions["create-workflow"] = { + ["iconClass"] = "fa fa-fw fa-play", + ["displayName"] = "Create Workflow" +} +return actions \ No newline at end of file diff --git a/resource_customizations/argoproj.io/CronWorkflow/actions/testdata/cronworkflow-without-label.yaml b/resource_customizations/argoproj.io/CronWorkflow/actions/testdata/cronworkflow-without-label.yaml new file mode 100644 index 0000000000000..a9f9e2ed8d5c0 --- /dev/null +++ b/resource_customizations/argoproj.io/CronWorkflow/actions/testdata/cronworkflow-without-label.yaml @@ -0,0 +1,31 @@ +apiVersion: argoproj.io/v1alpha1 +kind: CronWorkflow +metadata: + annotations: + cronworkflows.argoproj.io/last-used-schedule: CRON_TZ=America/Los_Angeles * * * * * + name: hello-world + namespace: default +spec: + concurrencyPolicy: Replace + failedJobsHistoryLimit: 4 + schedule: '* * * * *' + startingDeadlineSeconds: 0 + successfulJobsHistoryLimit: 4 + suspend: true + timezone: America/Los_Angeles + workflowSpec: + entrypoint: whalesay + templates: + - container: + args: + - "\U0001F553 hello world. Scheduled on: {{workflow.scheduledTime}}" + command: + - cowsay + image: 'docker/whalesay:latest' + name: whalesay + workflowMetadata: + labels: + example: test + annotations: + another-example: another-test + finalizers: [test-finalizer] diff --git a/resource_customizations/argoproj.io/CronWorkflow/actions/testdata/cronworkflow.yaml b/resource_customizations/argoproj.io/CronWorkflow/actions/testdata/cronworkflow.yaml new file mode 100644 index 0000000000000..2a2c7d1807db4 --- /dev/null +++ b/resource_customizations/argoproj.io/CronWorkflow/actions/testdata/cronworkflow.yaml @@ -0,0 +1,34 @@ +apiVersion: argoproj.io/v1alpha1 +kind: CronWorkflow +metadata: + annotations: + cronworkflows.argoproj.io/last-used-schedule: CRON_TZ=America/Los_Angeles * * * * * + labels: + workflows.argoproj.io/controller-instanceid: test-instance + app.kubernetes.io/instance: test + name: hello-world + namespace: default +spec: + concurrencyPolicy: Replace + failedJobsHistoryLimit: 4 + schedule: '* * * * *' + startingDeadlineSeconds: 0 + successfulJobsHistoryLimit: 4 + suspend: true + timezone: America/Los_Angeles + workflowSpec: + entrypoint: whalesay + templates: + - container: + args: + - "\U0001F553 hello world. Scheduled on: {{workflow.scheduledTime}}" + command: + - cowsay + image: 'docker/whalesay:latest' + name: whalesay + workflowMetadata: + labels: + example: test + annotations: + another-example: another-test + finalizers: [test-finalizer] diff --git a/resource_customizations/argoproj.io/CronWorkflow/actions/testdata/workflow-without-label.yaml b/resource_customizations/argoproj.io/CronWorkflow/actions/testdata/workflow-without-label.yaml new file mode 100644 index 0000000000000..1d20bc0d72a6a --- /dev/null +++ b/resource_customizations/argoproj.io/CronWorkflow/actions/testdata/workflow-without-label.yaml @@ -0,0 +1,26 @@ +- k8sOperation: create + unstructuredObj: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + annotations: + another-example: another-test + labels: + example: test + name: hello-world-202306221736 + namespace: default + ownerReferences: + - apiVersion: argoproj.io/v1alpha1 + kind: CronWorkflow + name: hello-world + finalizers: [test-finalizer] + spec: + entrypoint: whalesay + templates: + - container: + args: + - "\U0001F553 hello world. Scheduled on: {{workflow.scheduledTime}}" + command: + - cowsay + image: 'docker/whalesay:latest' + name: whalesay diff --git a/resource_customizations/argoproj.io/CronWorkflow/actions/testdata/workflow.yaml b/resource_customizations/argoproj.io/CronWorkflow/actions/testdata/workflow.yaml new file mode 100644 index 0000000000000..9f231dbb5c5b3 --- /dev/null +++ b/resource_customizations/argoproj.io/CronWorkflow/actions/testdata/workflow.yaml @@ -0,0 +1,28 @@ +- k8sOperation: create + unstructuredObj: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + annotations: + another-example: another-test + labels: + workflows.argoproj.io/cron-workflow: hello-world + workflows.argoproj.io/controller-instanceid: test-instance + example: test + name: hello-world-202306221736 + namespace: default + ownerReferences: + - apiVersion: argoproj.io/v1alpha1 + kind: CronWorkflow + name: hello-world + finalizers: [test-finalizer] + spec: + entrypoint: whalesay + templates: + - container: + args: + - "\U0001F553 hello world. Scheduled on: {{workflow.scheduledTime}}" + command: + - cowsay + image: 'docker/whalesay:latest' + name: whalesay diff --git a/resource_customizations/argoproj.io/CronWorkflow/health.lua b/resource_customizations/argoproj.io/CronWorkflow/health.lua index d1b736743504a..0a441df071a32 100644 --- a/resource_customizations/argoproj.io/CronWorkflow/health.lua +++ b/resource_customizations/argoproj.io/CronWorkflow/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then diff --git a/resource_customizations/argoproj.io/EventBus/health.lua b/resource_customizations/argoproj.io/EventBus/health.lua new file mode 100644 index 0000000000000..9e97eab7dfdf2 --- /dev/null +++ b/resource_customizations/argoproj.io/EventBus/health.lua @@ -0,0 +1,21 @@ +local hs={ status = "Progressing", message = "Waiting for initialization" } + +if obj.status ~= nil then + if obj.status.conditions ~= nil then + for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Deployed" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message or condition.reason + return hs + end + if condition.type == "Deployed" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message or condition.reason + return hs + end + end + end +end + + +return hs diff --git a/resource_customizations/argoproj.io/EventBus/health_test.yaml b/resource_customizations/argoproj.io/EventBus/health_test.yaml new file mode 100644 index 0000000000000..7babe7fc7f9a3 --- /dev/null +++ b/resource_customizations/argoproj.io/EventBus/health_test.yaml @@ -0,0 +1,9 @@ +tests: + - healthStatus: + status: Healthy + message: "JetStream is deployed" + inputPath: testdata/healthy.yaml + - healthStatus: + status: Degraded + message: 'failed to get jetstream version, err: unsupported version "iwillfail", supported versions: "2.9.5,latest"' + inputPath: testdata/degraded.yaml \ No newline at end of file diff --git a/resource_customizations/argoproj.io/EventBus/testdata/degraded.yaml b/resource_customizations/argoproj.io/EventBus/testdata/degraded.yaml new file mode 100644 index 0000000000000..4ffd1cf6a9623 --- /dev/null +++ b/resource_customizations/argoproj.io/EventBus/testdata/degraded.yaml @@ -0,0 +1,21 @@ +apiVersion: argoproj.io/v1alpha1 +kind: EventBus +metadata: + name: test + namespace: eventbus-test +spec: + jetstream: + replicas: 3 + version: iwillfail +status: + conditions: + - lastTransitionTime: null + status: 'True' + type: Configured + - lastTransitionTime: null + message: >- + failed to get jetstream version, err: unsupported version "iwillfail", + supported versions: "2.9.5,latest" + reason: JetStreamStatefulSetFailed + status: 'False' + type: Deployed \ No newline at end of file diff --git a/resource_customizations/argoproj.io/EventBus/testdata/healthy.yaml b/resource_customizations/argoproj.io/EventBus/testdata/healthy.yaml new file mode 100644 index 0000000000000..8db1455a14d8e --- /dev/null +++ b/resource_customizations/argoproj.io/EventBus/testdata/healthy.yaml @@ -0,0 +1,19 @@ +apiVersion: argoproj.io/v1alpha1 +kind: EventBus +metadata: + name: test + namespace: eventbus-test +spec: + jetstream: + replicas: 3 + version: latest +status: + conditions: + - lastTransitionTime: '2022-12-30T11:44:15Z' + status: 'True' + type: Configured + - lastTransitionTime: '2022-12-30T11:44:15Z' + message: JetStream is deployed + reason: Succeeded + status: 'True' + type: Deployed \ No newline at end of file diff --git a/resource_customizations/argoproj.io/Experiment/health.lua b/resource_customizations/argoproj.io/Experiment/health.lua index 1587f684cdef1..ad11caf5ebea7 100644 --- a/resource_customizations/argoproj.io/Experiment/health.lua +++ b/resource_customizations/argoproj.io/Experiment/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.phase == "Pending" then hs.status = "Progressing" diff --git a/resource_customizations/argoproj.io/Rollout/actions/discovery.lua b/resource_customizations/argoproj.io/Rollout/actions/discovery.lua index 93d09d978ed12..86a5307e0023b 100644 --- a/resource_customizations/argoproj.io/Rollout/actions/discovery.lua +++ b/resource_customizations/argoproj.io/Rollout/actions/discovery.lua @@ -1,4 +1,4 @@ -actions = {} +local actions = {} actions["restart"] = {["disabled"] = false} local paused = false @@ -9,13 +9,13 @@ elseif obj.spec.paused ~= nil then end actions["resume"] = {["disabled"] = not(paused)} -fullyPromoted = obj.status.currentPodHash == obj.status.stableRS +local fullyPromoted = obj.status.currentPodHash == obj.status.stableRS actions["abort"] = {["disabled"] = fullyPromoted or obj.status.abort} actions["retry"] = {["disabled"] = fullyPromoted or not(obj.status.abort)} actions["promote-full"] = {["disabled"] = true} if obj.status ~= nil and not(fullyPromoted) then - generation = tonumber(obj.status.observedGeneration) + local generation = tonumber(obj.status.observedGeneration) if generation == nil or generation > obj.metadata.generation then -- rollouts v0.9 - full promotion only supported for canary actions["promote-full"] = {["disabled"] = obj.spec.strategy.blueGreen ~= nil} diff --git a/resource_customizations/argoproj.io/Rollout/actions/promote-full/action.lua b/resource_customizations/argoproj.io/Rollout/actions/promote-full/action.lua index 431c3f9279da0..90805995b1381 100644 --- a/resource_customizations/argoproj.io/Rollout/actions/promote-full/action.lua +++ b/resource_customizations/argoproj.io/Rollout/actions/promote-full/action.lua @@ -1,5 +1,5 @@ if obj.status ~= nil then - generation = tonumber(obj.status.observedGeneration) + local generation = tonumber(obj.status.observedGeneration) if generation == nil or generation > obj.metadata.generation then -- rollouts v0.9 and below obj.status.abort = nil diff --git a/resource_customizations/argoproj.io/Rollout/health.lua b/resource_customizations/argoproj.io/Rollout/health.lua index 8d352a3ad7f30..ec733a87c106c 100644 --- a/resource_customizations/argoproj.io/Rollout/health.lua +++ b/resource_customizations/argoproj.io/Rollout/health.lua @@ -1,9 +1,9 @@ function checkReplicasStatus(obj) - hs = {} - desiredReplicas = getNumberValueOrDefault(obj.spec.replicas, 1) + local hs = {} + local desiredReplicas = getNumberValueOrDefault(obj.spec.replicas, 1) statusReplicas = getNumberValueOrDefault(obj.status.replicas, 0) updatedReplicas = getNumberValueOrDefault(obj.status.updatedReplicas, 0) - availableReplicas = getNumberValueOrDefault(obj.status.availableReplicas, 0) + local availableReplicas = getNumberValueOrDefault(obj.status.availableReplicas, 0) if updatedReplicas < desiredReplicas then hs.status = "Progressing" @@ -38,7 +38,7 @@ function getNumberValueOrDefault(field, default) end function checkPaused(obj) - hs = {} + local hs = {} hs.status = "Suspended" hs.message = "Rollout is paused" if obj.status.pauseConditions ~= nil and table.getn(obj.status.pauseConditions) > 0 then @@ -73,12 +73,12 @@ function isWorkloadGenerationObserved(obj) -- rollout is v1.0 or earlier return true end - workloadGen = tonumber(obj.metadata.annotations["rollout.argoproj.io/workload-generation"]) - observedWorkloadGen = tonumber(obj.status.workloadObservedGeneration) + local workloadGen = tonumber(obj.metadata.annotations["rollout.argoproj.io/workload-generation"]) + local observedWorkloadGen = tonumber(obj.status.workloadObservedGeneration) return workloadGen == observedWorkloadGen end -hs = {} +local hs = {} if not isGenerationObserved(obj) or not isWorkloadGenerationObserved(obj) then hs.status = "Progressing" hs.message = "Waiting for rollout spec update to be observed" @@ -115,7 +115,7 @@ for _, condition in ipairs(obj.status.conditions) do end end -isPaused = checkPaused(obj) +local isPaused = checkPaused(obj) if isPaused ~= nil then return isPaused end @@ -132,7 +132,7 @@ if replicasHS ~= nil then end -stableRS = getStableRS(obj) +local stableRS = getStableRS(obj) if obj.spec.strategy.blueGreen ~= nil then if obj.status.blueGreen == nil or obj.status.blueGreen.activeSelector ~= obj.status.currentPodHash then diff --git a/resource_customizations/argoproj.io/WorkflowTemplate/actions/action_test.yaml b/resource_customizations/argoproj.io/WorkflowTemplate/actions/action_test.yaml new file mode 100644 index 0000000000000..db503fe0b6aae --- /dev/null +++ b/resource_customizations/argoproj.io/WorkflowTemplate/actions/action_test.yaml @@ -0,0 +1,4 @@ +actionTests: +- action: create-workflow + inputPath: testdata/workflowtemplate.yaml + expectedOutputPath: testdata/workflow.yaml diff --git a/resource_customizations/argoproj.io/WorkflowTemplate/actions/create-workflow/action.lua b/resource_customizations/argoproj.io/WorkflowTemplate/actions/create-workflow/action.lua new file mode 100644 index 0000000000000..1eaffc4771ad5 --- /dev/null +++ b/resource_customizations/argoproj.io/WorkflowTemplate/actions/create-workflow/action.lua @@ -0,0 +1,39 @@ +local os = require("os") + +-- This action constructs a Workflow resource from a WorkflowTemplate resource, to enable creating a WorkflowTemplate instance +-- on demand. +-- It returns an array with a single member - a table with the operation to perform (create) and the Workflow resource. +-- It mimics the output of "argo submit --from=workflowtemplate/" command, declaratively. + +-- This code is written to mimic what the Argo Workflows API server does to create a Workflow from a WorkflowTemplate. +-- https://github.com/argoproj/argo-workflows/blob/873a58de7dd9dad76d5577b8c4294a58b52849b8/workflow/common/convert.go#L34 + +local workflow = {} +workflow.apiVersion = "argoproj.io/v1alpha1" +workflow.kind = "Workflow" + +workflow.metadata = {} +workflow.metadata.name = obj.metadata.name .. "-" ..os.date("!%Y%m%d%H%M") +workflow.metadata.namespace = obj.metadata.namespace +workflow.metadata.labels = {} +workflow.metadata.labels["workflows.argoproj.io/workflow-template"] = obj.metadata.name + +workflow.spec = {} +workflow.spec.workflowTemplateRef = {} +workflow.spec.workflowTemplateRef.name = obj.metadata.name + +local ownerRef = {} +ownerRef.apiVersion = obj.apiVersion +ownerRef.kind = obj.kind +ownerRef.name = obj.metadata.name +ownerRef.uid = obj.metadata.uid +workflow.metadata.ownerReferences = {} +workflow.metadata.ownerReferences[1] = ownerRef + +local impactedResource = {} +impactedResource.operation = "create" +impactedResource.resource = workflow +local result = {} +result[1] = impactedResource + +return result diff --git a/resource_customizations/argoproj.io/WorkflowTemplate/actions/discovery.lua b/resource_customizations/argoproj.io/WorkflowTemplate/actions/discovery.lua new file mode 100644 index 0000000000000..9a76d96643218 --- /dev/null +++ b/resource_customizations/argoproj.io/WorkflowTemplate/actions/discovery.lua @@ -0,0 +1,6 @@ +local actions = {} +actions["create-workflow"] = { + ["iconClass"] = "fa fa-fw fa-play", + ["displayName"] = "Create Workflow" +} +return actions \ No newline at end of file diff --git a/resource_customizations/argoproj.io/WorkflowTemplate/actions/testdata/workflow.yaml b/resource_customizations/argoproj.io/WorkflowTemplate/actions/testdata/workflow.yaml new file mode 100644 index 0000000000000..46063bee03397 --- /dev/null +++ b/resource_customizations/argoproj.io/WorkflowTemplate/actions/testdata/workflow.yaml @@ -0,0 +1,16 @@ +- k8sOperation: create + unstructuredObj: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + labels: + workflows.argoproj.io/workflow-template: workflow-template-submittable + name: workflow-template-submittable-202306221735 + namespace: default + ownerReferences: + - apiVersion: argoproj.io/v1alpha1 + kind: WorkflowTemplate + name: workflow-template-submittable + spec: + workflowTemplateRef: + name: workflow-template-submittable diff --git a/resource_customizations/argoproj.io/WorkflowTemplate/actions/testdata/workflowtemplate.yaml b/resource_customizations/argoproj.io/WorkflowTemplate/actions/testdata/workflowtemplate.yaml new file mode 100644 index 0000000000000..5b7d2319e9c9e --- /dev/null +++ b/resource_customizations/argoproj.io/WorkflowTemplate/actions/testdata/workflowtemplate.yaml @@ -0,0 +1,24 @@ +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + labels: + app.kubernetes.io/instance: test + name: workflow-template-submittable + namespace: default +spec: + arguments: + parameters: + - name: message + value: hello world + entrypoint: whalesay-template + templates: + - container: + args: + - '{{inputs.parameters.message}}' + command: + - cowsay + image: docker/whalesay + inputs: + parameters: + - name: message + name: whalesay-template diff --git a/resource_customizations/batch/CronJob/actions/action_test.yaml b/resource_customizations/batch/CronJob/actions/action_test.yaml new file mode 100644 index 0000000000000..a9b5320db5721 --- /dev/null +++ b/resource_customizations/batch/CronJob/actions/action_test.yaml @@ -0,0 +1,4 @@ +actionTests: +- action: create-job + inputPath: testdata/cronjob.yaml + expectedOutputPath: testdata/job.yaml diff --git a/resource_customizations/batch/CronJob/actions/create-job/action.lua b/resource_customizations/batch/CronJob/actions/create-job/action.lua new file mode 100644 index 0000000000000..a6f3253a5b757 --- /dev/null +++ b/resource_customizations/batch/CronJob/actions/create-job/action.lua @@ -0,0 +1,64 @@ +local os = require("os") + +-- This action constructs a Job resource from a CronJob resource, to enable creating a CronJob instance on demand. +-- It returns an array with a single member - a table with the operation to perform (create) and the Job resource. +-- It mimics the output of "kubectl create job --from=" command, declaratively. + +-- Deep-copying an object is a ChatGPT generated code. +-- Since empty tables are treated as empty arrays, the resulting k8s resource might be invalid (arrays instead of maps). +-- So empty tables are not cloned to the target object. +function deepCopy(object) + local lookup_table = {} + local function _copy(obj) + if type(obj) ~= "table" then + return obj + elseif lookup_table[obj] then + return lookup_table[obj] + elseif next(obj) == nil then + return nil + else + local new_table = {} + lookup_table[obj] = new_table + for key, value in pairs(obj) do + new_table[_copy(key)] = _copy(value) + end + return setmetatable(new_table, getmetatable(obj)) + end + end + return _copy(object) +end + +local job = {} +job.apiVersion = "batch/v1" +job.kind = "Job" + +job.metadata = deepCopy(obj.spec.jobTemplate.metadata) +if job.metadata == nil then + job.metadata = {} +end +job.metadata.name = obj.metadata.name .. "-" ..os.date("!%Y%m%d%H%M") +job.metadata.namespace = obj.metadata.namespace +if job.metadata.annotations == nil then + job.metadata.annotations = {} +end +job.metadata.annotations['cronjob.kubernetes.io/instantiate'] = "manual" + +local ownerRef = {} +ownerRef.apiVersion = obj.apiVersion +ownerRef.kind = obj.kind +ownerRef.name = obj.metadata.name +ownerRef.uid = obj.metadata.uid +ownerRef.blockOwnerDeletion = true +ownerRef.controller = true +job.metadata.ownerReferences = {} +job.metadata.ownerReferences[1] = ownerRef + +job.spec = deepCopy(obj.spec.jobTemplate.spec) + +local impactedResource = {} +impactedResource.operation = "create" +impactedResource.resource = job +local result = {} +result[1] = impactedResource + +return result diff --git a/resource_customizations/batch/CronJob/actions/discovery.lua b/resource_customizations/batch/CronJob/actions/discovery.lua new file mode 100644 index 0000000000000..61be2c3500122 --- /dev/null +++ b/resource_customizations/batch/CronJob/actions/discovery.lua @@ -0,0 +1,6 @@ +local actions = {} +actions["create-job"] = { + ["iconClass"] = "fa fa-fw fa-play", + ["displayName"] = "Create Job" +} +return actions \ No newline at end of file diff --git a/resource_customizations/batch/CronJob/actions/testdata/cronjob.yaml b/resource_customizations/batch/CronJob/actions/testdata/cronjob.yaml new file mode 100644 index 0000000000000..2c45c5eae1f38 --- /dev/null +++ b/resource_customizations/batch/CronJob/actions/testdata/cronjob.yaml @@ -0,0 +1,33 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: hello + namespace: test-ns + uid: "123" +spec: + schedule: "* * * * *" + jobTemplate: + metadata: + labels: + my: label + annotations: + my: annotation + spec: + ttlSecondsAfterFinished: 100 + template: + metadata: + labels: + pod: label + annotations: + pod: annotation + spec: + containers: + - name: hello + image: busybox:1.28 + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - date; echo Hello from the Kubernetes cluster + resources: {} + restartPolicy: OnFailure \ No newline at end of file diff --git a/resource_customizations/batch/CronJob/actions/testdata/job.yaml b/resource_customizations/batch/CronJob/actions/testdata/job.yaml new file mode 100644 index 0000000000000..322ab0480beb5 --- /dev/null +++ b/resource_customizations/batch/CronJob/actions/testdata/job.yaml @@ -0,0 +1,30 @@ +- k8sOperation: create + unstructuredObj: + apiVersion: batch/v1 + kind: Job + metadata: + name: hello-00000000000 + namespace: test-ns + labels: + my: label + annotations: + cronjob.kubernetes.io/instantiate: manual + my: annotation + spec: + ttlSecondsAfterFinished: 100 + template: + metadata: + labels: + pod: label + annotations: + pod: annotation + spec: + containers: + - name: hello + image: busybox:1.28 + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - date; echo Hello from the Kubernetes cluster + restartPolicy: OnFailure diff --git a/resource_customizations/beat.k8s.elastic.co/Beat/health.lua b/resource_customizations/beat.k8s.elastic.co/Beat/health.lua new file mode 100644 index 0000000000000..c7639dbbd94f0 --- /dev/null +++ b/resource_customizations/beat.k8s.elastic.co/Beat/health.lua @@ -0,0 +1,31 @@ +local hs = {} + +if obj.status ~= nil and (obj.status.health ~= nil or obj.status.expectedNodes ~= nil) then + if obj.status.health == "red" then + hs.status = "Degraded" + hs.message = "Elastic Beat status is Red" + return hs + elseif obj.status.health == "green" then + hs.status = "Healthy" + hs.message = "Elastic Beat status is Green" + return hs + elseif obj.status.health == "yellow" then + if obj.status.availableNodes ~= nil and obj.status.expectedNodes ~= nil then + hs.status = "Progressing" + hs.message = "Elastic Beat status is deploying, there is " .. obj.status.availableNodes .. " instance(s) on " .. obj.status.expectedNodes .. " expected" + return hs + else + hs.status = "Progressing" + hs.message = "Elastic Beat phase is progressing" + return hs + end + elseif obj.status.health == nil then + hs.status = "Progressing" + hs.message = "Elastic Beat phase is progressing" + return hs + end +end + +hs.status = "Unknown" +hs.message = "Elastic Beat status is unknown. Ensure your ArgoCD is current and then check for/file a bug report: https://github.com/argoproj/argo-cd/issues" +return hs diff --git a/resource_customizations/beat.k8s.elastic.co/Beat/health_test.yaml b/resource_customizations/beat.k8s.elastic.co/Beat/health_test.yaml new file mode 100644 index 0000000000000..fb44e998ffaf1 --- /dev/null +++ b/resource_customizations/beat.k8s.elastic.co/Beat/health_test.yaml @@ -0,0 +1,29 @@ +tests: +- healthStatus: + status: Healthy + message: "Elastic Beat status is Green" + inputPath: testdata/ready_green.yaml +- healthStatus: + status: Progressing + message: "Elastic Beat phase is progressing" + inputPath: testdata/ready_yellow_single_node.yaml +- healthStatus: + status: Progressing + message: "Elastic Beat status is deploying, there is 1 instance(s) on 2 expected" + inputPath: testdata/ready_yellow.yaml +- healthStatus: + status: Progressing + message: "Elastic Beat phase is progressing" + inputPath: testdata/progressing.yaml +- healthStatus: + status: Degraded + message: "Elastic Beat status is Red" + inputPath: testdata/ready_red.yaml +- healthStatus: + status: Unknown + message: "Elastic Beat status is unknown. Ensure your ArgoCD is current and then check for/file a bug report: https://github.com/argoproj/argo-cd/issues" + inputPath: testdata/unknown.yaml +- healthStatus: + status: Unknown + message: "Elastic Beat status is unknown. Ensure your ArgoCD is current and then check for/file a bug report: https://github.com/argoproj/argo-cd/issues" + inputPath: testdata/invalid.yaml diff --git a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/invalid.yaml b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/invalid.yaml new file mode 100644 index 0000000000000..3eca183165a5c --- /dev/null +++ b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/invalid.yaml @@ -0,0 +1,12 @@ +apiVersion: beat.k8s.elastic.co/v1beta1 +kind: Beat +metadata: + name: quickstart +spec: + version: 8.8.8 + type: metricbeat +status: + expectedNodes: 1 + health: invalid + observedGeneration: 1 + version: 8.8.1 diff --git a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/progressing.yaml b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/progressing.yaml new file mode 100644 index 0000000000000..b007ad72ae3fe --- /dev/null +++ b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/progressing.yaml @@ -0,0 +1,11 @@ +apiVersion: beat.k8s.elastic.co/v1beta1 +kind: Beat +metadata: + name: quickstart +spec: + version: 8.8.8 + type: metricbeat +status: + expectedNodes: 1 + observedGeneration: 1 + version: 8.8.1 diff --git a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_green.yaml b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_green.yaml new file mode 100644 index 0000000000000..3f3c1866793d8 --- /dev/null +++ b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_green.yaml @@ -0,0 +1,13 @@ +apiVersion: beat.k8s.elastic.co/v1beta1 +kind: Beat +metadata: + name: quickstart +spec: + version: 8.8.8 + type: metricbeat +status: + expectedNodes: 1 + availableNodes: 1 + health: green + observedGeneration: 1 + version: 8.8.1 diff --git a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_red.yaml b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_red.yaml new file mode 100644 index 0000000000000..fc2433c8076a8 --- /dev/null +++ b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_red.yaml @@ -0,0 +1,10 @@ +apiVersion: beat.k8s.elastic.co/v1beta1 +kind: Beat +metadata: + name: quickstart +spec: + version: 8.8.8 + type: metricbeat +status: + expectedNodes: 1 + health: red diff --git a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_yellow.yaml b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_yellow.yaml new file mode 100644 index 0000000000000..831ee281ef02d --- /dev/null +++ b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_yellow.yaml @@ -0,0 +1,11 @@ +apiVersion: beat.k8s.elastic.co/v1beta1 +kind: Beat +metadata: + name: quickstart +spec: + version: 8.8.8 + type: metricbeat +status: + availableNodes: 1 + expectedNodes: 2 + health: yellow diff --git a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_yellow_single_node.yaml b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_yellow_single_node.yaml new file mode 100644 index 0000000000000..d652b5a55d0ff --- /dev/null +++ b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/ready_yellow_single_node.yaml @@ -0,0 +1,10 @@ +apiVersion: beat.k8s.elastic.co/v1beta1 +kind: Beat +metadata: + name: quickstart +spec: + version: 8.8.8 + type: metricbeat +status: + expectedNodes: 1 + health: yellow diff --git a/resource_customizations/beat.k8s.elastic.co/Beat/testdata/unknown.yaml b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/unknown.yaml new file mode 100644 index 0000000000000..dbcca36c9e691 --- /dev/null +++ b/resource_customizations/beat.k8s.elastic.co/Beat/testdata/unknown.yaml @@ -0,0 +1,8 @@ +apiVersion: beat.k8s.elastic.co/v1beta1 +kind: Beat +metadata: + name: quickstart +spec: + version: 8.8.8 + type: metricbeat +status: {} diff --git a/resource_customizations/bitnami.com/SealedSecret/health.lua b/resource_customizations/bitnami.com/SealedSecret/health.lua index 2e3bc3a497223..e18a9a8a79bec 100644 --- a/resource_customizations/bitnami.com/SealedSecret/health.lua +++ b/resource_customizations/bitnami.com/SealedSecret/health.lua @@ -1,4 +1,4 @@ -health_status={} +local health_status={} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do diff --git a/resource_customizations/cassandra.rook.io/Cluster/health.lua b/resource_customizations/cassandra.rook.io/Cluster/health.lua index 5bf48f6c9c49f..74979f8612f3c 100644 --- a/resource_customizations/cassandra.rook.io/Cluster/health.lua +++ b/resource_customizations/cassandra.rook.io/Cluster/health.lua @@ -1,7 +1,7 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.racks ~= nil then - all_racks_good = true + local all_racks_good = true for key, value in pairs(obj.status.racks) do if all_racks_good and value.members ~= nil and value.readyMembers ~= nil and value.members ~= value.readyMembers then all_racks_good = false diff --git a/resource_customizations/cassandra.rook.io/Cluster/testdata/healthy.yaml b/resource_customizations/cassandra.rook.io/Cluster/testdata/healthy.yaml index fbfdce3ca4358..b42422a56f874 100644 --- a/resource_customizations/cassandra.rook.io/Cluster/testdata/healthy.yaml +++ b/resource_customizations/cassandra.rook.io/Cluster/testdata/healthy.yaml @@ -66,7 +66,7 @@ spec: memory: 32Gi limits: cpu: 8 - memory: 32Gi + memory: 32Gi # A key/value list of annotations annotations: # key: value @@ -75,11 +75,11 @@ spec: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: failure-domain.beta.kubernetes.io/region + - key: topology.kubernetes.io/region operator: In values: - us-east-1 - - key: failure-domain.beta.kubernetes.io/zone + - key: topology.kubernetes.io/zone operator: In values: - us-east-1a @@ -93,4 +93,4 @@ status: readyMembers: 3 us-east-1c: members: 3 - readyMembers: 3 \ No newline at end of file + readyMembers: 3 diff --git a/resource_customizations/cassandra.rook.io/Cluster/testdata/progressing.yaml b/resource_customizations/cassandra.rook.io/Cluster/testdata/progressing.yaml index 98c1a65bfc482..4034beca0d621 100644 --- a/resource_customizations/cassandra.rook.io/Cluster/testdata/progressing.yaml +++ b/resource_customizations/cassandra.rook.io/Cluster/testdata/progressing.yaml @@ -75,11 +75,11 @@ spec: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: failure-domain.beta.kubernetes.io/region + - key: topology.kubernetes.io/region operator: In values: - us-east-1 - - key: failure-domain.beta.kubernetes.io/zone + - key: topology.kubernetes.io/zone operator: In values: - us-east-1a diff --git a/resource_customizations/cdi.kubevirt.io/DataVolume/health.lua b/resource_customizations/cdi.kubevirt.io/DataVolume/health.lua index 73cdef241fa8c..10e3d1b9a91b7 100644 --- a/resource_customizations/cdi.kubevirt.io/DataVolume/health.lua +++ b/resource_customizations/cdi.kubevirt.io/DataVolume/health.lua @@ -1,4 +1,4 @@ -hs = { status="Progressing", message="No status available"} +local hs = { status="Progressing", message="No status available"} if obj.status ~= nil then if obj.status.phase ~= nil then hs.message = obj.status.phase diff --git a/resource_customizations/cert-manager.io/Certificate/health.lua b/resource_customizations/cert-manager.io/Certificate/health.lua index 209ebb596e149..fce5bcbe3d1d0 100644 --- a/resource_customizations/cert-manager.io/Certificate/health.lua +++ b/resource_customizations/cert-manager.io/Certificate/health.lua @@ -1,12 +1,17 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then + + -- Always Handle Issuing First to ensure consistent behaviour for i, condition in ipairs(obj.status.conditions) do if condition.type == "Issuing" and condition.status == "True" then hs.status = "Progressing" hs.message = condition.message return hs end + end + + for i, condition in ipairs(obj.status.conditions) do if condition.type == "Ready" and condition.status == "False" then hs.status = "Degraded" hs.message = condition.message diff --git a/resource_customizations/cert-manager.io/Certificate/health_test.yaml b/resource_customizations/cert-manager.io/Certificate/health_test.yaml index ebf8e75e89064..1af7b1a759a60 100644 --- a/resource_customizations/cert-manager.io/Certificate/health_test.yaml +++ b/resource_customizations/cert-manager.io/Certificate/health_test.yaml @@ -7,6 +7,10 @@ tests: status: Progressing message: Issuing certificate as Secret does not exist inputPath: testdata/progressing_issuing.yaml +- healthStatus: + status: Progressing + message: Issuing certificate as Secret does not exist + inputPath: testdata/progressing_issuing_last.yaml - healthStatus: status: Degraded message: 'Resource validation failed: spec.acme.config: Required value: no ACME diff --git a/resource_customizations/cert-manager.io/Certificate/testdata/progressing_issuing_last.yaml b/resource_customizations/cert-manager.io/Certificate/testdata/progressing_issuing_last.yaml new file mode 100644 index 0000000000000..4d21a9b3610f1 --- /dev/null +++ b/resource_customizations/cert-manager.io/Certificate/testdata/progressing_issuing_last.yaml @@ -0,0 +1,36 @@ +apiVersion: cert-manager.io/v1alpha2 +kind: Certificate +metadata: + creationTimestamp: '2018-11-07T00:06:12Z' + generation: 1 + name: test-cert + namespace: argocd + resourceVersion: '64763033' + selfLink: /apis/cert-manager.io/v1alpha2/namespaces/argocd/certificates/test-cert + uid: e6cfba50-314d-11e9-be3f-42010a800011 +spec: + acme: + config: + - domains: + - cd.apps.argoproj.io + http01: + ingress: http01 + commonName: cd.apps.argoproj.io + dnsNames: + - cd.apps.argoproj.io + issuerRef: + kind: Issuer + name: argo-cd-issuer + secretName: test-secret +status: + conditions: + - lastTransitionTime: '2021-09-15T02:10:00Z' + message: Issuing certificate as Secret does not exist + reason: DoesNotExist + status: 'False' + type: Ready + - lastTransitionTime: '2021-09-15T02:10:00Z' + message: Issuing certificate as Secret does not exist + reason: DoesNotExist + status: 'True' + type: Issuing diff --git a/resource_customizations/cert-manager.io/ClusterIssuer/health.lua b/resource_customizations/cert-manager.io/ClusterIssuer/health.lua new file mode 100644 index 0000000000000..b6eb208190496 --- /dev/null +++ b/resource_customizations/cert-manager.io/ClusterIssuer/health.lua @@ -0,0 +1,21 @@ +local hs = {} +if obj.status ~= nil then + if obj.status.conditions ~= nil then + for i, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end + end + end +end + +hs.status = "Progressing" +hs.message = "Initializing ClusterIssuer" +return hs diff --git a/resource_customizations/cert-manager.io/ClusterIssuer/health_test.yaml b/resource_customizations/cert-manager.io/ClusterIssuer/health_test.yaml new file mode 100644 index 0000000000000..1eda5cfb3ca4a --- /dev/null +++ b/resource_customizations/cert-manager.io/ClusterIssuer/health_test.yaml @@ -0,0 +1,14 @@ +tests: +- healthStatus: + status: Progressing + message: Initializing ClusterIssuer + inputPath: testdata/progressing_noStatus.yaml +- healthStatus: + status: Healthy + message: The ACME account was registered with the ACME server + inputPath: testdata/healthy_registered.yaml +- healthStatus: + status: Degraded + message: "Failed to verify ACME account: acme: : 404 page not found\n" + inputPath: testdata/degraded_acmeFailed.yaml + diff --git a/resource_customizations/cert-manager.io/ClusterIssuer/testdata/degraded_acmeFailed.yaml b/resource_customizations/cert-manager.io/ClusterIssuer/testdata/degraded_acmeFailed.yaml new file mode 100644 index 0000000000000..c99c1f4f84ba4 --- /dev/null +++ b/resource_customizations/cert-manager.io/ClusterIssuer/testdata/degraded_acmeFailed.yaml @@ -0,0 +1,26 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + creationTimestamp: "2019-02-15T19:23:48Z" + generation: 1 + name: test-issuer + resourceVersion: "68352438" + uid: 37f408e3-3157-11e9-be3f-42010a800011 +spec: + acme: + email: myemail@example.com + http01: {} + privateKeySecretRef: + key: "" + name: letsencrypt + server: https://acme-v02.api.letsencrypt.org/directory124 +status: + acme: + uri: "" + conditions: + - lastTransitionTime: "2019-02-15T19:23:53Z" + message: | + Failed to verify ACME account: acme: : 404 page not found + reason: ErrRegisterACMEAccount + status: "False" + type: Ready diff --git a/resource_customizations/cert-manager.io/ClusterIssuer/testdata/healthy_registered.yaml b/resource_customizations/cert-manager.io/ClusterIssuer/testdata/healthy_registered.yaml new file mode 100644 index 0000000000000..e883b51e3a793 --- /dev/null +++ b/resource_customizations/cert-manager.io/ClusterIssuer/testdata/healthy_registered.yaml @@ -0,0 +1,25 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + creationTimestamp: "2018-11-06T23:14:18Z" + generation: 1 + name: test-issuer + resourceVersion: "48889060" + uid: b0045219-e219-11e8-9f93-42010a80021d +spec: + acme: + email: myemail@example.com + http01: {} + privateKeySecretRef: + key: "" + name: letsencrypt + server: https://acme-v02.api.letsencrypt.org/directory +status: + acme: + uri: https://acme-v02.api.letsencrypt.org/acme/acct/45250083 + conditions: + - lastTransitionTime: "2018-12-06T06:42:59Z" + message: The ACME account was registered with the ACME server + reason: ACMEAccountRegistered + status: "True" + type: Ready diff --git a/resource_customizations/cert-manager.io/ClusterIssuer/testdata/progressing_noStatus.yaml b/resource_customizations/cert-manager.io/ClusterIssuer/testdata/progressing_noStatus.yaml new file mode 100644 index 0000000000000..4571d229ffed7 --- /dev/null +++ b/resource_customizations/cert-manager.io/ClusterIssuer/testdata/progressing_noStatus.yaml @@ -0,0 +1,16 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + creationTimestamp: "2018-11-06T23:14:18Z" + generation: 1 + name: test-issuer + resourceVersion: "48889060" + uid: b0045219-e219-11e8-9f93-42010a80021d +spec: + acme: + email: myemail@example.com + http01: {} + privateKeySecretRef: + key: "" + name: letsencrypt + server: https://acme-v02.api.letsencrypt.org/directory diff --git a/resource_customizations/cert-manager.io/Issuer/health.lua b/resource_customizations/cert-manager.io/Issuer/health.lua index 2626e18a1f69d..497a6a7f12e6f 100644 --- a/resource_customizations/cert-manager.io/Issuer/health.lua +++ b/resource_customizations/cert-manager.io/Issuer/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do diff --git a/resource_customizations/cert-manager.io/Issuer/testdata/degraded_acmeFailed.yaml b/resource_customizations/cert-manager.io/Issuer/testdata/degraded_acmeFailed.yaml index 62226e3b3be62..a5abcf57a5ac2 100644 --- a/resource_customizations/cert-manager.io/Issuer/testdata/degraded_acmeFailed.yaml +++ b/resource_customizations/cert-manager.io/Issuer/testdata/degraded_acmeFailed.yaml @@ -10,7 +10,7 @@ metadata: uid: 37f408e3-3157-11e9-be3f-42010a800011 spec: acme: - email: myemail@test.com + email: myemail@example.com http01: {} privateKeySecretRef: key: "" diff --git a/resource_customizations/cert-manager.io/Issuer/testdata/healthy_registered.yaml b/resource_customizations/cert-manager.io/Issuer/testdata/healthy_registered.yaml index 08b96394ec823..07181567145f2 100644 --- a/resource_customizations/cert-manager.io/Issuer/testdata/healthy_registered.yaml +++ b/resource_customizations/cert-manager.io/Issuer/testdata/healthy_registered.yaml @@ -10,7 +10,7 @@ metadata: uid: b0045219-e219-11e8-9f93-42010a80021d spec: acme: - email: myemail@test.com + email: myemail@example.com http01: {} privateKeySecretRef: key: "" diff --git a/resource_customizations/cert-manager.io/Issuer/testdata/progressing_noStatus.yaml b/resource_customizations/cert-manager.io/Issuer/testdata/progressing_noStatus.yaml index 820182e3e1e6a..f2e7b80e7f0b5 100644 --- a/resource_customizations/cert-manager.io/Issuer/testdata/progressing_noStatus.yaml +++ b/resource_customizations/cert-manager.io/Issuer/testdata/progressing_noStatus.yaml @@ -10,7 +10,7 @@ metadata: uid: b0045219-e219-11e8-9f93-42010a80021d spec: acme: - email: myemail@test.com + email: myemail@example.com http01: {} privateKeySecretRef: key: "" diff --git a/resource_customizations/certmanager.k8s.io/Certificate/health.lua b/resource_customizations/certmanager.k8s.io/Certificate/health.lua index 61b28c84b86ee..d512d2d422b5a 100644 --- a/resource_customizations/certmanager.k8s.io/Certificate/health.lua +++ b/resource_customizations/certmanager.k8s.io/Certificate/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do diff --git a/resource_customizations/certmanager.k8s.io/Issuer/health.lua b/resource_customizations/certmanager.k8s.io/Issuer/health.lua index 2626e18a1f69d..497a6a7f12e6f 100644 --- a/resource_customizations/certmanager.k8s.io/Issuer/health.lua +++ b/resource_customizations/certmanager.k8s.io/Issuer/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do diff --git a/resource_customizations/certmanager.k8s.io/Issuer/testdata/degraded_acmeFailed.yaml b/resource_customizations/certmanager.k8s.io/Issuer/testdata/degraded_acmeFailed.yaml index dbd819ca9f113..5f0dbec676917 100644 --- a/resource_customizations/certmanager.k8s.io/Issuer/testdata/degraded_acmeFailed.yaml +++ b/resource_customizations/certmanager.k8s.io/Issuer/testdata/degraded_acmeFailed.yaml @@ -10,7 +10,7 @@ metadata: uid: 37f408e3-3157-11e9-be3f-42010a800011 spec: acme: - email: myemail@test.com + email: myemail@example.com http01: {} privateKeySecretRef: key: "" diff --git a/resource_customizations/certmanager.k8s.io/Issuer/testdata/healthy_registered.yaml b/resource_customizations/certmanager.k8s.io/Issuer/testdata/healthy_registered.yaml index db0a81b941bab..a5f6aa14986d6 100644 --- a/resource_customizations/certmanager.k8s.io/Issuer/testdata/healthy_registered.yaml +++ b/resource_customizations/certmanager.k8s.io/Issuer/testdata/healthy_registered.yaml @@ -10,7 +10,7 @@ metadata: uid: b0045219-e219-11e8-9f93-42010a80021d spec: acme: - email: myemail@test.com + email: myemail@example.com http01: {} privateKeySecretRef: key: "" diff --git a/resource_customizations/certmanager.k8s.io/Issuer/testdata/progressing_noStatus.yaml b/resource_customizations/certmanager.k8s.io/Issuer/testdata/progressing_noStatus.yaml index 68f35fa773256..501b7aa20060f 100644 --- a/resource_customizations/certmanager.k8s.io/Issuer/testdata/progressing_noStatus.yaml +++ b/resource_customizations/certmanager.k8s.io/Issuer/testdata/progressing_noStatus.yaml @@ -10,7 +10,7 @@ metadata: uid: b0045219-e219-11e8-9f93-42010a80021d spec: acme: - email: myemail@test.com + email: myemail@example.com http01: {} privateKeySecretRef: key: "" diff --git a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/health.lua b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/health.lua new file mode 100644 index 0000000000000..3e07226b3cf89 --- /dev/null +++ b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/health.lua @@ -0,0 +1,42 @@ +local hs = {} +if obj.status ~= nil then + if obj.status.conditions ~= nil then + local ready = false + local synced = false + local suspended = false + + for i, condition in ipairs(obj.status.conditions) do + + if condition.type == "Ready" then + ready = condition.status == "True" + ready_message = condition.reason + elseif condition.type == "Synced" then + synced = condition.status == "True" + if condition.reason == "ReconcileError" then + synced_message = condition.message + elseif condition.reason == "ReconcilePaused" then + suspended = true + suspended_message = condition.reason + end + end + end + if ready and synced then + hs.status = "Healthy" + hs.message = ready_message + elseif synced == false and suspended == true then + hs.status = "Suspended" + hs.message = suspended_message + elseif ready == false and synced == true and suspended == false then + hs.status = "Progressing" + hs.message = "Waiting for distribution to be available" + else + hs.status = "Degraded" + hs.message = synced_message + end + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for distribution to be created" +return hs \ No newline at end of file diff --git a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/health_test.yaml b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/health_test.yaml new file mode 100644 index 0000000000000..981a6000ecb88 --- /dev/null +++ b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/health_test.yaml @@ -0,0 +1,37 @@ +tests: +- healthStatus: + status: Progressing + message: Waiting for distribution to be available + inputPath: testdata/progressing_creating.yaml +- healthStatus: + status: Progressing + message: Waiting for distribution to be available + inputPath: testdata/progressing_noavailable.yaml +- healthStatus: + status: Progressing + message: Waiting for distribution to be available + inputPath: testdata/progressing.yaml +- healthStatus: + status: Progressing + message: Waiting for distribution to be created + inputPath: testdata/progressing_noStatus.yaml +- healthStatus: + status: Degraded + message: > + update failed: cannot update Distribution in AWS: InvalidParameter: 2 + validation error(s) found. + + - missing required field, + UpdateDistributionInput.DistributionConfig.Origins.Items[0].DomainName. + + - missing required field, + UpdateDistributionInput.DistributionConfig.Origins.Items[0].Id. + inputPath: testdata/degraded_reconcileError.yaml +- healthStatus: + status: Suspended + message: ReconcilePaused + inputPath: testdata/suspended.yaml +- healthStatus: + status: Healthy + message: Available + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/degraded_reconcileError.yaml b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/degraded_reconcileError.yaml new file mode 100644 index 0000000000000..80ea7930574ac --- /dev/null +++ b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/degraded_reconcileError.yaml @@ -0,0 +1,96 @@ +apiVersion: cloudfront.aws.crossplane.io/v1alpha1 +kind: Distribution +metadata: + creationTimestamp: '2024-01-17T07:26:02Z' + generation: 2 + name: crossplane.io + resourceVersion: '261942288' + uid: 4b50c88b-165c-4176-be8e-aa28fdec0a94 +spec: + deletionPolicy: Orphan + forProvider: + distributionConfig: + comment: 'crossplane' + customErrorResponses: + items: [] + defaultCacheBehavior: + allowedMethods: + cachedMethods: + items: + - HEAD + - GET + items: + - HEAD + - GET + compress: false + defaultTTL: 600 + fieldLevelEncryptionID: '' + forwardedValues: + cookies: + forward: none + headers: + items: [] + queryString: false + queryStringCacheKeys: {} + functionAssociations: {} + lambdaFunctionAssociations: {} + maxTTL: 600 + minTTL: 0 + smoothStreaming: false + targetOriginID: crossplane.io + trustedKeyGroups: + enabled: false + trustedSigners: + enabled: false + viewerProtocolPolicy: allow-all + defaultRootObject: index.html + enabled: true + httpVersion: http2 + isIPV6Enabled: true + logging: + bucket: '' + enabled: false + includeCookies: false + prefix: '' + originGroups: {} + origins: + items: + - connectionAttempts: 3 + connectionTimeout: 10 + customOriginConfig: + httpPort: 8080 + httpSPort: 443 + originKeepaliveTimeout: 5 + originProtocolPolicy: http-only + originReadTimeout: 10 + originSSLProtocols: + items: + - TLSv1 + - TLSv1.1 + - TLSv1.2 + priceClass: PriceClass_200 + restrictions: + geoRestriction: + restrictionType: none + region: ap-northeast-2 + providerConfigRef: + name: crossplane +status: + conditions: + - lastTransitionTime: '2024-01-17T07:26:02Z' + message: > + update failed: cannot update Distribution in AWS: InvalidParameter: 2 + validation error(s) found. + + - missing required field, + UpdateDistributionInput.DistributionConfig.Origins.Items[0].DomainName. + + - missing required field, + UpdateDistributionInput.DistributionConfig.Origins.Items[0].Id. + reason: ReconcileError + status: 'False' + type: Synced + - lastTransitionTime: '2024-01-17T07:26:03Z' + reason: Available + status: 'True' + type: Ready diff --git a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/healthy.yaml b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/healthy.yaml new file mode 100644 index 0000000000000..23d0287445e83 --- /dev/null +++ b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/healthy.yaml @@ -0,0 +1,92 @@ +apiVersion: cloudfront.aws.crossplane.io/v1alpha1 +kind: Distribution +metadata: + creationTimestamp: "2023-09-07T01:01:16Z" + generation: 121 + name: crossplane.io + resourceVersion: "254225966" + uid: 531d989c-a3d2-4ab4-841d-ab380cce0bdb +spec: + deletionPolicy: Orphan + forProvider: + distributionConfig: + comment: 'crossplane' + customErrorResponses: + items: [] + defaultCacheBehavior: + allowedMethods: + cachedMethods: + items: + - HEAD + - GET + items: + - HEAD + - GET + compress: false + defaultTTL: 600 + fieldLevelEncryptionID: '' + forwardedValues: + cookies: + forward: none + headers: + items: [] + queryString: false + queryStringCacheKeys: {} + functionAssociations: {} + lambdaFunctionAssociations: {} + maxTTL: 600 + minTTL: 0 + smoothStreaming: false + targetOriginID: crossplane.io + trustedKeyGroups: + enabled: false + trustedSigners: + enabled: false + viewerProtocolPolicy: allow-all + defaultRootObject: index.html + enabled: true + httpVersion: http2 + isIPV6Enabled: true + logging: + bucket: '' + enabled: false + includeCookies: false + prefix: '' + originGroups: {} + origins: + items: + - connectionAttempts: 3 + connectionTimeout: 10 + customHeaders: {} + customOriginConfig: + httpPort: 8080 + httpSPort: 443 + originKeepaliveTimeout: 5 + originProtocolPolicy: http-only + originReadTimeout: 10 + originSSLProtocols: + items: + - TLSv1 + - TLSv1.1 + - TLSv1.2 + domainName: crossplane.io + id: crossplane.io + originShield: + enabled: false + priceClass: PriceClass_200 + restrictions: + geoRestriction: + restrictionType: none + region: ap-northeast-2 + providerConfigRef: + name: crossplane +status: + conditions: + - lastTransitionTime: "2024-01-11T06:23:18Z" + reason: ReconcileSuccess + status: "True" + type: Synced + - lastTransitionTime: "2024-01-10T03:23:02Z" + reason: Available + status: "True" + type: Ready diff --git a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing.yaml b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing.yaml new file mode 100644 index 0000000000000..3dbde7e040867 --- /dev/null +++ b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing.yaml @@ -0,0 +1,92 @@ +apiVersion: cloudfront.aws.crossplane.io/v1alpha1 +kind: Distribution +metadata: + creationTimestamp: '2023-06-16T04:42:04Z' + generation: 37 + name: crossplane.io + resourceVersion: '254326453' + uid: fd357670-b762-4285-ae83-00859c40dd6b +spec: + deletionPolicy: Orphan + forProvider: + distributionConfig: + comment: 'crossplane' + customErrorResponses: + items: [] + defaultCacheBehavior: + allowedMethods: + cachedMethods: + items: + - HEAD + - GET + items: + - GET + - HEAD + compress: false + defaultTTL: 600 + fieldLevelEncryptionID: "" + forwardedValues: + cookies: + forward: none + headers: + items: [] + queryString: false + queryStringCacheKeys: {} + functionAssociations: {} + lambdaFunctionAssociations: {} + maxTTL: 600 + minTTL: 0 + smoothStreaming: false + targetOriginID: crossplane.io + trustedKeyGroups: + enabled: false + trustedSigners: + enabled: false + viewerProtocolPolicy: allow-all + defaultRootObject: index.html + enabled: true + httpVersion: http2 + isIPV6Enabled: true + logging: + bucket: "" + enabled: false + includeCookies: false + prefix: "" + originGroups: {} + origins: + items: + - connectionAttempts: 3 + connectionTimeout: 10 + customHeaders: {} + customOriginConfig: + httpPort: 8080 + httpSPort: 443 + originKeepaliveTimeout: 5 + originProtocolPolicy: http-only + originReadTimeout: 10 + originSSLProtocols: + items: + - TLSv1 + - TLSv1.1 + - TLSv1.2 + domainName: crossplane.io + id: crossplane.io + originShield: + enabled: false + priceClass: PriceClass_200 + restrictions: + geoRestriction: + restrictionType: none + region: ap-northeast-2 + providerConfigRef: + name: crossplane +status: + conditions: + - lastTransitionTime: '2024-01-11T08:11:27Z' + reason: Unavailable + status: 'False' + type: Ready + - lastTransitionTime: '2024-01-11T08:11:02Z' + reason: ReconcileSuccess + status: 'True' + type: Synced diff --git a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_creating.yaml b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_creating.yaml new file mode 100644 index 0000000000000..122ab330d593b --- /dev/null +++ b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_creating.yaml @@ -0,0 +1,92 @@ +apiVersion: cloudfront.aws.crossplane.io/v1alpha1 +kind: Distribution +metadata: + creationTimestamp: "2023-09-07T01:01:16Z" + generation: 121 + name: crossplane.io + resourceVersion: "254225966" + uid: 531d989c-a3d2-4ab4-841d-ab380cce0bdb +spec: + deletionPolicy: Orphan + forProvider: + distributionConfig: + comment: 'crossplane' + customErrorResponses: + items: [] + defaultCacheBehavior: + allowedMethods: + cachedMethods: + items: + - HEAD + - GET + items: + - GET + - HEAD + compress: false + defaultTTL: 600 + fieldLevelEncryptionID: "" + forwardedValues: + cookies: + forward: none + headers: + items: [] + queryString: false + queryStringCacheKeys: {} + functionAssociations: {} + lambdaFunctionAssociations: {} + maxTTL: 600 + minTTL: 0 + smoothStreaming: false + targetOriginID: crossplane.io + trustedKeyGroups: + enabled: false + trustedSigners: + enabled: false + viewerProtocolPolicy: allow-all + defaultRootObject: index.html + enabled: true + httpVersion: http2 + isIPV6Enabled: true + logging: + bucket: "" + enabled: false + includeCookies: false + prefix: "" + originGroups: {} + origins: + items: + - connectionAttempts: 3 + connectionTimeout: 10 + customHeaders: {} + customOriginConfig: + httpPort: 8080 + httpSPort: 443 + originKeepaliveTimeout: 5 + originProtocolPolicy: http-only + originReadTimeout: 10 + originSSLProtocols: + items: + - TLSv1 + - TLSv1.1 + - TLSv1.2 + domainName: crossplane.io + id: crossplane.io + originShield: + enabled: false + priceClass: PriceClass_200 + restrictions: + geoRestriction: + restrictionType: none + region: ap-northeast-2 + providerConfigRef: + name: crossplane +status: + conditions: + - lastTransitionTime: "2023-11-16T04:44:27Z" + reason: Creating + status: "False" + type: Ready + - lastTransitionTime: "2023-11-16T04:44:25Z" + reason: ReconcileSuccess + status: "True" + type: Synced diff --git a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_noStatus.yaml b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_noStatus.yaml new file mode 100644 index 0000000000000..2985ec2dea657 --- /dev/null +++ b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_noStatus.yaml @@ -0,0 +1,82 @@ +apiVersion: cloudfront.aws.crossplane.io/v1alpha1 +kind: Distribution +metadata: + creationTimestamp: "2023-09-07T01:01:16Z" + generation: 121 + name: crossplane.io + resourceVersion: "254225966" + uid: 531d989c-a3d2-4ab4-841d-ab380cce0bdb +spec: + deletionPolicy: Orphan + forProvider: + distributionConfig: + comment: 'crossplane' + customErrorResponses: + items: [] + defaultCacheBehavior: + allowedMethods: + cachedMethods: + items: + - HEAD + - GET + items: + - GET + - HEAD + compress: false + defaultTTL: 600 + fieldLevelEncryptionID: "" + forwardedValues: + cookies: + forward: none + headers: + items: [] + queryString: false + queryStringCacheKeys: {} + functionAssociations: {} + lambdaFunctionAssociations: {} + maxTTL: 600 + minTTL: 0 + smoothStreaming: false + targetOriginID: crossplane.io + trustedKeyGroups: + enabled: false + trustedSigners: + enabled: false + viewerProtocolPolicy: allow-all + defaultRootObject: index.html + enabled: true + httpVersion: http2 + isIPV6Enabled: true + logging: + bucket: "" + enabled: false + includeCookies: false + prefix: "" + originGroups: {} + origins: + items: + - connectionAttempts: 3 + connectionTimeout: 10 + customHeaders: {} + customOriginConfig: + httpPort: 8080 + httpSPort: 443 + originKeepaliveTimeout: 5 + originProtocolPolicy: http-only + originReadTimeout: 10 + originSSLProtocols: + items: + - TLSv1 + - TLSv1.1 + - TLSv1.2 + domainName: crossplane.io + id: crossplane.io + originShield: + enabled: false + priceClass: PriceClass_200 + restrictions: + geoRestriction: + restrictionType: none + region: ap-northeast-2 + providerConfigRef: + name: crossplane diff --git a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_noavailable.yaml b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_noavailable.yaml new file mode 100644 index 0000000000000..7a47b0f48eea7 --- /dev/null +++ b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/progressing_noavailable.yaml @@ -0,0 +1,88 @@ +apiVersion: cloudfront.aws.crossplane.io/v1alpha1 +kind: Distribution +metadata: + generation: 1 + name: crossplane.io + resourceVersion: "261937039" + uid: a52c105f-b0e1-4027-aa19-7e93f269f2a6 +spec: + deletionPolicy: Orphan + forProvider: + distributionConfig: + comment: 'crossplane' + customErrorResponses: + items: [] + defaultCacheBehavior: + allowedMethods: + cachedMethods: + items: + - HEAD + - GET + items: + - GET + - HEAD + compress: false + defaultTTL: 600 + fieldLevelEncryptionID: "" + forwardedValues: + cookies: + forward: none + headers: + items: [] + queryString: false + queryStringCacheKeys: {} + functionAssociations: {} + lambdaFunctionAssociations: {} + maxTTL: 600 + minTTL: 0 + smoothStreaming: false + targetOriginID: crossplane.io + trustedKeyGroups: + enabled: false + trustedSigners: + enabled: false + viewerProtocolPolicy: allow-all + defaultRootObject: index.html + enabled: true + httpVersion: http2 + isIPV6Enabled: true + logging: + bucket: "" + enabled: false + includeCookies: false + prefix: "" + originGroups: {} + origins: + items: + - connectionAttempts: 3 + connectionTimeout: 10 + customHeaders: {} + customOriginConfig: + httpPort: 8080 + httpSPort: 443 + originKeepaliveTimeout: 5 + originProtocolPolicy: http-only + originReadTimeout: 10 + originSSLProtocols: + items: + - TLSv1 + - TLSv1.1 + - TLSv1.2 + domainName: crossplane.io + id: crossplane.io + originShield: + enabled: false + priceClass: PriceClass_200 + restrictions: + geoRestriction: + restrictionType: none + region: ap-northeast-2 + providerConfigRef: + name: crossplane +status: + atProvider: {} + conditions: + - lastTransitionTime: "2024-01-17T07:20:35Z" + reason: ReconcileSuccess + status: "True" + type: Synced diff --git a/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/suspended.yaml b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/suspended.yaml new file mode 100644 index 0000000000000..d15713737ff72 --- /dev/null +++ b/resource_customizations/cloudfront.aws.crossplane.io/Distribution/testdata/suspended.yaml @@ -0,0 +1,94 @@ +apiVersion: cloudfront.aws.crossplane.io/v1alpha1 +kind: Distribution +metadata: + annotations: + crossplane.io/paused: "true" + creationTimestamp: "2023-06-16T04:42:04Z" + generation: 34 + name: crossplane.io + resourceVersion: "254259056" + uid: fd357670-b762-4285-ae83-00859c40dd6b +spec: + deletionPolicy: Orphan + forProvider: + distributionConfig: + comment: 'crossplane' + customErrorResponses: + items: [] + defaultCacheBehavior: + allowedMethods: + cachedMethods: + items: + - HEAD + - GET + items: + - GET + - HEAD + compress: false + defaultTTL: 600 + fieldLevelEncryptionID: "" + forwardedValues: + cookies: + forward: none + headers: + items: [] + queryString: false + queryStringCacheKeys: {} + functionAssociations: {} + lambdaFunctionAssociations: {} + maxTTL: 600 + minTTL: 0 + smoothStreaming: false + targetOriginID: crossplane.io + trustedKeyGroups: + enabled: false + trustedSigners: + enabled: false + viewerProtocolPolicy: allow-all + defaultRootObject: index.html + enabled: true + httpVersion: http2 + isIPV6Enabled: true + logging: + bucket: "" + enabled: false + includeCookies: false + prefix: "" + originGroups: {} + origins: + items: + - connectionAttempts: 3 + connectionTimeout: 10 + customHeaders: {} + customOriginConfig: + httpPort: 8080 + httpSPort: 443 + originKeepaliveTimeout: 5 + originProtocolPolicy: http-only + originReadTimeout: 10 + originSSLProtocols: + items: + - TLSv1 + - TLSv1.1 + - TLSv1.2 + domainName: crossplane.io + id: crossplane.io + originShield: + enabled: false + priceClass: PriceClass_200 + restrictions: + geoRestriction: + restrictionType: none + region: ap-northeast-2 + providerConfigRef: + name: crossplane +status: + conditions: + - lastTransitionTime: "2023-10-16T07:40:47Z" + reason: Available + status: "True" + type: Ready + - lastTransitionTime: "2024-01-11T06:59:47Z" + reason: ReconcilePaused + status: "False" + type: Synced diff --git a/resource_customizations/cloudfunctions.cnrm.cloud.google.com/CloudFunctionsFunction/health.lua b/resource_customizations/cloudfunctions.cnrm.cloud.google.com/CloudFunctionsFunction/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/cloudfunctions.cnrm.cloud.google.com/CloudFunctionsFunction/health.lua +++ b/resource_customizations/cloudfunctions.cnrm.cloud.google.com/CloudFunctionsFunction/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/cloudscheduler.cnrm.cloud.google.com/CloudSchedulerJob/health.lua b/resource_customizations/cloudscheduler.cnrm.cloud.google.com/CloudSchedulerJob/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/cloudscheduler.cnrm.cloud.google.com/CloudSchedulerJob/health.lua +++ b/resource_customizations/cloudscheduler.cnrm.cloud.google.com/CloudSchedulerJob/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/cluster.x-k8s.io/Cluster/health.lua b/resource_customizations/cluster.x-k8s.io/Cluster/health.lua index 3d3f1c4e3892d..3f02513e460a8 100644 --- a/resource_customizations/cluster.x-k8s.io/Cluster/health.lua +++ b/resource_customizations/cluster.x-k8s.io/Cluster/health.lua @@ -27,7 +27,7 @@ function getReadyContitionStatus(obj, hs) return hs end -hs = {} +local hs = {} if obj.spec.paused ~= nil and obj.spec.paused then hs.status = "Suspended" hs.message = "Cluster is paused" diff --git a/resource_customizations/cluster.x-k8s.io/Machine/health.lua b/resource_customizations/cluster.x-k8s.io/Machine/health.lua index 2b52a4351e7e2..146b795d7496d 100644 --- a/resource_customizations/cluster.x-k8s.io/Machine/health.lua +++ b/resource_customizations/cluster.x-k8s.io/Machine/health.lua @@ -1,5 +1,5 @@ function getStatusBasedOnPhase(obj) - hs = {} + local hs = {} hs.status = "Progressing" hs.message = "Waiting for machines" if obj.status ~= nil and obj.status.phase ~= nil then @@ -26,7 +26,7 @@ function getReadyContitionMessage(obj) return "Condition is unknown" end -hs = getStatusBasedOnPhase(obj) +local hs = getStatusBasedOnPhase(obj) if hs.status ~= "Healthy" then hs.message = getReadyContitionMessage(obj) end diff --git a/resource_customizations/cluster.x-k8s.io/MachineDeployment/health.lua b/resource_customizations/cluster.x-k8s.io/MachineDeployment/health.lua index 0ac402c638843..14b0103d19ad6 100644 --- a/resource_customizations/cluster.x-k8s.io/MachineDeployment/health.lua +++ b/resource_customizations/cluster.x-k8s.io/MachineDeployment/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} hs.status = "Progressing" hs.message = "Waiting for machines" diff --git a/resource_customizations/cluster.x-k8s.io/MachineHealthCheck/health.lua b/resource_customizations/cluster.x-k8s.io/MachineHealthCheck/health.lua index 18840408ecae8..b8cff71bbe94b 100644 --- a/resource_customizations/cluster.x-k8s.io/MachineHealthCheck/health.lua +++ b/resource_customizations/cluster.x-k8s.io/MachineHealthCheck/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} hs.status = "Progressing" hs.message = "" diff --git a/resource_customizations/compute.cnrm.cloud.google.com/ComputeDisk/health.lua b/resource_customizations/compute.cnrm.cloud.google.com/ComputeDisk/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/compute.cnrm.cloud.google.com/ComputeDisk/health.lua +++ b/resource_customizations/compute.cnrm.cloud.google.com/ComputeDisk/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/db.atlasgo.io/AtlasMigration/health.lua b/resource_customizations/db.atlasgo.io/AtlasMigration/health.lua new file mode 100644 index 0000000000000..332b43ec21314 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasMigration/health.lua @@ -0,0 +1,37 @@ +hs = {} + +local function readyCond(obj) + if obj.status ~= nil and obj.status.conditions ~= nil then + for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" then + return condition + end + end + end + return nil +end + +local ready = readyCond(obj) + +if ready == nil then + hs.status = "Progressing" + hs.message = "Waiting for Atlas Operator" + return hs +end + +if ready.status == "True" then + hs.status = "Healthy" + hs.message = ready.reason + return hs +end + +if ready.reason == "Reconciling" then + hs.status = "Progressing" +else + hs.status = "Degraded" +end + +hs.message = ready.reason + +return hs + diff --git a/resource_customizations/db.atlasgo.io/AtlasMigration/health_test.yaml b/resource_customizations/db.atlasgo.io/AtlasMigration/health_test.yaml new file mode 100644 index 0000000000000..b827f89c0bdf2 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasMigration/health_test.yaml @@ -0,0 +1,13 @@ +tests: +- healthStatus: + status: Progressing + message: "Reconciling" + inputPath: testdata/progressing.yaml +- healthStatus: + status: Degraded + message: "Migrating" + inputPath: testdata/degraded.yaml +- healthStatus: + status: Healthy + message: "Applied" + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/degraded.yaml b/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/degraded.yaml new file mode 100644 index 0000000000000..ee51f15e48241 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/degraded.yaml @@ -0,0 +1,29 @@ +apiVersion: db.atlasgo.io/v1alpha1 +kind: AtlasMigration +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"db.atlasgo.io/v1alpha1","kind":"AtlasMigration","metadata":{"annotations":{},"name":"atlasmigration-sample","namespace":"default"},"spec":{"dir":{"configMapRef":{"name":"migration-dir"}},"urlFrom":{"secretKeyRef":{"key":"url","name":"mysql-credentials"}}}} + creationTimestamp: "2023-11-16T08:37:23Z" + generation: 1 + name: atlasmigration-sample + namespace: default + resourceVersion: "49923" + uid: 0d5bc3d6-750e-4f5a-82a3-8b9173106ef4 +spec: + dir: + configMapRef: + name: migration-dir + urlFrom: + secretKeyRef: + key: url + name: mysql-credentials +status: + conditions: + - lastTransitionTime: "2023-11-16T08:37:23Z" + message: 'Error: checksum mismatch' + reason: Migrating + status: "False" + type: Ready + lastApplied: 0 + observed_hash: "" diff --git a/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/healthy.yaml b/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/healthy.yaml new file mode 100644 index 0000000000000..4a7a91324d196 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/healthy.yaml @@ -0,0 +1,30 @@ +apiVersion: db.atlasgo.io/v1alpha1 +kind: AtlasMigration +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"db.atlasgo.io/v1alpha1","kind":"AtlasMigration","metadata":{"annotations":{},"name":"atlasmigration-sample","namespace":"default"},"spec":{"dir":{"configMapRef":{"name":"migration-dir"}},"urlFrom":{"secretKeyRef":{"key":"url","name":"mysql-credentials"}}}} + creationTimestamp: "2023-11-16T08:37:23Z" + generation: 1 + name: atlasmigration-sample + namespace: default + resourceVersion: "50387" + uid: 0d5bc3d6-750e-4f5a-82a3-8b9173106ef4 +spec: + dir: + configMapRef: + name: migration-dir + urlFrom: + secretKeyRef: + key: url + name: mysql-credentials +status: + conditions: + - lastTransitionTime: "2023-11-16T08:46:27Z" + message: "" + reason: Applied + status: "True" + type: Ready + lastApplied: 1700124387 + lastAppliedVersion: "20230316085611" + observed_hash: 4969b3c84c097ff61a9f9722b595a66c1a4473bd85fdd282107b98a92db8a43b diff --git a/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/progressing.yaml b/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/progressing.yaml new file mode 100644 index 0000000000000..024f9f7558d78 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/progressing.yaml @@ -0,0 +1,30 @@ +apiVersion: db.atlasgo.io/v1alpha1 +kind: AtlasMigration +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"db.atlasgo.io/v1alpha1","kind":"AtlasMigration","metadata":{"annotations":{},"name":"atlasmigration-sample","namespace":"default"},"spec":{"dir":{"configMapRef":{"name":"migration-dir"}},"urlFrom":{"secretKeyRef":{"key":"url","name":"mysql-credentials"}}}} + creationTimestamp: "2023-11-16T08:37:23Z" + generation: 1 + name: atlasmigration-sample + namespace: default + resourceVersion: "50387" + uid: 0d5bc3d6-750e-4f5a-82a3-8b9173106ef4 +spec: + dir: + configMapRef: + name: migration-dir + urlFrom: + secretKeyRef: + key: url + name: mysql-credentials +status: + conditions: + - lastTransitionTime: "2023-11-16T08:46:27Z" + message: "Current migration data has changed" + reason: "Reconciling" + status: "False" + type: Ready + lastApplied: 1700124387 + lastAppliedVersion: "20230316085611" + observed_hash: 4969b3c84c097ff61a9f9722b595a66c1a4473bd85fdd282107b98a92db8a43b diff --git a/resource_customizations/db.atlasgo.io/AtlasSchema/health.lua b/resource_customizations/db.atlasgo.io/AtlasSchema/health.lua new file mode 100644 index 0000000000000..c66d66d15b5a8 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasSchema/health.lua @@ -0,0 +1,37 @@ +hs = {} + +local function readyCond(obj) + if obj.status ~= nil and obj.status.conditions ~= nil then + for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" then + return condition + end + end + end + return nil +end + +local ready = readyCond(obj) + +if ready == nil then + hs.status = "Progressing" + hs.message = "Waiting for Atlas Operator" + return hs +end + +if ready.status == "True" then + hs.status = "Healthy" + hs.message = ready.reason + return hs +end + +if ready.message == "Reconciling" or ready.message == "GettingDevDB" then + hs.status = "Progressing" +else + hs.status = "Degraded" +end + +hs.message = ready.reason + +return hs + diff --git a/resource_customizations/db.atlasgo.io/AtlasSchema/health_test.yaml b/resource_customizations/db.atlasgo.io/AtlasSchema/health_test.yaml new file mode 100644 index 0000000000000..0fe102f299138 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasSchema/health_test.yaml @@ -0,0 +1,13 @@ +tests: +- healthStatus: + status: Progressing + message: "Reconciling" + inputPath: testdata/progressing.yaml +- healthStatus: + status: Degraded + message: "ApplyingSchema" + inputPath: testdata/degraded.yaml +- healthStatus: + status: Healthy + message: "Applied" + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/degraded.yaml b/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/degraded.yaml new file mode 100644 index 0000000000000..08383988e996a --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/degraded.yaml @@ -0,0 +1,38 @@ +apiVersion: db.atlasgo.io/v1alpha1 +kind: AtlasSchema +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"db.atlasgo.io/v1alpha1","kind":"AtlasSchema","metadata":{"annotations":{},"name":"atlasschema-mysql","namespace":"default"},"spec":{"schema":{"sql":"create table users (\n id int not null auto_increment,\n name varchar(255) not null,\n email varchar(255) unique not null,\n short_bio varchar(255) not null,\n primary key (id)\n);\n"},"urlFrom":{"secretKeyRef":{"key":"url","name":"mysql-credentials"}}}} + creationTimestamp: "2023-11-15T14:33:18Z" + generation: 2 + name: atlasschema-mysql + namespace: default + resourceVersion: "46659" + uid: 54a4cdfc-e4f9-4c3d-934c-e08b6122e38a +spec: + schema: + sql: | + xcreate table users ( + id int not null auto_increment, + name varchar(255) not null, + email varchar(255) unique not null, + short_bio varchar(255) not null, + primary key (id) + ); + urlFrom: + secretKeyRef: + key: url + name: mysql-credentials +status: + conditions: + - lastTransitionTime: "2023-11-15T14:38:41Z" + message: |- + Error: sql/migrate: read migration directory state: sql/migrate: execute: executing statement "xcreate table users (\n id int not null auto_increment,\n name varchar(255) not null,\n email varchar(255) unique not null,\n short_bio varchar(255) not null,\n primary key (id)\n);" from version "schema": Error 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xcreate table users ( + id int not null auto_increment, + name varchar(255) not ' at line 1 + reason: ApplyingSchema + status: "False" + type: Ready + last_applied: 1700058814 + observed_hash: ddfe666707ddf5c2cc7625c2a0de89da51e54fc7caa6403db307146430d20d85 diff --git a/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/healthy.yaml b/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/healthy.yaml new file mode 100644 index 0000000000000..eca8ec497f09a --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/healthy.yaml @@ -0,0 +1,39 @@ +apiVersion: db.atlasgo.io/v1alpha1 +kind: AtlasSchema +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"db.atlasgo.io/v1alpha1","kind":"AtlasSchema","metadata":{"annotations":{},"name":"atlasschema-mysql","namespace":"default"},"spec":{"schema":{"sql":"create table users (\n id int not null auto_increment,\n name varchar(255) not null,\n email varchar(255) unique not null,\n short_bio varchar(255) not null,\n primary key (id)\n);\n"},"urlFrom":{"secretKeyRef":{"key":"url","name":"mysql-credentials"}}}} + creationTimestamp: "2023-11-15T14:33:18Z" + generation: 1 + name: atlasschema-mysql + namespace: default + resourceVersion: "46390" + uid: 54a4cdfc-e4f9-4c3d-934c-e08b6122e38a +spec: + schema: + sql: | + create table users ( + id int not null auto_increment, + name varchar(255) not null, + email varchar(255) unique not null, + short_bio varchar(255) not null, + primary key (id) + ); + urlFrom: + secretKeyRef: + key: url + name: mysql-credentials +status: + conditions: + - lastTransitionTime: "2023-11-15T14:33:34Z" + message: 'The schema has been applied successfully. Apply response: {"Driver":"mysql","URL":{"Scheme":"mysql","Opaque":"","User":{},"Host":"mysql.default:3306","Path":"/myapp","RawPath":"","OmitHost":false,"ForceQuery":false,"RawQuery":"parseTime=true","Fragment":"","RawFragment":"","Schema":"myapp"},"Changes":{"Applied":["CREATE + TABLE `users` (\n `id` int NOT NULL AUTO_INCREMENT,\n `name` varchar(255) + NOT NULL,\n `email` varchar(255) NOT NULL,\n `short_bio` varchar(255) NOT + NULL,\n PRIMARY KEY (`id`),\n UNIQUE INDEX `email` (`email`)\n) CHARSET utf8mb4 + COLLATE utf8mb4_0900_ai_ci"]}}' + reason: Applied + status: "True" + type: Ready + last_applied: 1700058814 + observed_hash: ddfe666707ddf5c2cc7625c2a0de89da51e54fc7caa6403db307146430d20d85 diff --git a/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/progressing.yaml b/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/progressing.yaml new file mode 100644 index 0000000000000..79d59ca768141 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/progressing.yaml @@ -0,0 +1,35 @@ +apiVersion: db.atlasgo.io/v1alpha1 +kind: AtlasSchema +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"db.atlasgo.io/v1alpha1","kind":"AtlasSchema","metadata":{"annotations":{},"name":"atlasschema-mysql","namespace":"default"},"spec":{"schema":{"sql":"create table users (\n id int not null auto_increment,\n name varchar(255) not null,\n email varchar(255) unique not null,\n short_bio varchar(255) not null,\n primary key (id)\n);\n"},"urlFrom":{"secretKeyRef":{"key":"url","name":"mysql-credentials"}}}} + creationTimestamp: "2023-11-15T14:33:18Z" + generation: 1 + name: atlasschema-mysql + namespace: default + resourceVersion: "46390" + uid: 54a4cdfc-e4f9-4c3d-934c-e08b6122e38a +spec: + schema: + sql: | + create table users ( + id int not null auto_increment, + name varchar(255) not null, + email varchar(255) unique not null, + short_bio varchar(255) not null, + primary key (id) + ); + urlFrom: + secretKeyRef: + key: url + name: mysql-credentials +status: + conditions: + - lastTransitionTime: "2023-11-15T14:33:34Z" + message: 'Reconciling' + reason: Reconciling + status: "False" + type: Ready + last_applied: 1700058814 + observed_hash: ddfe666707ddf5c2cc7625c2a0de89da51e54fc7caa6403db307146430d20d85 diff --git a/resource_customizations/elasticsearch.k8s.elastic.co/Elasticsearch/health.lua b/resource_customizations/elasticsearch.k8s.elastic.co/Elasticsearch/health.lua index 3bac570b0c4d7..241413e68f4b5 100644 --- a/resource_customizations/elasticsearch.k8s.elastic.co/Elasticsearch/health.lua +++ b/resource_customizations/elasticsearch.k8s.elastic.co/Elasticsearch/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.availableNodes ~= nil then local sum = 0 @@ -17,7 +17,7 @@ if obj.status ~= nil then hs.message = "Elasticsearch Cluster status is Green" return hs elseif obj.status.health == "yellow" then - hs.status = "Degraded" + hs.status = "Progressing" hs.message = "Elasticsearch Cluster status is Yellow. Check the status of indices, replicas and shards" return hs elseif obj.status.health == "red" then diff --git a/resource_customizations/elasticsearch.k8s.elastic.co/Elasticsearch/health_test.yaml b/resource_customizations/elasticsearch.k8s.elastic.co/Elasticsearch/health_test.yaml index 0a0ef51d16f6c..015bc145d8ff8 100644 --- a/resource_customizations/elasticsearch.k8s.elastic.co/Elasticsearch/health_test.yaml +++ b/resource_customizations/elasticsearch.k8s.elastic.co/Elasticsearch/health_test.yaml @@ -4,7 +4,7 @@ tests: message: "Elasticsearch Cluster status is Green" inputPath: testdata/ready_green.yaml - healthStatus: - status: Degraded + status: Progressing message: "Elasticsearch Cluster status is Yellow. Check the status of indices, replicas and shards" inputPath: testdata/ready_yellow.yaml - healthStatus: diff --git a/resource_customizations/embed.go b/resource_customizations/embed.go index 251b12293efac..8a4d5316cd3df 100644 --- a/resource_customizations/embed.go +++ b/resource_customizations/embed.go @@ -5,5 +5,6 @@ import ( ) // Embedded contains embedded resource customization +// //go:embed * var Embedded embed.FS diff --git a/resource_customizations/external-secrets.io/ClusterExternalSecret/health.lua b/resource_customizations/external-secrets.io/ClusterExternalSecret/health.lua new file mode 100644 index 0000000000000..b89fbd5965e6c --- /dev/null +++ b/resource_customizations/external-secrets.io/ClusterExternalSecret/health.lua @@ -0,0 +1,25 @@ +local hs = {} +if obj.status ~= nil then + if obj.status.conditions ~= nil then + -- For ClusterExternalSecret, new statuses are appended to the end of the list + local lastStatus = obj.status.conditions[#obj.status.conditions] + if lastStatus.type == "Ready" and lastStatus.status == "True" then + hs.status = "Healthy" + hs.message = lastStatus.message + return hs + end + if lastStatus.type == "PartiallyReady" and lastStatus.status == "True" then + hs.status = "Degraded" + hs.message = lastStatus.message + return hs + end + if lastStatus.type == "NotReady" and lastStatus.status == "True" then + hs.status = "Degraded" + hs.message = lastStatus.message + return hs + end + end +end +hs.status = "Progressing" +hs.message = "Waiting for ClusterExternalSecret" +return hs diff --git a/resource_customizations/external-secrets.io/ClusterExternalSecret/health_test.yaml b/resource_customizations/external-secrets.io/ClusterExternalSecret/health_test.yaml new file mode 100644 index 0000000000000..52e4c7c13740f --- /dev/null +++ b/resource_customizations/external-secrets.io/ClusterExternalSecret/health_test.yaml @@ -0,0 +1,21 @@ +tests: + - healthStatus: + status: Progressing + message: Waiting for ClusterExternalSecret + inputPath: testdata/progressing.yaml + - healthStatus: + status: Degraded + message: 'one or more namespaces failed' + inputPath: testdata/notready.yaml + - healthStatus: + status: Degraded + message: 'one or more namespaces failed' + inputPath: testdata/partiallyready.yaml + - healthStatus: + status: Degraded + message: 'one or more namespaces failed' + inputPath: testdata/partiallyready-multiple-conditions.yaml + - healthStatus: + status: Healthy + message: '' + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/healthy.yaml b/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/healthy.yaml new file mode 100644 index 0000000000000..1a5f61b44a48f --- /dev/null +++ b/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/healthy.yaml @@ -0,0 +1,37 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ClusterExternalSecret +metadata: + name: ces +spec: + externalSecretName: hello-world-es + externalSecretSpec: + data: + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /foo + property: key + secretKey: mykey + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: secretmanager + target: + creationPolicy: Owner + deletionPolicy: Retain + name: mysecret + template: + data: + somekey: '{{ .somecreds }}' + engineVersion: v2 + type: Opaque + namespaceSelector: + matchLabels: + cool: label +status: + conditions: + - message: one or more namespaces failed + status: "True" + type: PartiallyReady + - status: "True" + type: Ready diff --git a/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/notready.yaml b/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/notready.yaml new file mode 100644 index 0000000000000..eeea3069bfb2d --- /dev/null +++ b/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/notready.yaml @@ -0,0 +1,38 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ClusterExternalSecret +metadata: + name: ces +spec: + externalSecretName: hello-world-es + externalSecretSpec: + data: + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /foo + property: key + secretKey: mykey + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: secretmanager + target: + creationPolicy: Owner + deletionPolicy: Retain + name: mysecret + template: + data: + somekey: '{{ .somecreds }}' + engineVersion: v2 + type: Opaque + namespaceSelector: + matchLabels: + cool: label +status: + conditions: + - message: one or more namespaces failed + status: "True" + type: NotReady + failedNamespaces: + - namespace: default + reason: external secret already exists in namespace diff --git a/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/partiallyready-multiple-conditions.yaml b/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/partiallyready-multiple-conditions.yaml new file mode 100644 index 0000000000000..52f6141871de0 --- /dev/null +++ b/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/partiallyready-multiple-conditions.yaml @@ -0,0 +1,43 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ClusterExternalSecret +metadata: + name: ces +spec: + externalSecretName: hello-world-es + externalSecretSpec: + data: + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /foo + property: key + secretKey: mykey + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: secretmanager + target: + creationPolicy: Owner + deletionPolicy: Retain + name: mysecret + template: + data: + somekey: '{{ .somecreds }}' + engineVersion: v2 + type: Opaque + namespaceSelector: + matchLabels: + cool: label +status: + conditions: + - message: one or more namespaces failed + status: "True" + type: NotReady + - message: one or more namespaces failed + status: "True" + type: PartiallyReady + failedNamespaces: + - namespace: default + reason: external secret already exists in namespace + provisionedNamespaces: + - other-namespace diff --git a/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/partiallyready.yaml b/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/partiallyready.yaml new file mode 100644 index 0000000000000..f6a291526fd7f --- /dev/null +++ b/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/partiallyready.yaml @@ -0,0 +1,40 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ClusterExternalSecret +metadata: + name: ces +spec: + externalSecretName: hello-world-es + externalSecretSpec: + data: + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /foo + property: key + secretKey: mykey + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: secretmanager + target: + creationPolicy: Owner + deletionPolicy: Retain + name: mysecret + template: + data: + somekey: '{{ .somecreds }}' + engineVersion: v2 + type: Opaque + namespaceSelector: + matchLabels: + cool: label +status: + conditions: + - message: one or more namespaces failed + status: "True" + type: PartiallyReady + failedNamespaces: + - namespace: default + reason: external secret already exists in namespace + provisionedNamespaces: + - other-namespace diff --git a/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/progressing.yaml b/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/progressing.yaml new file mode 100644 index 0000000000000..8e326e413cf8a --- /dev/null +++ b/resource_customizations/external-secrets.io/ClusterExternalSecret/testdata/progressing.yaml @@ -0,0 +1,30 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ClusterExternalSecret +metadata: + name: ces +spec: + externalSecretName: hello-world-es + externalSecretSpec: + data: + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /foo + property: key + secretKey: mykey + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: secretmanager + target: + creationPolicy: Owner + deletionPolicy: Retain + name: mysecret + template: + data: + somekey: '{{ .somecreds }}' + engineVersion: v2 + type: Opaque + namespaceSelector: + matchLabels: + cool: label diff --git a/resource_customizations/external-secrets.io/ClusterSecretStore/health.lua b/resource_customizations/external-secrets.io/ClusterSecretStore/health.lua new file mode 100644 index 0000000000000..4c430f78383f4 --- /dev/null +++ b/resource_customizations/external-secrets.io/ClusterSecretStore/health.lua @@ -0,0 +1,20 @@ +local hs = {} +if obj.status ~= nil then + if obj.status.conditions ~= nil then + for i, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end + end + end +end +hs.status = "Progressing" +hs.message = "Waiting for ClusterSecretStore" +return hs diff --git a/resource_customizations/external-secrets.io/ClusterSecretStore/health_test.yaml b/resource_customizations/external-secrets.io/ClusterSecretStore/health_test.yaml new file mode 100644 index 0000000000000..6e692158e6e10 --- /dev/null +++ b/resource_customizations/external-secrets.io/ClusterSecretStore/health_test.yaml @@ -0,0 +1,9 @@ +tests: + - healthStatus: + status: Degraded + message: 'unable to validate store' + inputPath: testdata/degraded.yaml + - healthStatus: + status: Healthy + message: 'store validated' + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/external-secrets.io/ClusterSecretStore/testdata/degraded.yaml b/resource_customizations/external-secrets.io/ClusterSecretStore/testdata/degraded.yaml new file mode 100644 index 0000000000000..1f00cc4ea61e7 --- /dev/null +++ b/resource_customizations/external-secrets.io/ClusterSecretStore/testdata/degraded.yaml @@ -0,0 +1,16 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ClusterSecretStore +metadata: + name: secretmanager +spec: + provider: + aws: + region: us-east-1 + service: SecretsManager +status: + conditions: + - lastTransitionTime: "2023-03-21T22:58:01Z" + message: unable to validate store + reason: ValidationFailed + status: "False" + type: Ready diff --git a/resource_customizations/external-secrets.io/ClusterSecretStore/testdata/healthy.yaml b/resource_customizations/external-secrets.io/ClusterSecretStore/testdata/healthy.yaml new file mode 100644 index 0000000000000..8c99de1326179 --- /dev/null +++ b/resource_customizations/external-secrets.io/ClusterSecretStore/testdata/healthy.yaml @@ -0,0 +1,17 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ClusterSecretStore +metadata: + name: secretmanager +spec: + provider: + aws: + region: us-east-1 + service: SecretsManager +status: + capabilities: ReadWrite + conditions: + - lastTransitionTime: "2023-03-22T04:51:03Z" + message: store validated + reason: Valid + status: "True" + type: Ready diff --git a/resource_customizations/external-secrets.io/ExternalSecret/actions/action_test.yaml b/resource_customizations/external-secrets.io/ExternalSecret/actions/action_test.yaml new file mode 100644 index 0000000000000..83f49fcff7439 --- /dev/null +++ b/resource_customizations/external-secrets.io/ExternalSecret/actions/action_test.yaml @@ -0,0 +1,4 @@ +actionTests: + - action: refresh + inputPath: testdata/external-secret.yaml + expectedOutputPath: testdata/external-secret-updated.yaml diff --git a/resource_customizations/external-secrets.io/ExternalSecret/actions/discovery.lua b/resource_customizations/external-secrets.io/ExternalSecret/actions/discovery.lua new file mode 100644 index 0000000000000..89d806c8deceb --- /dev/null +++ b/resource_customizations/external-secrets.io/ExternalSecret/actions/discovery.lua @@ -0,0 +1,3 @@ +local actions = {} +actions["refresh"] = {["disabled"] = false} +return actions diff --git a/resource_customizations/external-secrets.io/ExternalSecret/actions/refresh/action.lua b/resource_customizations/external-secrets.io/ExternalSecret/actions/refresh/action.lua new file mode 100644 index 0000000000000..fa29c485fa16a --- /dev/null +++ b/resource_customizations/external-secrets.io/ExternalSecret/actions/refresh/action.lua @@ -0,0 +1,6 @@ +local os = require("os") +if obj.metadata.annotations == nil then + obj.metadata.annotations = {} +end +obj.metadata.annotations["force-sync"] = os.date("!%Y-%m-%dT%XZ") +return obj diff --git a/resource_customizations/external-secrets.io/ExternalSecret/actions/testdata/external-secret-updated.yaml b/resource_customizations/external-secrets.io/ExternalSecret/actions/testdata/external-secret-updated.yaml new file mode 100644 index 0000000000000..4266e9a5d18fd --- /dev/null +++ b/resource_customizations/external-secrets.io/ExternalSecret/actions/testdata/external-secret-updated.yaml @@ -0,0 +1,56 @@ +apiVersion: external-secrets.io/v1alpha1 +kind: ExternalSecret +metadata: + annotations: + force-sync: '0001-01-01T00:00:00Z' + creationTimestamp: '2021-11-16T21:59:33Z' + generation: 1 + name: test-healthy + namespace: argocd + resourceVersion: '136487331' + selfLink: /apis/external-secrets.io/v1alpha1/namespaces/argocd/externalsecrets/test-healthy + uid: 1e754a7e-0781-4d57-932d-4651d5b19586 +spec: + data: + - remoteRef: + key: secret/sa/example + property: api.address + secretKey: url + - remoteRef: + key: secret/sa/example + property: ca.crt + secretKey: ca + - remoteRef: + key: secret/sa/example + property: token + secretKey: token + refreshInterval: 1m + secretStoreRef: + kind: SecretStore + name: example + target: + creationPolicy: Owner + template: + data: + config: | + { + "bearerToken": "{{ .token | base64decode | toString }}", + "tlsClientConfig": { + "insecure": false, + "caData": "{{ .ca | toString }}" + } + } + name: cluster-test + server: '{{ .url | toString }}' + metadata: + labels: + argocd.argoproj.io/secret-type: cluster +status: + conditions: + - lastTransitionTime: '2021-11-16T21:59:34Z' + message: Secret was synced + reason: SecretSynced + status: 'True' + type: Ready + refreshTime: '2021-11-29T18:32:24Z' + syncedResourceVersion: 1-519a61da0dc68b2575b4f8efada70e42 diff --git a/resource_customizations/external-secrets.io/ExternalSecret/actions/testdata/external-secret.yaml b/resource_customizations/external-secrets.io/ExternalSecret/actions/testdata/external-secret.yaml new file mode 100644 index 0000000000000..da17edbfe902d --- /dev/null +++ b/resource_customizations/external-secrets.io/ExternalSecret/actions/testdata/external-secret.yaml @@ -0,0 +1,54 @@ +apiVersion: external-secrets.io/v1alpha1 +kind: ExternalSecret +metadata: + creationTimestamp: '2021-11-16T21:59:33Z' + generation: 1 + name: test-healthy + namespace: argocd + resourceVersion: '136487331' + selfLink: /apis/external-secrets.io/v1alpha1/namespaces/argocd/externalsecrets/test-healthy + uid: 1e754a7e-0781-4d57-932d-4651d5b19586 +spec: + data: + - remoteRef: + key: secret/sa/example + property: api.address + secretKey: url + - remoteRef: + key: secret/sa/example + property: ca.crt + secretKey: ca + - remoteRef: + key: secret/sa/example + property: token + secretKey: token + refreshInterval: 1m + secretStoreRef: + kind: SecretStore + name: example + target: + creationPolicy: Owner + template: + data: + config: | + { + "bearerToken": "{{ .token | base64decode | toString }}", + "tlsClientConfig": { + "insecure": false, + "caData": "{{ .ca | toString }}" + } + } + name: cluster-test + server: '{{ .url | toString }}' + metadata: + labels: + argocd.argoproj.io/secret-type: cluster +status: + conditions: + - lastTransitionTime: '2021-11-16T21:59:34Z' + message: Secret was synced + reason: SecretSynced + status: 'True' + type: Ready + refreshTime: '2021-11-29T18:32:24Z' + syncedResourceVersion: 1-519a61da0dc68b2575b4f8efada70e42 diff --git a/resource_customizations/external-secrets.io/ExternalSecret/health.lua b/resource_customizations/external-secrets.io/ExternalSecret/health.lua index 889c924d2a3db..4def31d95d7ff 100644 --- a/resource_customizations/external-secrets.io/ExternalSecret/health.lua +++ b/resource_customizations/external-secrets.io/ExternalSecret/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do diff --git a/resource_customizations/external-secrets.io/PushSecret/actions/action_test.yaml b/resource_customizations/external-secrets.io/PushSecret/actions/action_test.yaml new file mode 100644 index 0000000000000..457e5892667a3 --- /dev/null +++ b/resource_customizations/external-secrets.io/PushSecret/actions/action_test.yaml @@ -0,0 +1,4 @@ +actionTests: + - action: push + inputPath: testdata/push-secret.yaml + expectedOutputPath: testdata/push-secret-updated.yaml diff --git a/resource_customizations/external-secrets.io/PushSecret/actions/discovery.lua b/resource_customizations/external-secrets.io/PushSecret/actions/discovery.lua new file mode 100644 index 0000000000000..6b095fbd98dbe --- /dev/null +++ b/resource_customizations/external-secrets.io/PushSecret/actions/discovery.lua @@ -0,0 +1,3 @@ +actions = {} +actions["push"] = {["disabled"] = false} +return actions diff --git a/resource_customizations/external-secrets.io/PushSecret/actions/push/action.lua b/resource_customizations/external-secrets.io/PushSecret/actions/push/action.lua new file mode 100644 index 0000000000000..fa29c485fa16a --- /dev/null +++ b/resource_customizations/external-secrets.io/PushSecret/actions/push/action.lua @@ -0,0 +1,6 @@ +local os = require("os") +if obj.metadata.annotations == nil then + obj.metadata.annotations = {} +end +obj.metadata.annotations["force-sync"] = os.date("!%Y-%m-%dT%XZ") +return obj diff --git a/resource_customizations/external-secrets.io/PushSecret/actions/testdata/push-secret-updated.yaml b/resource_customizations/external-secrets.io/PushSecret/actions/testdata/push-secret-updated.yaml new file mode 100644 index 0000000000000..952f7e98232c0 --- /dev/null +++ b/resource_customizations/external-secrets.io/PushSecret/actions/testdata/push-secret-updated.yaml @@ -0,0 +1,41 @@ +apiVersion: external-secrets.io/v1alpha1 +kind: PushSecret +metadata: + annotations: + force-sync: '0001-01-01T00:00:00Z' + creationTimestamp: '2023-07-05T20:49:16Z' + generation: 1 + name: test-healthy + namespace: external-secret + resourceVersion: '777692391' + uid: 88cb613a-07b0-4fb2-8fdb-d5a5a9c2c917 +spec: + data: + - match: + remoteRef: + property: test + remoteKey: remote/path + secretKey: test + deletionPolicy: None + refreshInterval: 5m + secretStoreRefs: + - kind: ClusterSecretStore + name: my-store + selector: + secret: + name: existing-secret +status: + conditions: + - lastTransitionTime: '2023-07-05T20:49:16Z' + message: PushSecret synced successfully + reason: Synced + status: 'True' + type: Ready + syncedPushSecrets: + ClusterSecretStore/my-store: + remote/path/test: + match: + remoteRef: + property: test + remoteKey: remote/path + secretKey: test diff --git a/resource_customizations/external-secrets.io/PushSecret/actions/testdata/push-secret.yaml b/resource_customizations/external-secrets.io/PushSecret/actions/testdata/push-secret.yaml new file mode 100644 index 0000000000000..487233a773e95 --- /dev/null +++ b/resource_customizations/external-secrets.io/PushSecret/actions/testdata/push-secret.yaml @@ -0,0 +1,39 @@ +apiVersion: external-secrets.io/v1alpha1 +kind: PushSecret +metadata: + creationTimestamp: '2023-07-05T20:49:16Z' + generation: 1 + name: test-healthy + namespace: external-secret + resourceVersion: '777692391' + uid: 88cb613a-07b0-4fb2-8fdb-d5a5a9c2c917 +spec: + data: + - match: + remoteRef: + property: test + remoteKey: remote/path + secretKey: test + deletionPolicy: None + refreshInterval: 5m + secretStoreRefs: + - kind: ClusterSecretStore + name: my-store + selector: + secret: + name: existing-secret +status: + conditions: + - lastTransitionTime: '2023-07-05T20:49:16Z' + message: PushSecret synced successfully + reason: Synced + status: 'True' + type: Ready + syncedPushSecrets: + ClusterSecretStore/my-store: + remote/path/test: + match: + remoteRef: + property: test + remoteKey: remote/path + secretKey: test diff --git a/resource_customizations/external-secrets.io/PushSecret/health.lua b/resource_customizations/external-secrets.io/PushSecret/health.lua new file mode 100644 index 0000000000000..d86cb4c47f8b5 --- /dev/null +++ b/resource_customizations/external-secrets.io/PushSecret/health.lua @@ -0,0 +1,20 @@ +hs = {} +if obj.status ~= nil then + if obj.status.conditions ~= nil then + for i, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" and condition.status == "False" then + hs.status = "Degraded" + hs.message = condition.message + return hs + end + if condition.type == "Ready" and condition.status == "True" then + hs.status = "Healthy" + hs.message = condition.message + return hs + end + end + end +end +hs.status = "Progressing" +hs.message = "Waiting for PushSecret" +return hs diff --git a/resource_customizations/external-secrets.io/PushSecret/health_test.yaml b/resource_customizations/external-secrets.io/PushSecret/health_test.yaml new file mode 100644 index 0000000000000..07d6ab3c70136 --- /dev/null +++ b/resource_customizations/external-secrets.io/PushSecret/health_test.yaml @@ -0,0 +1,13 @@ +tests: + - healthStatus: + status: Progressing + message: Waiting for PushSecret + inputPath: testdata/progressing.yaml + - healthStatus: + status: Degraded + message: 'set secret failed: could not write remote ref test to target secretstore my-store: Error making API request.' + inputPath: testdata/degraded.yaml + - healthStatus: + status: Healthy + message: 'PushSecret synced successfully' + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/external-secrets.io/PushSecret/testdata/degraded.yaml b/resource_customizations/external-secrets.io/PushSecret/testdata/degraded.yaml new file mode 100644 index 0000000000000..aab422eb3eb30 --- /dev/null +++ b/resource_customizations/external-secrets.io/PushSecret/testdata/degraded.yaml @@ -0,0 +1,33 @@ +apiVersion: external-secrets.io/v1alpha1 +kind: PushSecret +metadata: + creationTimestamp: '2023-07-05T20:49:16Z' + generation: 1 + name: test-degraded + namespace: external-secret + resourceVersion: '777692391' + uid: 88cb613a-07b0-4fb2-8fdb-d5a5a9c2c917 +spec: + data: + - match: + remoteRef: + property: test + remoteKey: remote/path + secretKey: test + deletionPolicy: None + refreshInterval: 5m + secretStoreRefs: + - kind: ClusterSecretStore + name: my-store + selector: + secret: + name: existing-secret +status: + conditions: + - lastTransitionTime: '2023-07-05T20:49:16Z' + message: 'set secret failed: could not write remote ref test to target secretstore my-store: Error making API request.' + reason: Errored + status: 'False' + type: Ready + syncedPushSecrets: + ClusterSecretStore/my-store: {} diff --git a/resource_customizations/external-secrets.io/PushSecret/testdata/healthy.yaml b/resource_customizations/external-secrets.io/PushSecret/testdata/healthy.yaml new file mode 100644 index 0000000000000..487233a773e95 --- /dev/null +++ b/resource_customizations/external-secrets.io/PushSecret/testdata/healthy.yaml @@ -0,0 +1,39 @@ +apiVersion: external-secrets.io/v1alpha1 +kind: PushSecret +metadata: + creationTimestamp: '2023-07-05T20:49:16Z' + generation: 1 + name: test-healthy + namespace: external-secret + resourceVersion: '777692391' + uid: 88cb613a-07b0-4fb2-8fdb-d5a5a9c2c917 +spec: + data: + - match: + remoteRef: + property: test + remoteKey: remote/path + secretKey: test + deletionPolicy: None + refreshInterval: 5m + secretStoreRefs: + - kind: ClusterSecretStore + name: my-store + selector: + secret: + name: existing-secret +status: + conditions: + - lastTransitionTime: '2023-07-05T20:49:16Z' + message: PushSecret synced successfully + reason: Synced + status: 'True' + type: Ready + syncedPushSecrets: + ClusterSecretStore/my-store: + remote/path/test: + match: + remoteRef: + property: test + remoteKey: remote/path + secretKey: test diff --git a/resource_customizations/external-secrets.io/PushSecret/testdata/progressing.yaml b/resource_customizations/external-secrets.io/PushSecret/testdata/progressing.yaml new file mode 100644 index 0000000000000..e67d679bae123 --- /dev/null +++ b/resource_customizations/external-secrets.io/PushSecret/testdata/progressing.yaml @@ -0,0 +1,24 @@ +apiVersion: external-secrets.io/v1alpha1 +kind: PushSecret +metadata: + creationTimestamp: '2023-07-05T20:49:16Z' + generation: 1 + name: test-progressing + namespace: external-secret + resourceVersion: '777692391' + uid: 88cb613a-07b0-4fb2-8fdb-d5a5a9c2c917 +spec: + data: + - match: + remoteRef: + property: test + remoteKey: remote/path + secretKey: test + deletionPolicy: None + refreshInterval: 5m + secretStoreRefs: + - kind: ClusterSecretStore + name: my-store + selector: + secret: + name: existing-secret diff --git a/resource_customizations/external-secrets.io/SecretStore/health.lua b/resource_customizations/external-secrets.io/SecretStore/health.lua index 5c58908ac6e9f..656c8a3146138 100644 --- a/resource_customizations/external-secrets.io/SecretStore/health.lua +++ b/resource_customizations/external-secrets.io/SecretStore/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do diff --git a/resource_customizations/flagger.app/Canary/health.lua b/resource_customizations/flagger.app/Canary/health.lua index 86872556ed4b5..dd32bbc133e0f 100644 --- a/resource_customizations/flagger.app/Canary/health.lua +++ b/resource_customizations/flagger.app/Canary/health.lua @@ -1,7 +1,7 @@ -sep = " --- " -hs = {} +local sep = " --- " +local hs = {} if obj.status ~= nil then - message = "" + local message = "" if tonumber(obj.status.canaryWeight) > 0 then message = "Canary Weight: " .. obj.status.canaryWeight .. " %" end diff --git a/resource_customizations/flink.apache.org/FlinkDeployment/health.lua b/resource_customizations/flink.apache.org/FlinkDeployment/health.lua index 64c0782209791..677f1a7b87049 100644 --- a/resource_customizations/flink.apache.org/FlinkDeployment/health.lua +++ b/resource_customizations/flink.apache.org/FlinkDeployment/health.lua @@ -1,7 +1,7 @@ -health_status = {} +local health_status = {} if obj.status ~= nil and obj.status.reconciliationStatus ~= nil then - if obj.status.reconciliationStatus.success then + if obj.status.reconciliationStatus.success or obj.status.reconciliationStatus.state == "DEPLOYED" then health_status.status = "Healthy" return health_status end diff --git a/resource_customizations/flink.apache.org/FlinkDeployment/health_test.yaml b/resource_customizations/flink.apache.org/FlinkDeployment/health_test.yaml index 1e8cb55b72769..25a4f1273a5d3 100644 --- a/resource_customizations/flink.apache.org/FlinkDeployment/health_test.yaml +++ b/resource_customizations/flink.apache.org/FlinkDeployment/health_test.yaml @@ -1,10 +1,16 @@ tests: - healthStatus: status: Healthy - inputPath: testdata/healthy_running.yaml + inputPath: testdata/healthy_running_v0.1.x.yaml - healthStatus: status: Healthy - inputPath: testdata/healthy_suspended.yaml + inputPath: testdata/healthy_running_v1.x.yaml +- healthStatus: + status: Healthy + inputPath: testdata/healthy_suspended_v0.1.x.yaml +- healthStatus: + status: Healthy + inputPath: testdata/healthy_suspended_v1.x.yaml - healthStatus: status: Progressing message: Waiting for deploying diff --git a/resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_running.yaml b/resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_running_v0.1.x.yaml similarity index 100% rename from resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_running.yaml rename to resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_running_v0.1.x.yaml diff --git a/resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_running_v1.x.yaml b/resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_running_v1.x.yaml new file mode 100644 index 0000000000000..60e2c2bfa26fc --- /dev/null +++ b/resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_running_v1.x.yaml @@ -0,0 +1,11 @@ +apiVersion: flink.apache.org/v1alpha1 +kind: FlinkDeployment +spec: + job: + state: running +status: + jobManagerDeploymentStatus: READY + jobStatus: + state: RUNNING + reconciliationStatus: + state: DEPLOYED diff --git a/resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_suspended.yaml b/resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_suspended_v0.1.x.yaml similarity index 100% rename from resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_suspended.yaml rename to resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_suspended_v0.1.x.yaml diff --git a/resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_suspended_v1.x.yaml b/resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_suspended_v1.x.yaml new file mode 100644 index 0000000000000..023c2899fb60d --- /dev/null +++ b/resource_customizations/flink.apache.org/FlinkDeployment/testdata/healthy_suspended_v1.x.yaml @@ -0,0 +1,11 @@ +apiVersion: flink.apache.org/v1alpha1 +kind: FlinkDeployment +spec: + job: + state: suspended +status: + jobManagerDeploymentStatus: MISSING + jobStatus: + state: SUSPENDED + reconciliationStatus: + state: DEPLOYED diff --git a/resource_customizations/iam.cnrm.cloud.google.com/IAMPartialPolicy/health.lua b/resource_customizations/iam.cnrm.cloud.google.com/IAMPartialPolicy/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/iam.cnrm.cloud.google.com/IAMPartialPolicy/health.lua +++ b/resource_customizations/iam.cnrm.cloud.google.com/IAMPartialPolicy/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicy/health.lua b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicy/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicy/health.lua +++ b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicy/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/health.lua b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/health.lua +++ b/resource_customizations/iam.cnrm.cloud.google.com/IAMPolicyMember/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/iam.cnrm.cloud.google.com/IAMServiceAccount/health.lua b/resource_customizations/iam.cnrm.cloud.google.com/IAMServiceAccount/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/iam.cnrm.cloud.google.com/IAMServiceAccount/health.lua +++ b/resource_customizations/iam.cnrm.cloud.google.com/IAMServiceAccount/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/iammanager.keikoproj.io/Iamrole/health.lua b/resource_customizations/iammanager.keikoproj.io/Iamrole/health.lua new file mode 100644 index 0000000000000..0b89687e08637 --- /dev/null +++ b/resource_customizations/iammanager.keikoproj.io/Iamrole/health.lua @@ -0,0 +1,33 @@ +local hs = {} +if obj.status ~= nil then + -- Each message may or may not use these. + local roleName = obj.status.roleName or "" + local roleARN = obj.status.roleARN or "" + local roleID = obj.status.roleID or "" + + if obj.status.state == "Ready" then + hs.status = "Healthy" + hs.message = "Role '" .. roleName .. "' exists with ARN '" .. roleARN .. "' and ID '" .. roleID .. "'." + return hs + end + + local message = "" + -- Current non-ready statuses: https://github.com/keikoproj/iam-manager/blob/3aeb2f8ec3005e1c53a057b3b0f79e14a0e5b9cb/api/v1alpha1/iamrole_types.go#L150-L156 + if obj.status.state == "Error" or obj.status.state == "RolesMaxLimitReached" or obj.status.state == "PolicyNotAllowed" or obj.status.state == "RoleNameNotAvailable" then + hs.status = "Degraded" + message = "Failed to reconcile the Iamrole " + if obj.status.retryCount ~= nil and obj.status.retryCount > 0 then + message = message .. "(retry " .. tostring(obj.status.retryCount) .. ") " + end + message = message .. "for role '" .. roleName .. "' with ARN '" .. roleARN .. "' and ID '" .. roleID .. "'." + if obj.status.errorDescription ~= nil then + message = message .. " Reconciliation error was: " .. obj.status.errorDescription + end + hs.message = message + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for Iamrole to be reconciled" +return hs diff --git a/resource_customizations/iammanager.keikoproj.io/Iamrole/health_test.yaml b/resource_customizations/iammanager.keikoproj.io/Iamrole/health_test.yaml new file mode 100644 index 0000000000000..660276f41f475 --- /dev/null +++ b/resource_customizations/iammanager.keikoproj.io/Iamrole/health_test.yaml @@ -0,0 +1,20 @@ +tests: +- healthStatus: + status: Degraded + message: |- + Failed to reconcile the Iamrole (retry 1) for role 'k8s-test' with ARN 'arn:aws:iam::111111111111:role/k8s-test' and ID 'ABCDEFGHIJKLMNOPQRSTU'. Reconciliation error was: NoSuchEntity: The role with name k8s-test cannot be found. + status code: 404, request id: f80c99fc-c78d-4b1c-806d-3a162fbbc900 + inputPath: testdata/degraded_error.yaml +- healthStatus: + status: Degraded + message: |- + Failed to reconcile the Iamrole for role 'k8s-test' with ARN '' and ID ''. Reconciliation error was: maximum number of allowed roles reached. You must delete any existing role before proceeding further + inputPath: testdata/degraded_rolesMaxLimitReached.yaml +- healthStatus: + status: Healthy + message: Role 'k8s-test' exists with ARN 'arn:aws:iam::111111111111:role/k8s-test' and ID 'ABCDEFGHIJKLMNOPQRSTU'. + inputPath: testdata/healthy.yaml +- healthStatus: + status: Progressing + message: 'Waiting for Iamrole to be reconciled' + inputPath: testdata/progressing_noStatus.yaml diff --git a/resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/degraded_error.yaml b/resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/degraded_error.yaml new file mode 100644 index 0000000000000..3bf3e7cee2b85 --- /dev/null +++ b/resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/degraded_error.yaml @@ -0,0 +1,29 @@ +apiVersion: iammanager.keikoproj.io/v1alpha1 +kind: Iamrole +metadata: + finalizers: + - iamrole.finalizers.iammanager.keikoproj.io + name: iamrole + namespace: test +spec: + PolicyDocument: + Statement: + - Action: + - ec2:* + Effect: Deny + Resource: + - '*' + - Action: + - iam:* + Effect: Deny + Resource: + - '*' +status: + errorDescription: "NoSuchEntity: The role with name k8s-test cannot + be found.\n\tstatus code: 404, request id: f80c99fc-c78d-4b1c-806d-3a162fbbc900" + lastUpdatedTimestamp: "2023-10-10T19:31:06Z" + retryCount: 1 + roleARN: arn:aws:iam::111111111111:role/k8s-test + roleID: ABCDEFGHIJKLMNOPQRSTU + roleName: k8s-test + state: Error diff --git a/resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/degraded_rolesMaxLimitReached.yaml b/resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/degraded_rolesMaxLimitReached.yaml new file mode 100644 index 0000000000000..906c72083bede --- /dev/null +++ b/resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/degraded_rolesMaxLimitReached.yaml @@ -0,0 +1,26 @@ +apiVersion: iammanager.keikoproj.io/v1alpha1 +kind: Iamrole +metadata: + finalizers: + - iamrole.finalizers.iammanager.keikoproj.io + name: iamrole + namespace: test +spec: + PolicyDocument: + Statement: + - Action: + - ec2:* + Effect: Deny + Resource: + - '*' + - Action: + - iam:* + Effect: Deny + Resource: + - '*' +status: + errorDescription: maximum number of allowed roles reached. You must delete any existing role before proceeding further + lastUpdatedTimestamp: "2023-10-10T19:25:26Z" + retryCount: 0 + roleName: k8s-test + state: RolesMaxLimitReached diff --git a/resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/healthy.yaml b/resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/healthy.yaml new file mode 100644 index 0000000000000..273cf3a571234 --- /dev/null +++ b/resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/healthy.yaml @@ -0,0 +1,27 @@ +apiVersion: iammanager.keikoproj.io/v1alpha1 +kind: Iamrole +metadata: + finalizers: + - iamrole.finalizers.iammanager.keikoproj.io + name: iamrole + namespace: default +spec: + PolicyDocument: + Statement: + - Action: + - 'ec2:*' + Effect: Deny + Resource: + - '*' + - Action: + - 'iam:*' + Effect: Deny + Resource: + - '*' +status: + lastUpdatedTimestamp: '2023-10-10T20:36:23Z' + retryCount: 0 + roleARN: 'arn:aws:iam::111111111111:role/k8s-test' + roleID: ABCDEFGHIJKLMNOPQRSTU + roleName: k8s-test + state: Ready diff --git a/resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/progressing_noStatus.yaml b/resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/progressing_noStatus.yaml new file mode 100644 index 0000000000000..29c18a4c0380a --- /dev/null +++ b/resource_customizations/iammanager.keikoproj.io/Iamrole/testdata/progressing_noStatus.yaml @@ -0,0 +1,20 @@ +apiVersion: iammanager.keikoproj.io/v1alpha1 +kind: Iamrole +metadata: + finalizers: + - iamrole.finalizers.iammanager.keikoproj.io + name: iamrole + namespace: default +spec: + PolicyDocument: + Statement: + - Action: + - 'ec2:*' + Effect: Deny + Resource: + - '*' + - Action: + - 'iam:*' + Effect: Deny + Resource: + - '*' diff --git a/resource_customizations/install.istio.io/IstioOperator/health.lua b/resource_customizations/install.istio.io/IstioOperator/health.lua index e2538dec24d57..874ff3cdd3351 100644 --- a/resource_customizations/install.istio.io/IstioOperator/health.lua +++ b/resource_customizations/install.istio.io/IstioOperator/health.lua @@ -1,4 +1,4 @@ -health_status = {} +local health_status = {} if obj.status ~= nil then if obj.status.status ~= nil then if obj.status.status == 0 or obj.status.status == "NONE" then diff --git a/resource_customizations/jaegertracing.io/Jaeger/health.lua b/resource_customizations/jaegertracing.io/Jaeger/health.lua index 71f10438ce9fe..b7514d53065db 100644 --- a/resource_customizations/jaegertracing.io/Jaeger/health.lua +++ b/resource_customizations/jaegertracing.io/Jaeger/health.lua @@ -1,4 +1,4 @@ -health_status = {} +local health_status = {} if obj.status ~= nil then if obj.status.phase == "Running" then health_status.status = "Healthy" diff --git a/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health.lua b/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health.lua index 06082dae24425..7422fd4104727 100644 --- a/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health.lua +++ b/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health.lua @@ -1,22 +1,17 @@ -health_status = {} +local health_status = {} if obj.status ~= nil then if obj.status.brokersState ~= nil then - counter = 0 - brokerReady = 0 - for i, broker in pairs(obj.status.brokersState) do - if (brokerReady <= tonumber(i)) then - brokerReady = tonumber(i)+1 - else - brokerReady = brokerReady - end - if broker.configurationState == "ConfigInSync" and broker.gracefulActionState.cruiseControlState == "GracefulUpscaleSucceeded" then - counter = counter + 1 - end - if broker.configurationState == "ConfigInSync" and broker.gracefulActionState.cruiseControlState == "GracefulDownscaleSucceeded" then - counter = counter + 1 + local numberBrokers = 0 + local healthyBrokers = 0 + for _, broker in pairs(obj.status.brokersState) do + numberBrokers = numberBrokers + 1 + if broker.configurationState == "ConfigInSync" then + if broker.gracefulActionState.cruiseControlState == "GracefulUpscaleSucceeded" or broker.gracefulActionState.cruiseControlState == "GracefulDownscaleSucceeded" then + healthyBrokers = healthyBrokers + 1 end + end end - if counter == brokerReady then + if numberBrokers == healthyBrokers then if obj.status.cruiseControlTopicStatus == "CruiseControlTopicReady" and obj.status.state == "ClusterRunning" then health_status.message = "Kafka Brokers, CruiseControl and cluster are in Healthy State." health_status.status = "Healthy" diff --git a/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health_test.yaml b/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health_test.yaml index 9446d882d941a..776cc02739326 100644 --- a/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health_test.yaml +++ b/resource_customizations/kafka.banzaicloud.io/KafkaCluster/health_test.yaml @@ -14,4 +14,4 @@ tests: - healthStatus: status: Healthy message: "Kafka Brokers, CruiseControl and cluster are in Healthy State." - inputPath: testdata/healthy.yaml + inputPath: testdata/healthy.yaml \ No newline at end of file diff --git a/resource_customizations/kafka.banzaicloud.io/KafkaCluster/testdata/healthy.yaml b/resource_customizations/kafka.banzaicloud.io/KafkaCluster/testdata/healthy.yaml index 9dd791b9c39fe..44666fd6a83a5 100644 --- a/resource_customizations/kafka.banzaicloud.io/KafkaCluster/testdata/healthy.yaml +++ b/resource_customizations/kafka.banzaicloud.io/KafkaCluster/testdata/healthy.yaml @@ -20,21 +20,21 @@ spec: {} status: alertCount: 0 brokersState: - "0": + "101": configurationState: ConfigInSync gracefulActionState: cruiseControlState: GracefulUpscaleSucceeded errorMessage: CruiseControl not yet ready rackAwarenessState: | broker.rack=us-east-1,us-east-1c - "1": + "102": configurationState: ConfigInSync gracefulActionState: cruiseControlState: GracefulUpscaleSucceeded errorMessage: CruiseControl not yet ready rackAwarenessState: | broker.rack=us-east-1,us-east-1b - "2": + "103": configurationState: ConfigInSync gracefulActionState: cruiseControlState: GracefulUpscaleSucceeded diff --git a/resource_customizations/kafka.strimzi.io/Kafka/health.lua b/resource_customizations/kafka.strimzi.io/Kafka/health.lua index 759f94a2cdcf8..346a18114f84d 100644 --- a/resource_customizations/kafka.strimzi.io/Kafka/health.lua +++ b/resource_customizations/kafka.strimzi.io/Kafka/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do diff --git a/resource_customizations/kafka.strimzi.io/Kafka/health_test.yaml b/resource_customizations/kafka.strimzi.io/Kafka/health_test.yaml index 9c7d7bc31fe45..0038036e84a47 100644 --- a/resource_customizations/kafka.strimzi.io/Kafka/health_test.yaml +++ b/resource_customizations/kafka.strimzi.io/Kafka/health_test.yaml @@ -5,7 +5,6 @@ tests: inputPath: testdata/progressing_noStatus.yaml - healthStatus: status: Degraded - message: "Error" message: "Exceeded timeout of 300000ms while waiting for StatefulSet resource my-cluster-zookeeper in namespace default to be ready" inputPath: testdata/degraded.yaml - healthStatus: diff --git a/resource_customizations/kafka.strimzi.io/KafkaConnect/health.lua b/resource_customizations/kafka.strimzi.io/KafkaConnect/health.lua index f12158def6798..8cd15dd14c6e7 100644 --- a/resource_customizations/kafka.strimzi.io/KafkaConnect/health.lua +++ b/resource_customizations/kafka.strimzi.io/KafkaConnect/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do diff --git a/resource_customizations/kafka.strimzi.io/KafkaConnect/testdata/degraded.yaml b/resource_customizations/kafka.strimzi.io/KafkaConnect/testdata/degraded.yaml index 14294958aa2c8..b7c9d15cd3dbb 100644 --- a/resource_customizations/kafka.strimzi.io/KafkaConnect/testdata/degraded.yaml +++ b/resource_customizations/kafka.strimzi.io/KafkaConnect/testdata/degraded.yaml @@ -81,7 +81,7 @@ spec: operator: In values: - kafka-connect - topologyKey: failure-domain.beta.kubernetes.io/zone + topologyKey: topology.kubernetes.io/zone weight: 100 metadata: annotations: diff --git a/resource_customizations/kafka.strimzi.io/KafkaConnect/testdata/healthy.yaml b/resource_customizations/kafka.strimzi.io/KafkaConnect/testdata/healthy.yaml index 7197050e30f35..1d03d3b5c95c8 100644 --- a/resource_customizations/kafka.strimzi.io/KafkaConnect/testdata/healthy.yaml +++ b/resource_customizations/kafka.strimzi.io/KafkaConnect/testdata/healthy.yaml @@ -81,7 +81,7 @@ spec: operator: In values: - kafka-connect - topologyKey: failure-domain.beta.kubernetes.io/zone + topologyKey: topology.kubernetes.io/zone weight: 100 metadata: annotations: diff --git a/resource_customizations/kafka.strimzi.io/KafkaConnect/testdata/progressing_noStatus.yaml b/resource_customizations/kafka.strimzi.io/KafkaConnect/testdata/progressing_noStatus.yaml index 7c1c7255f158a..7f218527ff13f 100644 --- a/resource_customizations/kafka.strimzi.io/KafkaConnect/testdata/progressing_noStatus.yaml +++ b/resource_customizations/kafka.strimzi.io/KafkaConnect/testdata/progressing_noStatus.yaml @@ -81,7 +81,7 @@ spec: operator: In values: - kafka-connect - topologyKey: failure-domain.beta.kubernetes.io/zone + topologyKey: topology.kubernetes.io/zone weight: 100 metadata: annotations: @@ -93,4 +93,4 @@ spec: - effect: NoSchedule key: dynamic-node operator: Equal - value: "true" \ No newline at end of file + value: "true" diff --git a/resource_customizations/kafka.strimzi.io/KafkaTopic/health.lua b/resource_customizations/kafka.strimzi.io/KafkaTopic/health.lua index 36509c7614f66..2d3ada3eac1e9 100644 --- a/resource_customizations/kafka.strimzi.io/KafkaTopic/health.lua +++ b/resource_customizations/kafka.strimzi.io/KafkaTopic/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do diff --git a/resource_customizations/kafka.strimzi.io/KafkaUser/health.lua b/resource_customizations/kafka.strimzi.io/KafkaUser/health.lua index ad25d41497027..44172d1e38105 100644 --- a/resource_customizations/kafka.strimzi.io/KafkaUser/health.lua +++ b/resource_customizations/kafka.strimzi.io/KafkaUser/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do diff --git a/resource_customizations/kiali.io/Kiali/health.lua b/resource_customizations/kiali.io/Kiali/health.lua index 6820b121a0f68..ea066cf88882f 100644 --- a/resource_customizations/kiali.io/Kiali/health.lua +++ b/resource_customizations/kiali.io/Kiali/health.lua @@ -1,4 +1,4 @@ -health_status = {} +local health_status = {} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do diff --git a/resource_customizations/kubernetes-client.io/ExternalSecret/health.lua b/resource_customizations/kubernetes-client.io/ExternalSecret/health.lua index 697a0b91ee60d..8e4109158e94f 100644 --- a/resource_customizations/kubernetes-client.io/ExternalSecret/health.lua +++ b/resource_customizations/kubernetes-client.io/ExternalSecret/health.lua @@ -1,4 +1,4 @@ -health_status = {} +local health_status = {} if obj.status ~= nil then if obj.status.status == "SUCCESS" then health_status.status = "Healthy" diff --git a/resource_customizations/kubevirt.io/VirtualMachine/health.lua b/resource_customizations/kubevirt.io/VirtualMachine/health.lua index d9e0ebdd96e51..6c0d63348b766 100644 --- a/resource_customizations/kubevirt.io/VirtualMachine/health.lua +++ b/resource_customizations/kubevirt.io/VirtualMachine/health.lua @@ -1,4 +1,4 @@ -hs = { status="Progressing", message="No status available"} +local hs = { status="Progressing", message="No status available"} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do @@ -13,7 +13,7 @@ if obj.status ~= nil then hs.message="Running" else if obj.status.created then - hs.message = "Starting" + hs.message = "Starting" else hs.status = "Suspended" hs.message = "Stopped" diff --git a/resource_customizations/kubevirt.io/VirtualMachineInstance/health.lua b/resource_customizations/kubevirt.io/VirtualMachineInstance/health.lua index 30264cf007c6d..27352077786f7 100644 --- a/resource_customizations/kubevirt.io/VirtualMachineInstance/health.lua +++ b/resource_customizations/kubevirt.io/VirtualMachineInstance/health.lua @@ -1,4 +1,4 @@ -hs = { status="Progressing", message="No status available"} +local hs = { status="Progressing", message="No status available"} if obj.status ~= nil then if obj.status.phase ~= nil then hs.message = obj.status.phase diff --git a/resource_customizations/lifecycle.keptn.sh/KeptnAppVersion/health.lua b/resource_customizations/lifecycle.keptn.sh/KeptnAppVersion/health.lua index daf3168907672..d99bf033981eb 100644 --- a/resource_customizations/lifecycle.keptn.sh/KeptnAppVersion/health.lua +++ b/resource_customizations/lifecycle.keptn.sh/KeptnAppVersion/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status.status == "Succeeded" then hs.status = "Healthy" hs.message = "KeptnAppVersion is healthy" diff --git a/resource_customizations/lifecycle.keptn.sh/KeptnEvaluation/health.lua b/resource_customizations/lifecycle.keptn.sh/KeptnEvaluation/health.lua index 2ea65e96736f2..97543ecc2b999 100644 --- a/resource_customizations/lifecycle.keptn.sh/KeptnEvaluation/health.lua +++ b/resource_customizations/lifecycle.keptn.sh/KeptnEvaluation/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status.overallStatus == "Succeeded" then hs.status = "Healthy" hs.message = "KeptnEvaluation is healthy" diff --git a/resource_customizations/lifecycle.keptn.sh/KeptnTask/health.lua b/resource_customizations/lifecycle.keptn.sh/KeptnTask/health.lua index e22de12347391..d8c6047679195 100644 --- a/resource_customizations/lifecycle.keptn.sh/KeptnTask/health.lua +++ b/resource_customizations/lifecycle.keptn.sh/KeptnTask/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status.status == "Succeeded" then hs.status = "Healthy" hs.message = "KeptnTask is healthy" diff --git a/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadInstance/health.lua b/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadInstance/health.lua index 64002013a273f..5ce79d0448779 100644 --- a/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadInstance/health.lua +++ b/resource_customizations/lifecycle.keptn.sh/KeptnWorkloadInstance/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status.status == "Succeeded" then hs.status = "Healthy" hs.message = "KeptnWorkloadInstance is healthy" diff --git a/resource_customizations/mariadb.mmontes.io/MariaDB/health.lua b/resource_customizations/mariadb.mmontes.io/MariaDB/health.lua new file mode 100644 index 0000000000000..b0278bb22650e --- /dev/null +++ b/resource_customizations/mariadb.mmontes.io/MariaDB/health.lua @@ -0,0 +1,25 @@ +local health_status = {} + +if obj.status ~= nil and obj.status.conditions ~= nil then + + for i, condition in ipairs(obj.status.conditions) do + + health_status.message = condition.message + + if condition.status == "False" then + if condition.reason == "Failed" then + health_status.status = "Degraded" + return health_status + end + health_status.status = "Progressing" + return health_status + end + end + + health_status.status = "Healthy" + return health_status +end + +health_status.status = "Progressing" +health_status.message = "No status info available" +return health_status diff --git a/resource_customizations/mariadb.mmontes.io/MariaDB/health_test.yaml b/resource_customizations/mariadb.mmontes.io/MariaDB/health_test.yaml new file mode 100644 index 0000000000000..f3dba1ac80c58 --- /dev/null +++ b/resource_customizations/mariadb.mmontes.io/MariaDB/health_test.yaml @@ -0,0 +1,25 @@ +tests: +- healthStatus: + status: Progressing + message: "No status info available" + inputPath: testdata/no_status.yaml +- healthStatus: + status: Healthy + message: "Running" + inputPath: testdata/statefulset_ready.yaml +- healthStatus: + status: Progressing + message: "Not ready" + inputPath: testdata/statefulset_not_ready.yaml +- healthStatus: + status: Healthy + message: "Running" + inputPath: testdata/restore_complete.yaml +- healthStatus: + status: Progressing + message: "Restoring backup" + inputPath: testdata/restore_not_complete.yaml +- healthStatus: + status: Degraded + message: "Error creating ConfigMap" + inputPath: testdata/mariadb_error.yaml diff --git a/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/mariadb_error.yaml b/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/mariadb_error.yaml new file mode 100644 index 0000000000000..030391dda7acc --- /dev/null +++ b/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/mariadb_error.yaml @@ -0,0 +1,27 @@ +apiVersion: mariadb.mmontes.io/v1alpha1 +kind: MariaDB +metadata: + name: mariadb-server +spec: + rootPasswordSecretKeyRef: + name: mariadb + key: root-password + image: + repository: mariadb + tag: "10.7.4" + pullPolicy: IfNotPresent + port: 3306 + volumeClaimTemplate: + resources: + requests: + storage: 100Mi + storageClassName: standard + accessModes: + - ReadWriteOnce +status: + conditions: + - lastTransitionTime: '2023-04-20T15:31:15Z' + message: Error creating ConfigMap + reason: Failed + status: 'False' + type: Ready diff --git a/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/no_status.yaml b/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/no_status.yaml new file mode 100644 index 0000000000000..276d09b04c272 --- /dev/null +++ b/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/no_status.yaml @@ -0,0 +1,22 @@ +apiVersion: mariadb.mmontes.io/v1alpha1 +kind: MariaDB +metadata: + name: mariadb-server +spec: + rootPasswordSecretKeyRef: + name: mariadb + key: root-password + image: + repository: mariadb + tag: "10.7.4" + pullPolicy: IfNotPresent + port: 3306 + volumeClaimTemplate: + resources: + requests: + storage: 100Mi + storageClassName: standard + accessModes: + - ReadWriteOnce +status: + revision: 0 diff --git a/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/restore_complete.yaml b/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/restore_complete.yaml new file mode 100644 index 0000000000000..7a9358c0f16d6 --- /dev/null +++ b/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/restore_complete.yaml @@ -0,0 +1,32 @@ +apiVersion: mariadb.mmontes.io/v1alpha1 +kind: MariaDB +metadata: + name: mariadb-server +spec: + rootPasswordSecretKeyRef: + name: mariadb + key: root-password + image: + repository: mariadb + tag: "10.7.4" + pullPolicy: IfNotPresent + port: 3306 + volumeClaimTemplate: + resources: + requests: + storage: 100Mi + storageClassName: standard + accessModes: + - ReadWriteOnce +status: + conditions: + - lastTransitionTime: "2023-04-05T14:18:01Z" + message: Ready + reason: RestoreComplete + status: "True" + type: Bootstrapped + - lastTransitionTime: "2023-04-05T14:18:02Z" + message: Running + reason: RestoreComplete + status: "True" + type: Ready diff --git a/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/restore_not_complete.yaml b/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/restore_not_complete.yaml new file mode 100644 index 0000000000000..a3f600eae96a2 --- /dev/null +++ b/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/restore_not_complete.yaml @@ -0,0 +1,32 @@ +apiVersion: mariadb.mmontes.io/v1alpha1 +kind: MariaDB +metadata: + name: mariadb-server +spec: + rootPasswordSecretKeyRef: + name: mariadb + key: root-password + image: + repository: mariadb + tag: "10.7.4" + pullPolicy: IfNotPresent + port: 3306 + volumeClaimTemplate: + resources: + requests: + storage: 100Mi + storageClassName: standard + accessModes: + - ReadWriteOnce +status: + conditions: + - lastTransitionTime: "2023-04-05T14:18:01Z" + message: Restoring backup + reason: RestoreNotComplete + status: "False" + type: Ready + - lastTransitionTime: "2023-04-05T14:18:02Z" + message: Not ready + reason: RestoreNotComplete + status: "False" + type: Bootstrapped diff --git a/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/statefulset_not_ready.yaml b/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/statefulset_not_ready.yaml new file mode 100644 index 0000000000000..96d46b4bf61fe --- /dev/null +++ b/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/statefulset_not_ready.yaml @@ -0,0 +1,27 @@ +apiVersion: mariadb.mmontes.io/v1alpha1 +kind: MariaDB +metadata: + name: mariadb-server +spec: + rootPasswordSecretKeyRef: + name: mariadb + key: root-password + image: + repository: mariadb + tag: "10.7.4" + pullPolicy: IfNotPresent + port: 3306 + volumeClaimTemplate: + resources: + requests: + storage: 100Mi + storageClassName: standard + accessModes: + - ReadWriteOnce +status: + conditions: + - lastTransitionTime: "2023-04-05T14:18:01Z" + message: Not ready + reason: StatefulSetNotReady + status: "False" + type: Ready diff --git a/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/statefulset_ready.yaml b/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/statefulset_ready.yaml new file mode 100644 index 0000000000000..e4870b0064dbb --- /dev/null +++ b/resource_customizations/mariadb.mmontes.io/MariaDB/testdata/statefulset_ready.yaml @@ -0,0 +1,27 @@ +apiVersion: mariadb.mmontes.io/v1alpha1 +kind: MariaDB +metadata: + name: mariadb-server +spec: + rootPasswordSecretKeyRef: + name: mariadb + key: root-password + image: + repository: mariadb + tag: "10.7.4" + pullPolicy: IfNotPresent + port: 3306 + volumeClaimTemplate: + resources: + requests: + storage: 100Mi + storageClassName: standard + accessModes: + - ReadWriteOnce +status: + conditions: + - lastTransitionTime: "2023-04-05T14:18:01Z" + message: Running + reason: StatefulSetReady + status: "True" + type: Ready diff --git a/resource_customizations/minio.min.io/Tenant/health.lua b/resource_customizations/minio.min.io/Tenant/health.lua index 088d70a5ecf4f..a62e3d2670a19 100644 --- a/resource_customizations/minio.min.io/Tenant/health.lua +++ b/resource_customizations/minio.min.io/Tenant/health.lua @@ -1,4 +1,4 @@ -health_status = {} +local health_status = {} if obj.status ~= nil then if obj.status.currentState ~= nil then if obj.status.currentState == "Initialized" then diff --git a/resource_customizations/monitoring.coreos.com/Prometheus/health.lua b/resource_customizations/monitoring.coreos.com/Prometheus/health.lua index 34fddd1393970..b0b052cf93673 100644 --- a/resource_customizations/monitoring.coreos.com/Prometheus/health.lua +++ b/resource_customizations/monitoring.coreos.com/Prometheus/health.lua @@ -1,4 +1,4 @@ -hs={ status = "Progressing", message = "Waiting for initialization" } +local hs={ status = "Progressing", message = "Waiting for initialization" } if obj.status ~= nil then if obj.status.conditions ~= nil then diff --git a/resource_customizations/networking.gke.io/ManagedCertificate/health.lua b/resource_customizations/networking.gke.io/ManagedCertificate/health.lua index 9f0f3e127f3f0..b46263f187a27 100644 --- a/resource_customizations/networking.gke.io/ManagedCertificate/health.lua +++ b/resource_customizations/networking.gke.io/ManagedCertificate/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.domainStatus ~= nil then diff --git a/resource_customizations/onepassword.com/OnePasswordItem/health.lua b/resource_customizations/onepassword.com/OnePasswordItem/health.lua index ee46c99782136..bce3f068ab9b9 100644 --- a/resource_customizations/onepassword.com/OnePasswordItem/health.lua +++ b/resource_customizations/onepassword.com/OnePasswordItem/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then for i, condition in ipairs(obj.status.conditions) do diff --git a/resource_customizations/operator.knative.dev/KnativeEventing/health.lua b/resource_customizations/operator.knative.dev/KnativeEventing/health.lua index f9cb10370ba9a..aadf6cac3f8a8 100644 --- a/resource_customizations/operator.knative.dev/KnativeEventing/health.lua +++ b/resource_customizations/operator.knative.dev/KnativeEventing/health.lua @@ -1,9 +1,9 @@ -health_status = {} +local health_status = {} if obj.status ~= nil then if obj.status.conditions ~= nil then - numTrue = 0 - numFalse = 0 - msg = "" + local numTrue = 0 + local numFalse = 0 + local msg = "" for i, condition in pairs(obj.status.conditions) do msg = msg .. i .. ": " .. condition.type .. " | " .. condition.status .. "\n" if condition.type == "Ready" and condition.status == "True" then diff --git a/resource_customizations/operator.knative.dev/KnativeServing/health.lua b/resource_customizations/operator.knative.dev/KnativeServing/health.lua index 9903f98ee3788..e412a159634ca 100644 --- a/resource_customizations/operator.knative.dev/KnativeServing/health.lua +++ b/resource_customizations/operator.knative.dev/KnativeServing/health.lua @@ -1,9 +1,9 @@ -health_status = {} +local health_status = {} if obj.status ~= nil then if obj.status.conditions ~= nil then - numTrue = 0 - numFalse = 0 - msg = "" + local numTrue = 0 + local numFalse = 0 + local msg = "" for i, condition in pairs(obj.status.conditions) do msg = msg .. i .. ": " .. condition.type .. " | " .. condition.status .. "\n" if condition.type == "Ready" and condition.status == "True" then diff --git a/resource_customizations/operator.openshift.io/IngressController/health.lua b/resource_customizations/operator.openshift.io/IngressController/health.lua new file mode 100644 index 0000000000000..837a82e980fb0 --- /dev/null +++ b/resource_customizations/operator.openshift.io/IngressController/health.lua @@ -0,0 +1,31 @@ +-- healthcheck for IngressController resources +local hs = {} +if obj.status ~= nil then + if obj.status.conditions ~= nil then + -- if the status conditions are present, iterate over them and check their status + for _, condition in pairs(obj.status.conditions) do + if condition.type == "Degraded" and condition.status == "True" then + hs.status = "Degraded" + hs.message = condition.message + return hs + elseif condition.type == "DeploymentReplicasAllAvailable" and condition.status == "False" then + hs.status = "Progressing" + hs.message = condition.message + return hs + elseif condition.type == "Progressing" and condition.status == "True" then + hs.status = "Progressing" + hs.message = condition.reason + return hs + elseif condition.type == "Available" and condition.status == "True" then + hs.status = "Healthy" + hs.message = "IngressController is available" + return hs + end + end + end +end + +-- default status when none of the previous condition matches +hs.status = "Progressing" +hs.message = "Status of IngressController is not known yet" +return hs diff --git a/resource_customizations/operator.openshift.io/IngressController/health_test.yaml b/resource_customizations/operator.openshift.io/IngressController/health_test.yaml new file mode 100644 index 0000000000000..761d0d6e8fac1 --- /dev/null +++ b/resource_customizations/operator.openshift.io/IngressController/health_test.yaml @@ -0,0 +1,17 @@ +tests: +- healthStatus: + status: Progressing + message: "Status of IngressController is not known yet" + inputPath: testdata/progressing_initialization.yaml +- healthStatus: + status: Progressing + message: "0/1 of replicas are available" + inputPath: testdata/progressing_pod_rollout.yaml +- healthStatus: + status: Degraded + message: "One or more other status conditions indicate a degraded state." + inputPath: testdata/degraded.yaml +- healthStatus: + status: Healthy + message: "IngressController is available" + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/operator.openshift.io/IngressController/testdata/degraded.yaml b/resource_customizations/operator.openshift.io/IngressController/testdata/degraded.yaml new file mode 100644 index 0000000000000..73c7c89e370a9 --- /dev/null +++ b/resource_customizations/operator.openshift.io/IngressController/testdata/degraded.yaml @@ -0,0 +1,103 @@ +--- +apiVersion: operator.openshift.io/v1 +kind: IngressController +metadata: + name: default + namespace: openshift-ingress-operator +spec: + domain: openshift.example.com + endpointPublishingStrategy: + hostNetwork: + httpPort: 80 + httpsPort: 443 + statsPort: 1936 + type: HostNetwork + nodePlacement: + nodeSelector: + matchLabels: + node-role.kubernetes.io/worker: "" + replicas: 1 +status: + availableReplicas: 0 + conditions: + - lastTransitionTime: "2023-01-28T10:05:06Z" + reason: Valid + status: "True" + type: Admitted + - lastTransitionTime: "2023-01-28T10:09:15Z" + status: "True" + type: PodsScheduled + - lastTransitionTime: "2023-01-28T10:05:06Z" + message: The configured endpoint publishing strategy does not include a managed + load balancer + reason: EndpointPublishingStrategyExcludesManagedLoadBalancer + status: "False" + type: LoadBalancerManaged + - lastTransitionTime: "2023-01-28T10:05:06Z" + message: No DNS zones are defined in the cluster dns config. + reason: NoDNSZones + status: "False" + type: DNSManaged + - lastTransitionTime: "2023-01-28T10:05:06Z" + status: "False" + type: Progressing + - lastTransitionTime: "2023-01-28T10:13:55Z" + message: "One or more other status conditions indicate a degraded state." + # message: 'One or more other status conditions indicate a degraded state: CanaryChecksSucceeding=False + # (CanaryChecksRepetitiveFailures: Canary route checks for the default ingress + # controller are failing)' + reason: DegradedConditions + status: "True" + type: Degraded + - lastTransitionTime: "2023-01-28T10:05:06Z" + message: IngressController is upgradeable. + reason: Upgradeable + status: "True" + type: Upgradeable + - lastTransitionTime: "2023-01-28T10:12:55Z" + message: Canary route checks for the default ingress controller are failing + reason: CanaryChecksRepetitiveFailures + status: "False" + type: CanaryChecksSucceeding + domain: openshift.example.com + endpointPublishingStrategy: + hostNetwork: + httpPort: 80 + httpsPort: 443 + protocol: TCP + statsPort: 1936 + type: HostNetwork + namespaceSelector: {} + observedGeneration: 2 + routeSelector: {} + selector: ingresscontroller.operator.openshift.io/deployment-ingresscontroller=default + tlsProfile: + ciphers: + - ECDHE-ECDSA-CHACHA20-POLY1305 + - ECDHE-RSA-CHACHA20-POLY1305 + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + - ECDHE-ECDSA-AES256-GCM-SHA384 + - ECDHE-RSA-AES256-GCM-SHA384 + - DHE-RSA-AES128-GCM-SHA256 + - DHE-RSA-AES256-GCM-SHA384 + - ECDHE-ECDSA-AES128-SHA256 + - ECDHE-RSA-AES128-SHA256 + - ECDHE-ECDSA-AES128-SHA + - ECDHE-RSA-AES256-SHA384 + - ECDHE-RSA-AES128-SHA + - ECDHE-ECDSA-AES256-SHA384 + - ECDHE-ECDSA-AES256-SHA + - ECDHE-RSA-AES256-SHA + - DHE-RSA-AES128-SHA256 + - DHE-RSA-AES128-SHA + - DHE-RSA-AES256-SHA256 + - DHE-RSA-AES256-SHA + - AES128-GCM-SHA256 + - AES256-GCM-SHA384 + - AES128-SHA256 + - AES256-SHA256 + - AES128-SHA + - AES256-SHA + - '!DSS' + minTLSVersion: VersionTLS11 diff --git a/resource_customizations/operator.openshift.io/IngressController/testdata/healthy.yaml b/resource_customizations/operator.openshift.io/IngressController/testdata/healthy.yaml new file mode 100644 index 0000000000000..4c7ad766f1d86 --- /dev/null +++ b/resource_customizations/operator.openshift.io/IngressController/testdata/healthy.yaml @@ -0,0 +1,93 @@ +--- +apiVersion: operator.openshift.io/v1 +kind: IngressController +metadata: + name: apps-shard-2 + namespace: openshift-ingress-operator +spec: + domain: openshift-apps-shard-2.example.com + endpointPublishingStrategy: + hostNetwork: + httpPort: 80 + httpsPort: 443 + statsPort: 1936 + type: HostNetwork + nodePlacement: + nodeSelector: + matchLabels: + node-role.kubernetes.io/worker: "" + replicas: 1 +status: + availableReplicas: 1 + conditions: + - lastTransitionTime: "2023-01-28T09:34:36Z" + reason: Valid + status: "True" + type: Admitted + - lastTransitionTime: "2023-01-28T09:43:42Z" + status: "True" + type: PodsScheduled + - lastTransitionTime: "2023-01-28T09:34:36Z" + message: The deployment has Available status condition set to True + reason: DeploymentAvailable + status: "True" + type: DeploymentAvailable + - lastTransitionTime: "2023-01-28T09:34:36Z" + message: Minimum replicas requirement is met + reason: DeploymentMinimumReplicasMet + status: "True" + type: DeploymentReplicasMinAvailable + - lastTransitionTime: "2023-01-28T09:44:36Z" + message: All replicas are available + reason: DeploymentReplicasAvailable + status: "True" + type: DeploymentReplicasAllAvailable + - lastTransitionTime: "2023-01-28T09:34:36Z" + message: The configured endpoint publishing strategy does not include a managed + load balancer + reason: EndpointPublishingStrategyExcludesManagedLoadBalancer + status: "False" + type: LoadBalancerManaged + - lastTransitionTime: "2023-01-28T09:34:36Z" + message: No DNS zones are defined in the cluster dns config. + reason: NoDNSZones + status: "False" + type: DNSManaged + - lastTransitionTime: "2023-01-28T09:34:36Z" + status: "True" + type: Available + - lastTransitionTime: "2023-01-28T09:34:36Z" + status: "False" + type: Progressing + - lastTransitionTime: "2023-01-28T09:34:36Z" + status: "False" + type: Degraded + - lastTransitionTime: "2023-01-28T09:34:36Z" + message: IngressController is upgradeable. + reason: Upgradeable + status: "True" + type: Upgradeable + domain: openshift-apps-shard-2.example.com + endpointPublishingStrategy: + hostNetwork: + httpPort: 80 + httpsPort: 443 + protocol: TCP + statsPort: 1936 + type: HostNetwork + observedGeneration: 5 + selector: ingresscontroller.operator.openshift.io/deployment-ingresscontroller=apps-shard-2 + tlsProfile: + ciphers: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + - ECDHE-ECDSA-AES256-GCM-SHA384 + - ECDHE-RSA-AES256-GCM-SHA384 + - ECDHE-ECDSA-CHACHA20-POLY1305 + - ECDHE-RSA-CHACHA20-POLY1305 + - DHE-RSA-AES128-GCM-SHA256 + - DHE-RSA-AES256-GCM-SHA384 + - TLS_AES_128_GCM_SHA256 + - TLS_AES_256_GCM_SHA384 + - TLS_CHACHA20_POLY1305_SHA256 + minTLSVersion: VersionTLS12 diff --git a/resource_customizations/operator.openshift.io/IngressController/testdata/progressing_initialization.yaml b/resource_customizations/operator.openshift.io/IngressController/testdata/progressing_initialization.yaml new file mode 100644 index 0000000000000..470216e376e84 --- /dev/null +++ b/resource_customizations/operator.openshift.io/IngressController/testdata/progressing_initialization.yaml @@ -0,0 +1,36 @@ +--- +apiVersion: operator.openshift.io/v1 +kind: IngressController +metadata: + name: apps-shard-2 + namespace: openshift-ingress-operator +spec: + domain: openshift-apps-shard-2.example.com + endpointPublishingStrategy: + hostNetwork: + httpPort: 80 + httpsPort: 443 + statsPort: 1936 + type: HostNetwork + nodePlacement: + nodeSelector: + matchLabels: + node-role.kubernetes.io/worker: "" + replicas: 1 +status: + availableReplicas: 0 + conditions: + - lastTransitionTime: "2023-01-28T09:34:36Z" + reason: Valid + status: "True" + type: Admitted + domain: openshift-apps-shard-2.example.com + endpointPublishingStrategy: + hostNetwork: + httpPort: 80 + httpsPort: 443 + protocol: TCP + statsPort: 1936 + type: HostNetwork + observedGeneration: 1 + selector: "" diff --git a/resource_customizations/operator.openshift.io/IngressController/testdata/progressing_pod_rollout.yaml b/resource_customizations/operator.openshift.io/IngressController/testdata/progressing_pod_rollout.yaml new file mode 100644 index 0000000000000..73a33ae48613b --- /dev/null +++ b/resource_customizations/operator.openshift.io/IngressController/testdata/progressing_pod_rollout.yaml @@ -0,0 +1,101 @@ +--- +apiVersion: operator.openshift.io/v1 +kind: IngressController +metadata: + name: apps-shard-2 + namespace: openshift-ingress-operator +spec: + domain: openshift-apps-shard-2.example.com + endpointPublishingStrategy: + hostNetwork: + httpPort: 80 + httpsPort: 443 + statsPort: 1936 + type: HostNetwork + nodePlacement: + nodeSelector: + matchLabels: + node-role.kubernetes.io/worker: "" + replicas: 1 +status: + availableReplicas: 0 + conditions: + - lastTransitionTime: "2023-01-28T09:34:36Z" + reason: Valid + status: "True" + type: Admitted + - lastTransitionTime: "2023-01-28T09:34:36Z" + message: 'Some pods are not scheduled: Pod "router-apps-shard-2-7b5cb5f98d-gk4hj" + cannot be scheduled: 0/6 nodes are available: 2 node(s) didn''t have free ports + for the requested pod ports, 3 node(s) had untolerated taint {node-role.kubernetes.io/master: + }, 5 node(s) didn''t match Pod''s node affinity/selector. preemption: 0/6 nodes + are available: 1 node(s) didn''t have free ports for the requested pod ports, + 5 Preemption is not helpful for scheduling. Make sure you have sufficient worker + nodes.' + reason: PodsNotScheduled + status: "False" + type: PodsScheduled + - lastTransitionTime: "2023-01-28T09:34:36Z" + message: The deployment has Available status condition set to True + reason: DeploymentAvailable + status: "True" + type: DeploymentAvailable + - lastTransitionTime: "2023-01-28T09:34:36Z" + message: Minimum replicas requirement is met + reason: DeploymentMinimumReplicasMet + status: "True" + type: DeploymentReplicasMinAvailable + - lastTransitionTime: "2023-01-28T09:34:36Z" + message: 0/1 of replicas are available + reason: DeploymentReplicasNotAvailable + status: "False" + type: DeploymentReplicasAllAvailable + - lastTransitionTime: "2023-01-28T09:34:36Z" + message: The configured endpoint publishing strategy does not include a managed + load balancer + reason: EndpointPublishingStrategyExcludesManagedLoadBalancer + status: "False" + type: LoadBalancerManaged + - lastTransitionTime: "2023-01-28T09:34:36Z" + message: No DNS zones are defined in the cluster dns config. + reason: NoDNSZones + status: "False" + type: DNSManaged + - lastTransitionTime: "2023-01-28T09:34:36Z" + status: "True" + type: Available + - lastTransitionTime: "2023-01-28T09:34:36Z" + status: "False" + type: Progressing + - lastTransitionTime: "2023-01-28T09:34:36Z" + status: "False" + type: Degraded + - lastTransitionTime: "2023-01-28T09:34:36Z" + message: IngressController is upgradeable. + reason: Upgradeable + status: "True" + type: Upgradeable + domain: openshift-apps-shard-2.example.com + endpointPublishingStrategy: + hostNetwork: + httpPort: 80 + httpsPort: 443 + protocol: TCP + statsPort: 1936 + type: HostNetwork + observedGeneration: 2 + selector: ingresscontroller.operator.openshift.io/deployment-ingresscontroller=apps-shard-2 + tlsProfile: + ciphers: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + - ECDHE-ECDSA-AES256-GCM-SHA384 + - ECDHE-RSA-AES256-GCM-SHA384 + - ECDHE-ECDSA-CHACHA20-POLY1305 + - ECDHE-RSA-CHACHA20-POLY1305 + - DHE-RSA-AES128-GCM-SHA256 + - DHE-RSA-AES256-GCM-SHA384 + - TLS_AES_128_GCM_SHA256 + - TLS_AES_256_GCM_SHA384 + - TLS_CHACHA20_POLY1305_SHA256 + minTLSVersion: VersionTLS12 diff --git a/resource_customizations/operators.coreos.com/Subscription/health.lua b/resource_customizations/operators.coreos.com/Subscription/health.lua index 41da0ee12bd5d..ca5f917f0715a 100644 --- a/resource_customizations/operators.coreos.com/Subscription/health.lua +++ b/resource_customizations/operators.coreos.com/Subscription/health.lua @@ -1,9 +1,9 @@ -health_status = {} +local health_status = {} if obj.status ~= nil then if obj.status.conditions ~= nil then - numDegraded = 0 - numPending = 0 - msg = "" + local numDegraded = 0 + local numPending = 0 + local msg = "" for i, condition in pairs(obj.status.conditions) do msg = msg .. i .. ": " .. condition.type .. " | " .. condition.status .. "\n" if condition.type == "InstallPlanPending" and condition.status == "True" then diff --git a/resource_customizations/pkg.crossplane.io/Provider/health.lua b/resource_customizations/pkg.crossplane.io/Provider/health.lua index 04242d67115a3..bcffa34e8cc36 100644 --- a/resource_customizations/pkg.crossplane.io/Provider/health.lua +++ b/resource_customizations/pkg.crossplane.io/Provider/health.lua @@ -1,8 +1,8 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then - installed = false - healthy = false + local installed = false + local healthy = false for i, condition in ipairs(obj.status.conditions) do if condition.type == "Installed" then installed = condition.status == "True" diff --git a/resource_customizations/platform.confluent.io/Connect/health.lua b/resource_customizations/platform.confluent.io/Connect/health.lua index 329585870952b..5ae4d7af34b73 100644 --- a/resource_customizations/platform.confluent.io/Connect/health.lua +++ b/resource_customizations/platform.confluent.io/Connect/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.phase ~= nil then if obj.status.phase == "RUNNING" then diff --git a/resource_customizations/platform.confluent.io/ControlCenter/health.lua b/resource_customizations/platform.confluent.io/ControlCenter/health.lua index 6b54872a2d0a0..2270545c7305b 100644 --- a/resource_customizations/platform.confluent.io/ControlCenter/health.lua +++ b/resource_customizations/platform.confluent.io/ControlCenter/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.phase ~= nil then if obj.status.phase == "RUNNING" then diff --git a/resource_customizations/platform.confluent.io/Kafka/health.lua b/resource_customizations/platform.confluent.io/Kafka/health.lua index 00033faf3132b..1abafd5d26a69 100644 --- a/resource_customizations/platform.confluent.io/Kafka/health.lua +++ b/resource_customizations/platform.confluent.io/Kafka/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.phase ~= nil then if obj.status.phase == "RUNNING" then diff --git a/resource_customizations/platform.confluent.io/KsqlDB/health.lua b/resource_customizations/platform.confluent.io/KsqlDB/health.lua index cfada56c772a5..263a72786a4e7 100644 --- a/resource_customizations/platform.confluent.io/KsqlDB/health.lua +++ b/resource_customizations/platform.confluent.io/KsqlDB/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.phase ~= nil then if obj.status.phase == "RUNNING" then diff --git a/resource_customizations/platform.confluent.io/SchemaRegistry/health.lua b/resource_customizations/platform.confluent.io/SchemaRegistry/health.lua index e5542ba826ed7..9aaa1a5ef3388 100644 --- a/resource_customizations/platform.confluent.io/SchemaRegistry/health.lua +++ b/resource_customizations/platform.confluent.io/SchemaRegistry/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.phase ~= nil then if obj.status.phase == "RUNNING" then diff --git a/resource_customizations/platform.confluent.io/Zookeeper/health.lua b/resource_customizations/platform.confluent.io/Zookeeper/health.lua index 20c0fa847fe5f..92f89b1fc6596 100644 --- a/resource_customizations/platform.confluent.io/Zookeeper/health.lua +++ b/resource_customizations/platform.confluent.io/Zookeeper/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.phase ~= nil then if obj.status.phase == "RUNNING" then diff --git a/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/health.lua b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/health.lua new file mode 100644 index 0000000000000..a83520eaa8265 --- /dev/null +++ b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/health.lua @@ -0,0 +1,29 @@ +local adopted = { status = "Unknown" } +local advertised = { status = "Unknown" } +local discovered = { status = "Unknown" } + +if obj.status ~= nil then + if obj.status.conditions ~= nil then + for i, c in ipairs(obj.status.conditions) do + if c.type == "Adopted" then + adopted = c + elseif c.type == "Advertised" then + advertised = c + elseif c.type == "Discoverable" then + discovered = c + end + end + end +end + +if adopted.status == "False" then + return { status = "Degraded", message = adopted.message } +elseif advertised.reason == "AdvertiseError" or advertised.reason == "UnadvertiseError" then + return { status = "Degraded", message = advertised.message } +elseif discovered.reason == "DiscoveryError" then + return { status = "Unknown", message = discovered.message } +elseif discovered.status == "True" then + return { status = "Healthy", message = discovered.message } +else + return { status = "Progressing", message = discovered.message } +end diff --git a/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/health_test.yaml b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/health_test.yaml new file mode 100644 index 0000000000000..1b9b30cf2e44b --- /dev/null +++ b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/health_test.yaml @@ -0,0 +1,29 @@ +tests: + - healthStatus: + status: Healthy + message: DNS-SD browse and lookup results match the advertised DNS records + inputPath: testdata/healthy.yaml + - healthStatus: + status: Progressing + message: DNS-SD browse could not find this instance + inputPath: testdata/progressing_negativeBrowse.yaml + - healthStatus: + status: Progressing + message: DNS-SD lookup could not find this instance + inputPath: testdata/progressing_negativeLookup.yaml + - healthStatus: + status: Degraded + message: none of the configured providers can advertise on "example.org" + inputPath: testdata/degraded_notAdopted.yaml + - healthStatus: + status: Degraded + message: "" + inputPath: testdata/degraded_advertiseError.yaml + - healthStatus: + status: Degraded + message: "" + inputPath: testdata/degraded_unadvertiseError.yaml + - healthStatus: + status: Unknown + message: "" + inputPath: testdata/unknown_discoveryError.yaml diff --git a/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/degraded_advertiseError.yaml b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/degraded_advertiseError.yaml new file mode 100644 index 0000000000000..905b2e9194e8b --- /dev/null +++ b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/degraded_advertiseError.yaml @@ -0,0 +1,35 @@ +apiVersion: proclaim.dogmatiq.io/v1 +kind: DNSSDServiceInstance +metadata: + creationTimestamp: "2023-03-20T01:47:37Z" + finalizers: + - proclaim.dogmatiq.io/unadvertise + generation: 2 + name: test-instance + namespace: proclaim + resourceVersion: "308914" + uid: 991a66a3-9b7e-4515-9a41-f7513e9b7b33 +spec: + instance: + attributes: + - baz: qux + flag: "" + foo: bar + - more: attrs + domain: example.org + name: test-instance + serviceType: _proclaim._tcp + targets: + - host: test.example.org + port: 8080 + priority: 0 + weight: 0 + ttl: 1m0s +status: + conditions: + - lastTransitionTime: "2023-03-20T01:47:40Z" + message: "" + observedGeneration: 2 + reason: AdvertiseError + status: "False" + type: Advertised diff --git a/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/degraded_notAdopted.yaml b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/degraded_notAdopted.yaml new file mode 100644 index 0000000000000..efccdb2c3f247 --- /dev/null +++ b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/degraded_notAdopted.yaml @@ -0,0 +1,35 @@ +apiVersion: proclaim.dogmatiq.io/v1 +kind: DNSSDServiceInstance +metadata: + creationTimestamp: "2023-03-20T01:47:37Z" + finalizers: + - proclaim.dogmatiq.io/unadvertise + generation: 2 + name: test-instance + namespace: proclaim + resourceVersion: "308914" + uid: 991a66a3-9b7e-4515-9a41-f7513e9b7b33 +spec: + instance: + attributes: + - baz: qux + flag: "" + foo: bar + - more: attrs + domain: example.org + name: test-instance + serviceType: _proclaim._tcp + targets: + - host: test.example.org + port: 8080 + priority: 0 + weight: 0 + ttl: 1m0s +status: + conditions: + - lastTransitionTime: "2023-03-20T01:47:40Z" + message: none of the configured providers can advertise on "example.org" + observedGeneration: 2 + reason: InstanceIgnored + status: "False" + type: Adopted diff --git a/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/degraded_unadvertiseError.yaml b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/degraded_unadvertiseError.yaml new file mode 100644 index 0000000000000..552eadbe702cc --- /dev/null +++ b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/degraded_unadvertiseError.yaml @@ -0,0 +1,35 @@ +apiVersion: proclaim.dogmatiq.io/v1 +kind: DNSSDServiceInstance +metadata: + creationTimestamp: "2023-03-20T01:47:37Z" + finalizers: + - proclaim.dogmatiq.io/unadvertise + generation: 2 + name: test-instance + namespace: proclaim + resourceVersion: "308914" + uid: 991a66a3-9b7e-4515-9a41-f7513e9b7b33 +spec: + instance: + attributes: + - baz: qux + flag: "" + foo: bar + - more: attrs + domain: example.org + name: test-instance + serviceType: _proclaim._tcp + targets: + - host: test.example.org + port: 8080 + priority: 0 + weight: 0 + ttl: 1m0s +status: + conditions: + - lastTransitionTime: "2023-03-20T01:47:40Z" + message: "" + observedGeneration: 2 + reason: UnadvertiseError + status: "False" + type: Advertised diff --git a/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/healthy.yaml b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/healthy.yaml new file mode 100644 index 0000000000000..f8ad890b9f934 --- /dev/null +++ b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/healthy.yaml @@ -0,0 +1,35 @@ +apiVersion: proclaim.dogmatiq.io/v1 +kind: DNSSDServiceInstance +metadata: + creationTimestamp: "2023-03-20T01:47:37Z" + finalizers: + - proclaim.dogmatiq.io/unadvertise + generation: 2 + name: test-instance + namespace: proclaim + resourceVersion: "308914" + uid: 991a66a3-9b7e-4515-9a41-f7513e9b7b33 +spec: + instance: + attributes: + - baz: qux + flag: "" + foo: bar + - more: attrs + domain: example.org + name: test-instance + serviceType: _proclaim._tcp + targets: + - host: test.example.org + port: 8080 + priority: 0 + weight: 0 + ttl: 1m0s +status: + conditions: + - lastTransitionTime: "2023-03-20T01:47:40Z" + message: DNS-SD browse and lookup results match the advertised DNS records + observedGeneration: 2 + reason: Discovered + status: "True" + type: Discoverable diff --git a/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/progressing_negativeBrowse.yaml b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/progressing_negativeBrowse.yaml new file mode 100644 index 0000000000000..e34e6c18f853a --- /dev/null +++ b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/progressing_negativeBrowse.yaml @@ -0,0 +1,35 @@ +apiVersion: proclaim.dogmatiq.io/v1 +kind: DNSSDServiceInstance +metadata: + creationTimestamp: "2023-03-20T01:47:37Z" + finalizers: + - proclaim.dogmatiq.io/unadvertise + generation: 2 + name: test-instance + namespace: proclaim + resourceVersion: "308914" + uid: 991a66a3-9b7e-4515-9a41-f7513e9b7b33 +spec: + instance: + attributes: + - baz: qux + flag: "" + foo: bar + - more: attrs + domain: example.org + name: test-instance + serviceType: _proclaim._tcp + targets: + - host: test.example.org + port: 8080 + priority: 0 + weight: 0 + ttl: 1m0s +status: + conditions: + - lastTransitionTime: "2023-03-20T01:47:40Z" + message: DNS-SD browse could not find this instance + observedGeneration: 2 + reason: NegativeBrowseResult + status: "False" + type: Discoverable diff --git a/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/progressing_negativeLookup.yaml b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/progressing_negativeLookup.yaml new file mode 100644 index 0000000000000..a563e7c9c40ca --- /dev/null +++ b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/progressing_negativeLookup.yaml @@ -0,0 +1,35 @@ +apiVersion: proclaim.dogmatiq.io/v1 +kind: DNSSDServiceInstance +metadata: + creationTimestamp: "2023-03-20T01:47:37Z" + finalizers: + - proclaim.dogmatiq.io/unadvertise + generation: 2 + name: test-instance + namespace: proclaim + resourceVersion: "308914" + uid: 991a66a3-9b7e-4515-9a41-f7513e9b7b33 +spec: + instance: + attributes: + - baz: qux + flag: "" + foo: bar + - more: attrs + domain: example.org + name: test-instance + serviceType: _proclaim._tcp + targets: + - host: test.example.org + port: 8080 + priority: 0 + weight: 0 + ttl: 1m0s +status: + conditions: + - lastTransitionTime: "2023-03-20T01:47:40Z" + message: DNS-SD lookup could not find this instance + observedGeneration: 2 + reason: NegativeLookupResult + status: "False" + type: Discoverable diff --git a/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/unknown_discoveryError.yaml b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/unknown_discoveryError.yaml new file mode 100644 index 0000000000000..c6139a504c3ff --- /dev/null +++ b/resource_customizations/proclaim.dogmatiq.io/DNSSDServiceInstance/testdata/unknown_discoveryError.yaml @@ -0,0 +1,35 @@ +apiVersion: proclaim.dogmatiq.io/v1 +kind: DNSSDServiceInstance +metadata: + creationTimestamp: "2023-03-20T01:47:37Z" + finalizers: + - proclaim.dogmatiq.io/unadvertise + generation: 2 + name: test-instance + namespace: proclaim + resourceVersion: "308914" + uid: 991a66a3-9b7e-4515-9a41-f7513e9b7b33 +spec: + instance: + attributes: + - baz: qux + flag: "" + foo: bar + - more: attrs + domain: example.org + name: test-instance + serviceType: _proclaim._tcp + targets: + - host: test.example.org + port: 8080 + priority: 0 + weight: 0 + ttl: 1m0s +status: + conditions: + - lastTransitionTime: "2023-03-20T01:47:40Z" + message: "" + observedGeneration: 2 + reason: DiscoveryError + status: "Unknown" + type: Discoverable diff --git a/resource_customizations/pubsub.cnrm.cloud.google.com/PubSubSubscription/health.lua b/resource_customizations/pubsub.cnrm.cloud.google.com/PubSubSubscription/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/pubsub.cnrm.cloud.google.com/PubSubSubscription/health.lua +++ b/resource_customizations/pubsub.cnrm.cloud.google.com/PubSubSubscription/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/pubsub.cnrm.cloud.google.com/PubSubTopic/health.lua b/resource_customizations/pubsub.cnrm.cloud.google.com/PubSubTopic/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/pubsub.cnrm.cloud.google.com/PubSubTopic/health.lua +++ b/resource_customizations/pubsub.cnrm.cloud.google.com/PubSubTopic/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/health.lua b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/health.lua new file mode 100644 index 0000000000000..d614828d461f2 --- /dev/null +++ b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/health.lua @@ -0,0 +1,38 @@ +local hs = {} +if obj.status ~= nil then + + if obj.status.state == "initializing" then + hs.status = "Progressing" + hs.message = obj.status.ready .. "/" .. obj.status.size .. " node(s) are ready" + return hs + end + + if obj.status.state == "ready" then + hs.status = "Healthy" + hs.message = obj.status.ready .. "/" .. obj.status.size .. " node(s) are ready" + return hs + end + + if obj.status.state == "paused" then + hs.status = "Unknown" + hs.message = "Cluster is paused" + return hs + end + + if obj.status.state == "stopping" then + hs.status = "Degraded" + hs.message = "Cluster is stopping (" .. obj.status.ready .. "/" .. obj.status.size .. " node(s) are ready)" + return hs + end + + if obj.status.state == "error" then + hs.status = "Degraded" + hs.message = "Cluster is on error: " .. table.concat(obj.status.message, ", ") + return hs + end + +end + +hs.status = "Unknown" +hs.message = "Cluster status is unknown. Ensure your ArgoCD is current and then check for/file a bug report: https://github.com/argoproj/argo-cd/issues" +return hs diff --git a/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/health_test.yaml b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/health_test.yaml new file mode 100644 index 0000000000000..73b9968ff7a4c --- /dev/null +++ b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/health_test.yaml @@ -0,0 +1,25 @@ +tests: +- healthStatus: + status: Progressing + message: "0/1 node(s) are ready" + inputPath: testdata/initializing.yaml +- healthStatus: + status: Healthy + message: "1/1 node(s) are ready" + inputPath: testdata/ready.yaml +- healthStatus: + status: Unknown + message: "Cluster is paused" + inputPath: testdata/paused.yaml +- healthStatus: + status: Degraded + message: "Cluster is stopping (1/2 node(s) are ready)" + inputPath: testdata/stopping.yaml +- healthStatus: + status: Degraded + message: "Cluster is on error: we lost node" + inputPath: testdata/error.yaml +- healthStatus: + status: Unknown + message: "Cluster status is unknown. Ensure your ArgoCD is current and then check for/file a bug report: https://github.com/argoproj/argo-cd/issues" + inputPath: testdata/unknown.yaml diff --git a/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/error.yaml b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/error.yaml new file mode 100644 index 0000000000000..4a373358dcd8c --- /dev/null +++ b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/error.yaml @@ -0,0 +1,24 @@ +apiVersion: pxc.percona.com/v1 +kind: PerconaXtraDBCluster +metadata: + name: quickstart +spec: {} +status: + backup: {} + haproxy: {} + host: pxc-mysql-pxc + logcollector: {} + observedGeneration: 1 + pmm: {} + proxysql: {} + pxc: + image: "" + ready: 1 + size: 2 + status: error + version: 8.0.21-12.1 + ready: 1 + size: 2 + state: error + message: + - we lost node diff --git a/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/initializing.yaml b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/initializing.yaml new file mode 100644 index 0000000000000..11f3ff046543e --- /dev/null +++ b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/initializing.yaml @@ -0,0 +1,22 @@ +apiVersion: pxc.percona.com/v1 +kind: PerconaXtraDBCluster +metadata: + name: quickstart +spec: {} +status: + backup: {} + haproxy: {} + host: pxc-mysql-pxc + logcollector: {} + observedGeneration: 1 + pmm: {} + proxysql: {} + pxc: + image: '' + ready: 0 + size: 1 + status: initializing + version: 8.0.21-12.1 + ready: 0 + size: 1 + state: initializing diff --git a/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/paused.yaml b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/paused.yaml new file mode 100644 index 0000000000000..46440a23df7e2 --- /dev/null +++ b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/paused.yaml @@ -0,0 +1,22 @@ +apiVersion: pxc.percona.com/v1 +kind: PerconaXtraDBCluster +metadata: + name: quickstart +spec: {} +status: + backup: {} + haproxy: {} + host: pxc-mysql-pxc + logcollector: {} + observedGeneration: 1 + pmm: {} + proxysql: {} + pxc: + image: '' + ready: 1 + size: 1 + status: paused + version: 8.0.21-12.1 + ready: 1 + size: 1 + state: paused diff --git a/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/ready.yaml b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/ready.yaml new file mode 100644 index 0000000000000..bd7d82a2a08fe --- /dev/null +++ b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/ready.yaml @@ -0,0 +1,22 @@ +apiVersion: pxc.percona.com/v1 +kind: PerconaXtraDBCluster +metadata: + name: quickstart +spec: {} +status: + backup: {} + haproxy: {} + host: pxc-mysql-pxc + logcollector: {} + observedGeneration: 1 + pmm: {} + proxysql: {} + pxc: + image: '' + ready: 1 + size: 1 + status: ready + version: 8.0.21-12.1 + ready: 1 + size: 1 + state: ready diff --git a/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/stopping.yaml b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/stopping.yaml new file mode 100644 index 0000000000000..f527445c506a0 --- /dev/null +++ b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/stopping.yaml @@ -0,0 +1,22 @@ +apiVersion: pxc.percona.com/v1 +kind: PerconaXtraDBCluster +metadata: + name: quickstart +spec: {} +status: + backup: {} + haproxy: {} + host: pxc-mysql-pxc + logcollector: {} + observedGeneration: 1 + pmm: {} + proxysql: {} + pxc: + image: '' + ready: 1 + size: 2 + status: stopping + version: 8.0.21-12.1 + ready: 1 + size: 2 + state: stopping diff --git a/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/unknown.yaml b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/unknown.yaml new file mode 100644 index 0000000000000..c12b04682b18e --- /dev/null +++ b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/unknown.yaml @@ -0,0 +1,22 @@ +apiVersion: pxc.percona.com/v1 +kind: PerconaXtraDBCluster +metadata: + name: quickstart +spec: {} +status: + backup: {} + haproxy: {} + host: pxc-mysql-pxc + logcollector: {} + observedGeneration: 1 + pmm: {} + proxysql: {} + pxc: + image: '' + ready: 1 + size: 1 + status: dontknow + version: 8.0.21-12.1 + ready: 1 + size: 1 + state: dontknow diff --git a/resource_customizations/resourcemanager.cnrm.cloud.google.com/Project/health.lua b/resource_customizations/resourcemanager.cnrm.cloud.google.com/Project/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/resourcemanager.cnrm.cloud.google.com/Project/health.lua +++ b/resource_customizations/resourcemanager.cnrm.cloud.google.com/Project/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/rollouts.kruise.io/Rollout/health.lua b/resource_customizations/rollouts.kruise.io/Rollout/health.lua new file mode 100644 index 0000000000000..5fd4ddb2a5486 --- /dev/null +++ b/resource_customizations/rollouts.kruise.io/Rollout/health.lua @@ -0,0 +1,31 @@ +hs={ status = "Progressing", message = "Rollout is still progressing" } + +if obj.metadata.generation == obj.status.observedGeneration then + + if obj.status.canaryStatus.currentStepState == "StepUpgrade" and obj.status.phase == "Progressing" then + hs.status = "Progressing" + hs.message = "Rollout is still progressing" + return hs + end + + if obj.status.canaryStatus.currentStepState == "StepPaused" and obj.status.phase == "Progressing" then + hs.status = "Suspended" + hs.message = "Rollout is Paused need manual intervention" + return hs + end + + if obj.status.canaryStatus.currentStepState == "Completed" and obj.status.phase == "Healthy" then + hs.status = "Healthy" + hs.message = "Rollout is Completed" + return hs + end + + if obj.status.canaryStatus.currentStepState == "StepPaused" and (obj.status.phase == "Terminating" or obj.status.phase == "Disabled") then + hs.status = "Degraded" + hs.message = "Rollout is Disabled or Terminating" + return hs + end + +end + +return hs diff --git a/resource_customizations/rollouts.kruise.io/Rollout/health_test.yaml b/resource_customizations/rollouts.kruise.io/Rollout/health_test.yaml new file mode 100644 index 0000000000000..c89ea3409ec77 --- /dev/null +++ b/resource_customizations/rollouts.kruise.io/Rollout/health_test.yaml @@ -0,0 +1,17 @@ +tests: + - healthStatus: + status: Healthy + message: "Rollout is Completed" + inputPath: testdata/healthy.yaml + - healthStatus: + status: Degraded + message: "Rollout is Disabled or Terminating" + inputPath: testdata/degraded.yaml + - healthStatus: + status: Progressing + message: "Rollout is still progressing" + inputPath: testdata/progressing.yaml + - healthStatus: + status: Suspended + message: "Rollout is Paused need manual intervention" + inputPath: testdata/suspended.yaml diff --git a/resource_customizations/rollouts.kruise.io/Rollout/testdata/degraded.yaml b/resource_customizations/rollouts.kruise.io/Rollout/testdata/degraded.yaml new file mode 100644 index 0000000000000..97c40f10a0c96 --- /dev/null +++ b/resource_customizations/rollouts.kruise.io/Rollout/testdata/degraded.yaml @@ -0,0 +1,50 @@ +apiVersion: rollouts.kruise.io/v1alpha1 +kind: Rollout +metadata: + name: rollouts-demo + namespace: default + annotations: + rollouts.kruise.io/rolling-style: partition + generation: 5 +spec: + objectRef: + workloadRef: + apiVersion: apps/v1 + kind: Deployment + name: workload-demo + strategy: + canary: + steps: + - replicas: 1 + pause: + duration: 0 + - replicas: 50% + pause: + duration: 0 + - replicas: 100% + +status: + canaryStatus: + canaryReadyReplicas: 1 + canaryReplicas: 1 + canaryRevision: 76fd76f75b + currentStepIndex: 1 + currentStepState: StepPaused + lastUpdateTime: '2023-09-23T11:44:39Z' + message: BatchRelease is at state Ready, rollout-id , step 1 + observedWorkloadGeneration: 7 + podTemplateHash: 76fd76f75b + rolloutHash: 77cxd69w47b7bwddwv2w7vxvb4xxdbwcx9x289vw69w788w4w6z4x8dd4vbz2zbw + stableRevision: 6bfdfb5bfb + conditions: + - lastTransitionTime: '2023-09-23T11:44:09Z' + lastUpdateTime: '2023-09-23T11:44:09Z' + message: Rollout is in Progressing + reason: InRolling + status: 'True' + type: Progressing + message: >- + Rollout is in step(1/3), and you need manually confirm to enter the next + step + observedGeneration: 5 + phase: Disabled diff --git a/resource_customizations/rollouts.kruise.io/Rollout/testdata/healthy.yaml b/resource_customizations/rollouts.kruise.io/Rollout/testdata/healthy.yaml new file mode 100644 index 0000000000000..77743b50007ad --- /dev/null +++ b/resource_customizations/rollouts.kruise.io/Rollout/testdata/healthy.yaml @@ -0,0 +1,56 @@ +apiVersion: rollouts.kruise.io/v1alpha1 +kind: Rollout +metadata: + name: rollouts-demo + namespace: default + annotations: + rollouts.kruise.io/rolling-style: partition + generation: 7 +spec: + objectRef: + workloadRef: + apiVersion: apps/v1 + kind: Deployment + name: workload-demo + strategy: + canary: + steps: + - replicas: 1 + pause: + duration: 0 + - replicas: 50% + pause: + duration: 0 + - replicas: 100% + +status: + canaryStatus: + canaryReadyReplicas: 10 + canaryReplicas: 10 + canaryRevision: 76fd76f75b + currentStepIndex: 3 + currentStepState: Completed + lastUpdateTime: '2023-09-23T11:48:58Z' + message: BatchRelease is at state Ready, rollout-id , step 3 + observedWorkloadGeneration: 22 + podTemplateHash: 76fd76f75b + rolloutHash: 77cxd69w47b7bwddwv2w7vxvb4xxdbwcx9x289vw69w788w4w6z4x8dd4vbz2zbw + stableRevision: 6bfdfb5bfb + conditions: + - lastTransitionTime: '2023-09-23T11:44:09Z' + lastUpdateTime: '2023-09-23T11:44:09Z' + message: Rollout progressing has been completed + reason: Completed + status: 'False' + type: Progressing + - lastTransitionTime: '2023-09-23T11:49:01Z' + lastUpdateTime: '2023-09-23T11:49:01Z' + message: '' + reason: '' + status: 'True' + type: Succeeded + message: Rollout progressing has been completed + observedGeneration: 7 + phase: Healthy + + diff --git a/resource_customizations/rollouts.kruise.io/Rollout/testdata/progressing.yaml b/resource_customizations/rollouts.kruise.io/Rollout/testdata/progressing.yaml new file mode 100644 index 0000000000000..f84d395867530 --- /dev/null +++ b/resource_customizations/rollouts.kruise.io/Rollout/testdata/progressing.yaml @@ -0,0 +1,48 @@ +apiVersion: rollouts.kruise.io/v1alpha1 +kind: Rollout +metadata: + name: rollouts-demo + namespace: default + annotations: + rollouts.kruise.io/rolling-style: partition + generation: 5 +spec: + objectRef: + workloadRef: + apiVersion: apps/v1 + kind: Deployment + name: workload-demo + strategy: + canary: + steps: + - replicas: 1 + pause: + duration: 0 + - replicas: 50% + pause: + duration: 0 + - replicas: 100% + +status: + canaryStatus: + canaryReadyReplicas: 0 + canaryReplicas: 1 + canaryRevision: 76fd76f75b + currentStepIndex: 1 + currentStepState: StepUpgrade + lastUpdateTime: '2023-09-23T11:44:12Z' + message: BatchRelease is at state Verifying, rollout-id , step 1 + observedWorkloadGeneration: 6 + podTemplateHash: 76fd76f75b + rolloutHash: 77cxd69w47b7bwddwv2w7vxvb4xxdbwcx9x289vw69w788w4w6z4x8dd4vbz2zbw + stableRevision: 6bfdfb5bfb + conditions: + - lastTransitionTime: '2023-09-23T11:44:09Z' + lastUpdateTime: '2023-09-23T11:44:09Z' + message: Rollout is in Progressing + reason: InRolling + status: 'True' + type: Progressing + message: Rollout is in step(1/3), and upgrade workload to new version + observedGeneration: 5 + phase: Progressing diff --git a/resource_customizations/rollouts.kruise.io/Rollout/testdata/suspended.yaml b/resource_customizations/rollouts.kruise.io/Rollout/testdata/suspended.yaml new file mode 100644 index 0000000000000..77a67129a248e --- /dev/null +++ b/resource_customizations/rollouts.kruise.io/Rollout/testdata/suspended.yaml @@ -0,0 +1,50 @@ +apiVersion: rollouts.kruise.io/v1alpha1 +kind: Rollout +metadata: + name: rollouts-demo + namespace: default + annotations: + rollouts.kruise.io/rolling-style: partition + generation: 5 +spec: + objectRef: + workloadRef: + apiVersion: apps/v1 + kind: Deployment + name: workload-demo + strategy: + canary: + steps: + - replicas: 1 + pause: + duration: 0 + - replicas: 50% + pause: + duration: 0 + - replicas: 100% + +status: + canaryStatus: + canaryReadyReplicas: 1 + canaryReplicas: 1 + canaryRevision: 76fd76f75b + currentStepIndex: 1 + currentStepState: StepPaused + lastUpdateTime: '2023-09-23T11:44:39Z' + message: BatchRelease is at state Ready, rollout-id , step 1 + observedWorkloadGeneration: 7 + podTemplateHash: 76fd76f75b + rolloutHash: 77cxd69w47b7bwddwv2w7vxvb4xxdbwcx9x289vw69w788w4w6z4x8dd4vbz2zbw + stableRevision: 6bfdfb5bfb + conditions: + - lastTransitionTime: '2023-09-23T11:44:09Z' + lastUpdateTime: '2023-09-23T11:44:09Z' + message: Rollout is in Progressing + reason: InRolling + status: 'True' + type: Progressing + message: >- + Rollout is in step(1/3), and you need manually confirm to enter the next + step + observedGeneration: 5 + phase: Progressing diff --git a/resource_customizations/route.openshift.io/Route/health.lua b/resource_customizations/route.openshift.io/Route/health.lua index 9183d493bfad9..5a8400b2cee92 100644 --- a/resource_customizations/route.openshift.io/Route/health.lua +++ b/resource_customizations/route.openshift.io/Route/health.lua @@ -1,9 +1,9 @@ -health_status = {} +local health_status = {} if obj.status ~= nil then if obj.status.ingress ~= nil then - numIngressRules = 0 - numTrue = 0 - numFalse = 0 + local numIngressRules = 0 + local numTrue = 0 + local numFalse = 0 for _, ingressRules in pairs(obj.status.ingress) do numIngressRules = numIngressRules + 1 if obj.status.ingress ~= nil then diff --git a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/health_test.yaml b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/health_test.yaml new file mode 100644 index 0000000000000..aa83951d5a2db --- /dev/null +++ b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/health_test.yaml @@ -0,0 +1,25 @@ +tests: +- healthStatus: + status: Progressing + message: Waiting for resourcrecordset to be available + inputPath: testdata/progressing_creating.yaml +- healthStatus: + status: Progressing + message: Waiting for resourcrecordset to be created + inputPath: testdata/progressing_noStatus.yaml +- healthStatus: + status: Degraded + message: >- + create failed: failed to create the ResourceRecordSet resource: + InvalidChangeBatch: [RRSet of type CNAME with DNS name + www.crossplane.io. is not permitted as it conflicts with other + records with the same DNS name in zone crossplane.io.] + inputPath: testdata/degraded_reconcileError.yaml +- healthStatus: + status: Suspended + message: ReconcilePaused + inputPath: testdata/suspended_reconcilePaused.yaml +- healthStatus: + status: Healthy + message: Available + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/heatlh.lua b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/heatlh.lua new file mode 100644 index 0000000000000..0cf5253e910ff --- /dev/null +++ b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/heatlh.lua @@ -0,0 +1,41 @@ +local hs = {} +if obj.status ~= nil then + if obj.status.conditions ~= nil then + local ready = false + local synced = false + local suspended = false + for i, condition in ipairs(obj.status.conditions) do + + if condition.type == "Ready" then + ready = condition.status == "True" + ready_message = condition.reason + elseif condition.type == "Synced" then + synced = condition.status == "True" + if condition.reason == "ReconcileError" then + synced_message = condition.message + elseif condition.reason == "ReconcilePaused" then + suspended = true + suspended_message = condition.reason + end + end + end + if ready and synced then + hs.status = "Healthy" + hs.message = ready_message + elseif synced == false and suspended == true then + hs.status = "Suspended" + hs.message = suspended_message + elseif ready == false and synced == true and suspended == false then + hs.status = "Progressing" + hs.message = "Waiting for resourcrecordset to be available" + else + hs.status = "Degraded" + hs.message = synced_message + end + return hs + end +end + +hs.status = "Progressing" +hs.message = "Waiting for resourcrecordset to be created" +return hs diff --git a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/degraded_reconcileError.yaml b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/degraded_reconcileError.yaml new file mode 100644 index 0000000000000..31bc5123c7bfd --- /dev/null +++ b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/degraded_reconcileError.yaml @@ -0,0 +1,35 @@ +apiVersion: route53.aws.crossplane.io/v1alpha1 +kind: ResourceRecordSet +metadata: + creationTimestamp: '2024-01-11T03:48:32Z' + generation: 1 + name: www-domain + resourceVersion: '187731157' + selfLink: /apis/route53.aws.crossplane.io/v1alpha1/resourcerecordsets/www-domain + uid: c9c85395-0830-4549-b255-e9e426663547 +spec: + providerConfigRef: + name: crossplane + forProvider: + resourceRecords: + - value: www.crossplane.io + setIdentifier: www + ttl: 60 + type: CNAME + weight: 0 + zoneId: ABCDEFGAB07CD +status: + conditions: + - lastTransitionTime: '2024-01-11T03:48:57Z' + message: >- + create failed: failed to create the ResourceRecordSet resource: + InvalidChangeBatch: [RRSet of type CNAME with DNS name + www.crossplane.io. is not permitted as it conflicts with other + records with the same DNS name in zone crossplane.io.] + reason: ReconcileError + status: 'False' + type: Synced + - lastTransitionTime: '2024-01-11T03:48:34Z' + reason: Creating + status: 'False' + type: Ready diff --git a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/healthy.yaml b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/healthy.yaml new file mode 100644 index 0000000000000..f808e46cc8c92 --- /dev/null +++ b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/healthy.yaml @@ -0,0 +1,29 @@ +apiVersion: route53.aws.crossplane.io/v1alpha1 +kind: ResourceRecordSet +metadata: + creationTimestamp: "2023-11-16T04:44:19Z" + generation: 4 + name: www-domain + resourceVersion: "140397563" + selfLink: /apis/route53.aws.crossplane.io/v1alpha1/resourcerecordsets/www-domain + uid: 11f0d48d-134f-471b-9340-b6d45d953fcb +spec: + providerConfigRef: + name: crossplane + forProvider: + zoneId: A1B2C3D4 + type: A + aliasTarget: + dnsName: abcdefg.cloudfront.net. + evaluateTargetHealth: false + hostedZoneId: AZBZCZDEFG +status: + conditions: + - lastTransitionTime: "2023-11-16T04:44:27Z" + reason: Available + status: "True" + type: Ready + - lastTransitionTime: "2023-11-16T04:44:25Z" + reason: ReconcileSuccess + status: "True" + type: Synced diff --git a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/progressing_creating.yaml b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/progressing_creating.yaml new file mode 100644 index 0000000000000..abf59775fb8e0 --- /dev/null +++ b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/progressing_creating.yaml @@ -0,0 +1,29 @@ +apiVersion: route53.aws.crossplane.io/v1alpha1 +kind: ResourceRecordSet +metadata: + creationTimestamp: "2023-11-16T04:44:19Z" + generation: 4 + name: www-domain + resourceVersion: "140397563" + selfLink: /apis/route53.aws.crossplane.io/v1alpha1/resourcerecordsets/www-domain + uid: 11f0d48d-134f-471b-9340-b6d45d953fcb +spec: + providerConfigRef: + name: crossplane + forProvider: + zoneId: A1B2C3D4 + type: A + aliasTarget: + dnsName: abcdefg.cloudfront.net. + evaluateTargetHealth: false + hostedZoneId: AZBZCZDEFG +status: + conditions: + - lastTransitionTime: "2023-11-16T04:44:27Z" + reason: Creating + status: "False" + type: Ready + - lastTransitionTime: "2023-11-16T04:44:25Z" + reason: ReconcileSuccess + status: "True" + type: Synced diff --git a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/progressing_noStatus.yaml b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/progressing_noStatus.yaml new file mode 100644 index 0000000000000..28d778d055050 --- /dev/null +++ b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/progressing_noStatus.yaml @@ -0,0 +1,19 @@ +apiVersion: route53.aws.crossplane.io/v1alpha1 +kind: ResourceRecordSet +metadata: + creationTimestamp: "2023-11-16T04:44:19Z" + generation: 4 + name: www-domain + resourceVersion: "140397563" + selfLink: /apis/route53.aws.crossplane.io/v1alpha1/resourcerecordsets/www-domain + uid: 11f0d48d-134f-471b-9340-b6d45d953fcb +spec: + providerConfigRef: + name: crossplane + forProvider: + zoneId: A1B2C3D4 + type: A + aliasTarget: + dnsName: abcdefg.cloudfront.net. + evaluateTargetHealth: false + hostedZoneId: AZBZCZDEFG diff --git a/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/suspended_reconcilePaused.yaml b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/suspended_reconcilePaused.yaml new file mode 100644 index 0000000000000..522c0e878dcf8 --- /dev/null +++ b/resource_customizations/route53.aws.crossplane.io/ResourceRecordSet/testdata/suspended_reconcilePaused.yaml @@ -0,0 +1,27 @@ +apiVersion: route53.aws.crossplane.io/v1alpha1 +kind: ResourceRecordSet +metadata: + annotations: + crossplane.io/paused: "true" + creationTimestamp: "2024-01-11T04:16:15Z" + generation: 1 + name: www-domain + resourceVersion: "187746011" + uid: 5517b419-5052-43d9-941e-c32f60d8c7e5 +spec: + providerConfigRef: + name: crossplane + forProvider: + resourceRecords: + - value: www.crossplane.io + setIdentifier: www + ttl: 60 + type: CNAME + weight: 0 + zoneId: ABCDEFGAB07CD +status: + conditions: + - lastTransitionTime: "2024-01-11T04:16:16Z" + reason: ReconcilePaused + status: "False" + type: Synced diff --git a/resource_customizations/serving.knative.dev/Service/health.lua b/resource_customizations/serving.knative.dev/Service/health.lua index 9867d8ab09df6..2a8c8a4683fd0 100644 --- a/resource_customizations/serving.knative.dev/Service/health.lua +++ b/resource_customizations/serving.knative.dev/Service/health.lua @@ -1,10 +1,10 @@ -health_status = {} +local health_status = {} health_status.status = "Progressing" health_status.message = "Waiting for status update." if obj.status ~= nil and obj.status.conditions ~= nil then - status_true = 0 - status_false = 0 - status_unknown = 0 + local status_true = 0 + local status_false = 0 + local status_unknown = 0 health_status.message = "" for i, condition in pairs(obj.status.conditions) do if condition.status == "True" and (condition.type == "ConfigurationsReady" or condition.type == "RoutesReady" or condition.type == "Ready") then diff --git a/resource_customizations/serving.kserve.io/InferenceService/health.lua b/resource_customizations/serving.kserve.io/InferenceService/health.lua new file mode 100644 index 0000000000000..fbcfbf77820f9 --- /dev/null +++ b/resource_customizations/serving.kserve.io/InferenceService/health.lua @@ -0,0 +1,40 @@ +local health_status = {} +health_status.status = "Progressing" +health_status.message = "Waiting for status update." +if obj.status ~= nil and obj.status.conditions ~= nil then + local status_true = 0 + local status_false = 0 + local status_unknown = 0 + health_status.message = "" + for i, condition in pairs(obj.status.conditions) do + if condition.status == "True" and (condition.type == "IngressReady" or condition.type == "PredictorConfigurationReady" or condition.type == "PredictorReady" or condition.type == "PredictorRouteReady" or condition.type == "Ready") then + status_true = status_true + 1 + elseif condition.status == "False" or condition.status == "Unknown" then + msg = condition.type .. " is " .. condition.status + if condition.reason ~= nil and condition.reason ~= "" then + msg = msg .. ", since " .. condition.reason .. "." + end + if condition.message ~= nil and condition.message ~= "" then + msg = msg .. " " .. condition.message + end + health_status.message = health_status.message .. msg .. "\n" + if condition.status == "False" then + status_false = status_false + 1 + else + status_unknown = status_unknown + 1 + end + end + end + if status_true == 5 and status_false == 0 and status_unknown == 0 then + health_status.message = "Inference Service is healthy." + health_status.status = "Healthy" + return health_status + elseif status_false > 0 then + health_status.status = "Degraded" + return health_status + else + health_status.status = "Progressing" + return health_status + end +end +return health_status \ No newline at end of file diff --git a/resource_customizations/serving.kserve.io/InferenceService/health_test.yaml b/resource_customizations/serving.kserve.io/InferenceService/health_test.yaml new file mode 100644 index 0000000000000..e8f32bd51f798 --- /dev/null +++ b/resource_customizations/serving.kserve.io/InferenceService/health_test.yaml @@ -0,0 +1,13 @@ +tests: +- healthStatus: + status: Progressing + message: "PredictorConfigurationReady is Unknown\nPredictorReady is Unknown, since RevisionMissing. Configuration \"hello-world-predictor-default\" is waiting for a Revision to become ready.\nPredictorRouteReady is Unknown, since RevisionMissing. Configuration \"hello-world-predictor-default\" is waiting for a Revision to become ready.\nReady is Unknown, since RevisionMissing. Configuration \"hello-world-predictor-default\" is waiting for a Revision to become ready.\n" + inputPath: testdata/progressing.yaml +- healthStatus: + status: Degraded + message: "IngressReady is False, since Predictor ingress not created.\nPredictorConfigurationReady is False, since RevisionFailed. Revision \"helloworld-00002\" failed with message: Container failed with: container exited with no error.\nPredictorReady is False, since RevisionFailed. Revision \"helloworld-00002\" failed with message: Container failed with: container exited with no error.\nReady is False, since Predictor ingress not created.\n" + inputPath: testdata/degraded.yaml +- healthStatus: + status: Healthy + message: Inference Service is healthy. + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/serving.kserve.io/InferenceService/testdata/degraded.yaml b/resource_customizations/serving.kserve.io/InferenceService/testdata/degraded.yaml new file mode 100644 index 0000000000000..0cd337860c670 --- /dev/null +++ b/resource_customizations/serving.kserve.io/InferenceService/testdata/degraded.yaml @@ -0,0 +1,30 @@ +apiVersion: serving.kserve.io/v1beta1 +kind: InferenceService +metadata: + name: helloworld + namespace: default +spec: {} +status: + conditions: + - lastTransitionTime: "2022-06-14T03:45:38Z" + reason: Predictor ingress not created + status: "False" + type: IngressReady + - lastTransitionTime: "2022-06-14T03:45:38Z" + message: 'Revision "helloworld-00002" failed with message: Container failed with: container exited with no error.' + reason: RevisionFailed + status: "False" + type: PredictorConfigurationReady + - lastTransitionTime: "2022-06-14T03:45:38Z" + message: 'Revision "helloworld-00002" failed with message: Container failed with: container exited with no error.' + reason: RevisionFailed + status: "False" + type: PredictorReady + - lastTransitionTime: "2022-06-14T03:45:38Z" + severity: Info + status: "True" + type: PredictorRouteReady + - lastTransitionTime: "2022-06-14T03:45:38Z" + reason: Predictor ingress not created + status: "False" + type: Ready diff --git a/resource_customizations/serving.kserve.io/InferenceService/testdata/healthy.yaml b/resource_customizations/serving.kserve.io/InferenceService/testdata/healthy.yaml new file mode 100644 index 0000000000000..3c28c61d48602 --- /dev/null +++ b/resource_customizations/serving.kserve.io/InferenceService/testdata/healthy.yaml @@ -0,0 +1,25 @@ +apiVersion: serving.kserve.io/v1beta1 +kind: InferenceService +metadata: + name: helloworld + namespace: default +spec: {} +status: + conditions: + - lastTransitionTime: "2023-06-20T22:44:51Z" + status: "True" + type: IngressReady + - lastTransitionTime: "2023-06-20T22:44:50Z" + severity: Info + status: "True" + type: PredictorConfigurationReady + - lastTransitionTime: "2023-06-20T22:44:51Z" + status: "True" + type: PredictorReady + - lastTransitionTime: "2023-06-20T22:44:51Z" + severity: Info + status: "True" + type: PredictorRouteReady + - lastTransitionTime: "2023-06-20T22:44:51Z" + status: "True" + type: Ready diff --git a/resource_customizations/serving.kserve.io/InferenceService/testdata/progressing.yaml b/resource_customizations/serving.kserve.io/InferenceService/testdata/progressing.yaml new file mode 100644 index 0000000000000..fab0a57b61f23 --- /dev/null +++ b/resource_customizations/serving.kserve.io/InferenceService/testdata/progressing.yaml @@ -0,0 +1,28 @@ +apiVersion: serving.kserve.io/v1beta1 +kind: InferenceService +metadata: + name: helloworld + namespace: default +spec: {} +status: + conditions: + - lastTransitionTime: "2023-06-21T22:25:58Z" + severity: Info + status: Unknown + type: PredictorConfigurationReady + - lastTransitionTime: "2023-06-21T22:25:58Z" + message: 'Configuration "hello-world-predictor-default" is waiting for a Revision to become ready.' + reason: RevisionMissing + status: Unknown + type: PredictorReady + - lastTransitionTime: "2023-06-21T22:25:58Z" + message: 'Configuration "hello-world-predictor-default" is waiting for a Revision to become ready.' + reason: RevisionMissing + severity: Info + status: Unknown + type: PredictorRouteReady + - lastTransitionTime: "2023-06-21T22:25:58Z" + message: 'Configuration "hello-world-predictor-default" is waiting for a Revision to become ready.' + reason: RevisionMissing + status: Unknown + type: Ready diff --git a/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/health.lua b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/health.lua new file mode 100644 index 0000000000000..082de2ca45761 --- /dev/null +++ b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/health.lua @@ -0,0 +1,18 @@ +local hs = {} + +if obj.status ~= nil and obj.status.readyToUse then + hs.status = "Healthy" + hs.message = "Ready to use" + return hs +end + +if obj.status ~= nil and obj.status.error ~= nil then + hs.status = "Degraded" + hs.message = obj.status.error.message + return hs +end + +hs.status = "Progressing" +hs.message = "Waiting for status" + +return hs diff --git a/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/health_test.yaml b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/health_test.yaml new file mode 100644 index 0000000000000..7914d4acdd3d8 --- /dev/null +++ b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/health_test.yaml @@ -0,0 +1,14 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/initializing.yaml +- healthStatus: + status: Healthy + message: "Ready to use" + inputPath: testdata/good.yaml +- healthStatus: + status: Degraded + message: "VolumeSnapshotContent is dynamically provisioned while expecting a pre-provisioned one" + inputPath: testdata/bad.yaml + diff --git a/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/testdata/bad.yaml b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/testdata/bad.yaml new file mode 100644 index 0000000000000..2d7447f1334e7 --- /dev/null +++ b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/testdata/bad.yaml @@ -0,0 +1,14 @@ +apiVersion: snapshot.storage.k8s.io/v1 +kind: VolumeSnapshot +metadata: + name: data-04-06-2023 +spec: + source: + volumeSnapshotContentName: data-04-06-2023 +status: + error: + message: >- + VolumeSnapshotContent is dynamically provisioned while expecting a + pre-provisioned one + time: '2023-06-05T14:51:25Z' + readyToUse: false diff --git a/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/testdata/good.yaml b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/testdata/good.yaml new file mode 100644 index 0000000000000..b8a82eff5b45c --- /dev/null +++ b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/testdata/good.yaml @@ -0,0 +1,15 @@ +apiVersion: snapshot.storage.k8s.io/v1 +kind: VolumeSnapshot +metadata: + finalizers: + - snapshot.storage.kubernetes.io/volumesnapshot-as-source-protection + - snapshot.storage.kubernetes.io/volumesnapshot-bound-protection +status: + boundVolumeSnapshotContentName: snapcontent-7db10be0-424c-4ed2-9dfe-6c2120eae05b + creationTime: '2023-06-04T19:13:20Z' + readyToUse: true + restoreSize: 1Ti +spec: + source: + persistentVolumeClaimName: mask-data-process-trcxk-mysql-data + volumeSnapshotClassName: azure-tools diff --git a/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/testdata/initializing.yaml b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/testdata/initializing.yaml new file mode 100644 index 0000000000000..3df029d9a46cf --- /dev/null +++ b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshot/testdata/initializing.yaml @@ -0,0 +1,7 @@ +apiVersion: snapshot.storage.k8s.io/v1 +kind: VolumeSnapshot +metadata: + name: data-04-06-2023 +spec: + driver: disk.csi.azure.com +status: {} diff --git a/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/health.lua b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/health.lua new file mode 100644 index 0000000000000..082de2ca45761 --- /dev/null +++ b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/health.lua @@ -0,0 +1,18 @@ +local hs = {} + +if obj.status ~= nil and obj.status.readyToUse then + hs.status = "Healthy" + hs.message = "Ready to use" + return hs +end + +if obj.status ~= nil and obj.status.error ~= nil then + hs.status = "Degraded" + hs.message = obj.status.error.message + return hs +end + +hs.status = "Progressing" +hs.message = "Waiting for status" + +return hs diff --git a/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/health_test.yaml b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/health_test.yaml new file mode 100644 index 0000000000000..6cc455afabe0a --- /dev/null +++ b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/health_test.yaml @@ -0,0 +1,13 @@ +tests: +- healthStatus: + status: Progressing + message: "Waiting for status" + inputPath: testdata/initializing.yaml +- healthStatus: + status: Healthy + message: "Ready to use" + inputPath: testdata/good.yaml +- healthStatus: + status: Degraded + message: "Failed to check and update snapshot content" + inputPath: testdata/bad.yaml diff --git a/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/testdata/bad.yaml b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/testdata/bad.yaml new file mode 100644 index 0000000000000..d8d3d3d7b5ff0 --- /dev/null +++ b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/testdata/bad.yaml @@ -0,0 +1,12 @@ +apiVersion: snapshot.storage.k8s.io/v1 +kind: VolumeSnapshotContent +metadata: + name: data-04-06-2023 +spec: + driver: disk.csi.azure.com +status: + error: + message: >- + Failed to check and update snapshot content + time: '2023-06-05T15:44:50Z' + readyToUse: false diff --git a/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/testdata/good.yaml b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/testdata/good.yaml new file mode 100644 index 0000000000000..56166bec0c859 --- /dev/null +++ b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/testdata/good.yaml @@ -0,0 +1,20 @@ +apiVersion: snapshot.storage.k8s.io/v1 +kind: VolumeSnapshotContent +metadata: + creationTimestamp: '2023-06-04T19:13:19Z' + finalizers: + - snapshot.storage.kubernetes.io/volumesnapshotcontent-bound-protection +status: + creationTime: 1685906000388294100 + readyToUse: true + restoreSize: 1099511627776 + snapshotHandle: >- + /subscriptions/XXXXXX +spec: + driver: disk.csi.azure.com + source: + volumeHandle: >- + /subscriptions/XXXXXX + volumeSnapshotClassName: azure-tools + volumeSnapshotRef: + apiVersion: snapshot.storage.k8s.io/v1 diff --git a/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/testdata/initializing.yaml b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/testdata/initializing.yaml new file mode 100644 index 0000000000000..8558cf3e44966 --- /dev/null +++ b/resource_customizations/snapshot.storage.k8s.io/VolumeSnapshotContent/testdata/initializing.yaml @@ -0,0 +1,7 @@ +apiVersion: snapshot.storage.k8s.io/v1 +kind: VolumeSnapshotContent +metadata: + name: data-04-06-2023 +spec: + driver: disk.csi.azure.com +status: {} diff --git a/resource_customizations/sparkoperator.k8s.io/SparkApplication/health.lua b/resource_customizations/sparkoperator.k8s.io/SparkApplication/health.lua index 98e8feee227db..c900823186dae 100644 --- a/resource_customizations/sparkoperator.k8s.io/SparkApplication/health.lua +++ b/resource_customizations/sparkoperator.k8s.io/SparkApplication/health.lua @@ -1,4 +1,63 @@ -health_status = {} +local health_status = {} +-- Can't use standard lib, math.huge equivalent +local infinity = 2^1024-1 + +local function executor_range_api() + local min_executor_instances = 0 + local max_executor_instances = infinity + if obj.spec.dynamicAllocation.maxExecutors then + max_executor_instances = obj.spec.dynamicAllocation.maxExecutors + end + if obj.spec.dynamicAllocation.minExecutors then + min_executor_instances = obj.spec.dynamicAllocation.minExecutors + end + return min_executor_instances, max_executor_instances +end + +local function maybe_executor_range_spark_conf() + local min_executor_instances = 0 + local max_executor_instances = infinity + if obj.spec.sparkConf["spark.streaming.dynamicAllocation.enabled"] ~= nil and + obj.spec.sparkConf["spark.streaming.dynamicAllocation.enabled"] == "true" then + if(obj.spec.sparkConf["spark.streaming.dynamicAllocation.maxExecutors"] ~= nil) then + max_executor_instances = tonumber(obj.spec.sparkConf["spark.streaming.dynamicAllocation.maxExecutors"]) + end + if(obj.spec.sparkConf["spark.streaming.dynamicAllocation.minExecutors"] ~= nil) then + min_executor_instances = tonumber(obj.spec.sparkConf["spark.streaming.dynamicAllocation.minExecutors"]) + end + return min_executor_instances, max_executor_instances + elseif obj.spec.sparkConf["spark.dynamicAllocation.enabled"] ~= nil and + obj.spec.sparkConf["spark.dynamicAllocation.enabled"] == "true" then + if(obj.spec.sparkConf["spark.dynamicAllocation.maxExecutors"] ~= nil) then + max_executor_instances = tonumber(obj.spec.sparkConf["spark.dynamicAllocation.maxExecutors"]) + end + if(obj.spec.sparkConf["spark.dynamicAllocation.minExecutors"] ~= nil) then + min_executor_instances = tonumber(obj.spec.sparkConf["spark.dynamicAllocation.minExecutors"]) + end + return min_executor_instances, max_executor_instances + else + return nil + end +end + +local function maybe_executor_range() + if obj.spec["dynamicAllocation"] and obj.spec.dynamicAllocation.enabled then + return executor_range_api() + elseif obj.spec["sparkConf"] ~= nil then + return maybe_executor_range_spark_conf() + else + return nil + end +end + +local function dynamic_executors_without_spec_config() + if obj.spec.dynamicAllocation == nil and obj.spec.executor.instances == nil then + return true + else + return false + end +end + if obj.status ~= nil then if obj.status.applicationState.state ~= nil then if obj.status.applicationState.state == "" then @@ -9,13 +68,23 @@ if obj.status ~= nil then if obj.status.applicationState.state == "RUNNING" then if obj.status.executorState ~= nil then count=0 - executor_instances = obj.spec.executor.instances for i, executorState in pairs(obj.status.executorState) do if executorState == "RUNNING" then count=count+1 end end - if executor_instances == count then + if obj.spec.executor.instances ~= nil and obj.spec.executor.instances == count then + health_status.status = "Healthy" + health_status.message = "SparkApplication is Running" + return health_status + elseif maybe_executor_range() then + local min_executor_instances, max_executor_instances = maybe_executor_range() + if count >= min_executor_instances and count <= max_executor_instances then + health_status.status = "Healthy" + health_status.message = "SparkApplication is Running" + return health_status + end + elseif dynamic_executors_without_spec_config() and count >= 1 then health_status.status = "Healthy" health_status.message = "SparkApplication is Running" return health_status @@ -72,4 +141,4 @@ if obj.status ~= nil then end health_status.status = "Progressing" health_status.message = "Waiting for Executor pods" -return health_status \ No newline at end of file +return health_status diff --git a/resource_customizations/sparkoperator.k8s.io/SparkApplication/health_test.yaml b/resource_customizations/sparkoperator.k8s.io/SparkApplication/health_test.yaml index 7c971cbe666b8..e0ad7dfdf387d 100644 --- a/resource_customizations/sparkoperator.k8s.io/SparkApplication/health_test.yaml +++ b/resource_customizations/sparkoperator.k8s.io/SparkApplication/health_test.yaml @@ -11,3 +11,19 @@ tests: status: Healthy message: "SparkApplication is Running" inputPath: testdata/healthy.yaml +- healthStatus: + status: Healthy + message: "SparkApplication is Running" + inputPath: testdata/healthy_dynamic_alloc.yaml +- healthStatus: + status: Healthy + message: "SparkApplication is Running" + inputPath: testdata/healthy_dynamic_alloc_dstream.yaml +- healthStatus: + status: Healthy + message: "SparkApplication is Running" + inputPath: testdata/healthy_dynamic_alloc_operator_api.yaml +- healthStatus: + status: Healthy + message: "SparkApplication is Running" + inputPath: testdata/healthy_dynamic_alloc_without_spec_config.yaml diff --git a/resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc.yaml b/resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc.yaml new file mode 100644 index 0000000000000..9ff52e78b98c5 --- /dev/null +++ b/resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc.yaml @@ -0,0 +1,37 @@ +apiVersion: sparkoperator.k8s.io/v1beta2 +kind: SparkApplication +metadata: + generation: 4 + labels: + argocd.argoproj.io/instance: spark-job + name: spark-job-app + namespace: spark-cluster + resourceVersion: "31812990" + uid: bfee52b0-74ca-4465-8005-f6643097ed64 +spec: + executor: + instances: 4 + sparkConf: + spark.dynamicAllocation.enabled: 'true' + spark.dynamicAllocation.maxExecutors: '10' + spark.dynamicAllocation.minExecutors: '2' +status: + applicationState: + state: RUNNING + driverInfo: + podName: ingestion-datalake-news-app-driver + webUIAddress: 172.20.207.161:4040 + webUIPort: 4040 + webUIServiceName: ingestion-datalake-news-app-ui-svc + executionAttempts: 13 + executorState: + ingestion-datalake-news-app-1591613851251-exec-1: RUNNING + ingestion-datalake-news-app-1591613851251-exec-2: RUNNING + ingestion-datalake-news-app-1591613851251-exec-4: RUNNING + ingestion-datalake-news-app-1591613851251-exec-5: RUNNING + ingestion-datalake-news-app-1591613851251-exec-6: RUNNING + lastSubmissionAttemptTime: "2020-06-08T10:57:32Z" + sparkApplicationId: spark-a5920b2a5aa04d22a737c60759b5bf82 + submissionAttempts: 1 + submissionID: 3e713ec8-9f6c-4e78-ac28-749797c846f0 + terminationTime: null diff --git a/resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc_dstream.yaml b/resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc_dstream.yaml new file mode 100644 index 0000000000000..ce24ff77177d2 --- /dev/null +++ b/resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc_dstream.yaml @@ -0,0 +1,35 @@ +apiVersion: sparkoperator.k8s.io/v1beta2 +kind: SparkApplication +metadata: + generation: 4 + labels: + argocd.argoproj.io/instance: spark-job + name: spark-job-app + namespace: spark-cluster + resourceVersion: "31812990" + uid: bfee52b0-74ca-4465-8005-f6643097ed64 +spec: + executor: + instances: 4 + sparkConf: + spark.streaming.dynamicAllocation.enabled: 'true' + spark.streaming.dynamicAllocation.maxExecutors: '10' + spark.streaming.dynamicAllocation.minExecutors: '2' +status: + applicationState: + state: RUNNING + driverInfo: + podName: ingestion-datalake-news-app-driver + webUIAddress: 172.20.207.161:4040 + webUIPort: 4040 + webUIServiceName: ingestion-datalake-news-app-ui-svc + executionAttempts: 13 + executorState: + ingestion-datalake-news-app-1591613851251-exec-1: RUNNING + ingestion-datalake-news-app-1591613851251-exec-4: RUNNING + ingestion-datalake-news-app-1591613851251-exec-6: RUNNING + lastSubmissionAttemptTime: "2020-06-08T10:57:32Z" + sparkApplicationId: spark-a5920b2a5aa04d22a737c60759b5bf82 + submissionAttempts: 1 + submissionID: 3e713ec8-9f6c-4e78-ac28-749797c846f0 + terminationTime: null diff --git a/resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc_operator_api.yaml b/resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc_operator_api.yaml new file mode 100644 index 0000000000000..538a27991bb5a --- /dev/null +++ b/resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc_operator_api.yaml @@ -0,0 +1,38 @@ +apiVersion: sparkoperator.k8s.io/v1beta2 +kind: SparkApplication +metadata: + generation: 4 + labels: + argocd.argoproj.io/instance: spark-job + name: spark-job-app + namespace: spark-cluster + resourceVersion: "31812990" + uid: bfee52b0-74ca-4465-8005-f6643097ed64 +spec: + executor: + instances: 4 + dynamicAllocation: + enabled: true + initialExecutors: 2 + minExecutors: 2 + maxExecutors: 10 +status: + applicationState: + state: RUNNING + driverInfo: + podName: ingestion-datalake-news-app-driver + webUIAddress: 172.20.207.161:4040 + webUIPort: 4040 + webUIServiceName: ingestion-datalake-news-app-ui-svc + executionAttempts: 13 + executorState: + ingestion-datalake-news-app-1591613851251-exec-1: RUNNING + ingestion-datalake-news-app-1591613851251-exec-2: RUNNING + ingestion-datalake-news-app-1591613851251-exec-4: RUNNING + ingestion-datalake-news-app-1591613851251-exec-5: RUNNING + ingestion-datalake-news-app-1591613851251-exec-6: RUNNING + lastSubmissionAttemptTime: "2020-06-08T10:57:32Z" + sparkApplicationId: spark-a5920b2a5aa04d22a737c60759b5bf82 + submissionAttempts: 1 + submissionID: 3e713ec8-9f6c-4e78-ac28-749797c846f0 + terminationTime: null diff --git a/resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc_without_spec_config.yaml b/resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc_without_spec_config.yaml new file mode 100644 index 0000000000000..a2ab7b85b5c50 --- /dev/null +++ b/resource_customizations/sparkoperator.k8s.io/SparkApplication/testdata/healthy_dynamic_alloc_without_spec_config.yaml @@ -0,0 +1,31 @@ +apiVersion: sparkoperator.k8s.io/v1beta2 +kind: SparkApplication +metadata: + generation: 4 + labels: + argocd.argoproj.io/instance: spark-job + name: spark-job-app + namespace: spark-cluster + resourceVersion: "31812990" + uid: bfee52b0-74ca-4465-8005-f6643097ed64 +spec: + executor: {} +status: + applicationState: + state: RUNNING + driverInfo: + podName: ingestion-datalake-news-app-driver + webUIAddress: 172.20.207.161:4040 + webUIPort: 4040 + webUIServiceName: ingestion-datalake-news-app-ui-svc + executionAttempts: 13 + executorState: + ingestion-datalake-news-app-1591613851251-exec-1: RUNNING + ingestion-datalake-news-app-1591613851251-exec-2: RUNNING + ingestion-datalake-news-app-1591613851251-exec-4: RUNNING + ingestion-datalake-news-app-1591613851251-exec-5: RUNNING + lastSubmissionAttemptTime: "2020-06-08T10:57:32Z" + sparkApplicationId: spark-a5920b2a5aa04d22a737c60759b5bf82 + submissionAttempts: 1 + submissionID: 3e713ec8-9f6c-4e78-ac28-749797c846f0 + terminationTime: null diff --git a/resource_customizations/spot.io/SpotDeployment/health.lua b/resource_customizations/spot.io/SpotDeployment/health.lua index 030e2c5493ba8..cd39bcaa1325e 100644 --- a/resource_customizations/spot.io/SpotDeployment/health.lua +++ b/resource_customizations/spot.io/SpotDeployment/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status == nil or obj.status.conditions == nil then return hs diff --git a/resource_customizations/sql.cnrm.cloud.google.com/SQLDatabase/health.lua b/resource_customizations/sql.cnrm.cloud.google.com/SQLDatabase/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/sql.cnrm.cloud.google.com/SQLDatabase/health.lua +++ b/resource_customizations/sql.cnrm.cloud.google.com/SQLDatabase/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/sql.cnrm.cloud.google.com/SQLInstance/health.lua b/resource_customizations/sql.cnrm.cloud.google.com/SQLInstance/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/sql.cnrm.cloud.google.com/SQLInstance/health.lua +++ b/resource_customizations/sql.cnrm.cloud.google.com/SQLInstance/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/stacks.crossplane.io/ClusterStackInstall/health.lua b/resource_customizations/stacks.crossplane.io/ClusterStackInstall/health.lua index 9a213cd956a9f..9e94c5fb80660 100644 --- a/resource_customizations/stacks.crossplane.io/ClusterStackInstall/health.lua +++ b/resource_customizations/stacks.crossplane.io/ClusterStackInstall/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Waiting for stack to be installed" } diff --git a/resource_customizations/storage.cnrm.cloud.google.com/StorageBucket/health.lua b/resource_customizations/storage.cnrm.cloud.google.com/StorageBucket/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/storage.cnrm.cloud.google.com/StorageBucket/health.lua +++ b/resource_customizations/storage.cnrm.cloud.google.com/StorageBucket/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/storage.cnrm.cloud.google.com/StorageBucketAccessControl/health.lua b/resource_customizations/storage.cnrm.cloud.google.com/StorageBucketAccessControl/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/storage.cnrm.cloud.google.com/StorageBucketAccessControl/health.lua +++ b/resource_customizations/storage.cnrm.cloud.google.com/StorageBucketAccessControl/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/storage.cnrm.cloud.google.com/StorageDefaultObjectAccessControl/health.lua b/resource_customizations/storage.cnrm.cloud.google.com/StorageDefaultObjectAccessControl/health.lua index 63ce5d12a4fbf..585b5e27a3e98 100644 --- a/resource_customizations/storage.cnrm.cloud.google.com/StorageDefaultObjectAccessControl/health.lua +++ b/resource_customizations/storage.cnrm.cloud.google.com/StorageDefaultObjectAccessControl/health.lua @@ -1,4 +1,4 @@ -hs = { +local hs = { status = "Progressing", message = "Update in progress" } diff --git a/resource_customizations/tower.ansible.com/AnsibleJob/health.lua b/resource_customizations/tower.ansible.com/AnsibleJob/health.lua new file mode 100644 index 0000000000000..1e4a514c500e1 --- /dev/null +++ b/resource_customizations/tower.ansible.com/AnsibleJob/health.lua @@ -0,0 +1,25 @@ +hs = {} +if obj.status ~= nil then + if obj.status.ansibleJobResult ~= nil then + jobstatus = obj.status.ansibleJobResult.status + if jobstatus == "successful" then + hs.status = "Healthy" + hs.message = jobstatus .. " job - " .. obj.status.ansibleJobResult.url + return hs + end + if jobstatus == "failed" or jobstatus == "error" or jobstatus == "canceled" then + hs.status = "Degraded" + hs.message = jobstatus .. " job - " .. obj.status.ansibleJobResult.url + return hs + end + if jobstatus == "new" or jobstatus == "pending" or jobstatus == "waiting" or jobstatus == "running" then + hs.status = "Progressing" + hs.message = jobstatus .. " job - " .. obj.status.ansibleJobResult.url + return hs + end + end +end + +hs.status = "Progressing" +hs.message = "Waiting for AnsibleJob" +return hs diff --git a/resource_customizations/tower.ansible.com/AnsibleJob/health_test.yaml b/resource_customizations/tower.ansible.com/AnsibleJob/health_test.yaml new file mode 100644 index 0000000000000..bb4143ae5f5a3 --- /dev/null +++ b/resource_customizations/tower.ansible.com/AnsibleJob/health_test.yaml @@ -0,0 +1,37 @@ +tests: +- healthStatus: + status: Progressing + message: Waiting for AnsibleJob + inputPath: testdata/progressing_noStatus.yaml +- healthStatus: + status: Progressing + message: 'new job - https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1' + inputPath: testdata/progressing_new.yaml +- healthStatus: + status: Progressing + message: 'pending job - https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1' + inputPath: testdata/progressing_pending.yaml +- healthStatus: + status: Progressing + message: 'running job - https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1' + inputPath: testdata/progressing_running.yaml +- healthStatus: + status: Progressing + message: 'waiting job - https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1' + inputPath: testdata/progressing_waiting.yaml +- healthStatus: + status: Degraded + message: 'canceled job - https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1' + inputPath: testdata/degraded_canceled.yaml +- healthStatus: + status: Degraded + message: 'error job - https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1' + inputPath: testdata/degraded_error.yaml +- healthStatus: + status: Degraded + message: 'failed job - https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1' + inputPath: testdata/degraded_failed.yaml +- healthStatus: + status: Healthy + message: 'successful job - https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1' + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/tower.ansible.com/AnsibleJob/testdata/degraded_canceled.yaml b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/degraded_canceled.yaml new file mode 100644 index 0000000000000..55fa0cdec7767 --- /dev/null +++ b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/degraded_canceled.yaml @@ -0,0 +1,27 @@ +apiVersion: tower.ansible.com/v1alpha1 +kind: AnsibleJob +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + creationTimestamp: "2023-06-27T20:22:22Z" + generateName: prehook-test- + generation: 1 + labels: + app.kubernetes.io/instance: ansible-hooks + tower_job_id: "1" + name: prehook-test-dfcff01-presync-1687897341 + namespace: argocd + resourceVersion: "6536518" + uid: 09fa0d39-a170-4c37-a3b0-6e140e029868 +spec: + job_template_name: Demo Job Template + tower_auth_secret: toweraccess +status: + ansibleJobResult: + changed: true + elapsed: "5.21" + failed: false + finished: "2023-06-27T20:22:40.116381Z" + started: "2023-06-27T20:22:34.906399Z" + status: canceled + url: https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1 diff --git a/resource_customizations/tower.ansible.com/AnsibleJob/testdata/degraded_error.yaml b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/degraded_error.yaml new file mode 100644 index 0000000000000..0ebb059ea8e2f --- /dev/null +++ b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/degraded_error.yaml @@ -0,0 +1,27 @@ +apiVersion: tower.ansible.com/v1alpha1 +kind: AnsibleJob +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + creationTimestamp: "2023-06-27T20:22:22Z" + generateName: prehook-test- + generation: 1 + labels: + app.kubernetes.io/instance: ansible-hooks + tower_job_id: "1" + name: prehook-test-dfcff01-presync-1687897341 + namespace: argocd + resourceVersion: "6536518" + uid: 09fa0d39-a170-4c37-a3b0-6e140e029868 +spec: + job_template_name: Demo Job Template + tower_auth_secret: toweraccess +status: + ansibleJobResult: + changed: true + elapsed: "5.21" + failed: true + finished: "2023-06-27T20:22:40.116381Z" + started: "2023-06-27T20:22:34.906399Z" + status: error + url: https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1 diff --git a/resource_customizations/tower.ansible.com/AnsibleJob/testdata/degraded_failed.yaml b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/degraded_failed.yaml new file mode 100644 index 0000000000000..0400570b15f7f --- /dev/null +++ b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/degraded_failed.yaml @@ -0,0 +1,27 @@ +apiVersion: tower.ansible.com/v1alpha1 +kind: AnsibleJob +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + creationTimestamp: "2023-06-27T20:22:22Z" + generateName: prehook-test- + generation: 1 + labels: + app.kubernetes.io/instance: ansible-hooks + tower_job_id: "1" + name: prehook-test-dfcff01-presync-1687897341 + namespace: argocd + resourceVersion: "6536518" + uid: 09fa0d39-a170-4c37-a3b0-6e140e029868 +spec: + job_template_name: Demo Job Template + tower_auth_secret: toweraccess +status: + ansibleJobResult: + changed: true + elapsed: "5.21" + failed: true + finished: "2023-06-27T20:22:40.116381Z" + started: "2023-06-27T20:22:34.906399Z" + status: failed + url: https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1 diff --git a/resource_customizations/tower.ansible.com/AnsibleJob/testdata/healthy.yaml b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/healthy.yaml new file mode 100644 index 0000000000000..395a1bd09625f --- /dev/null +++ b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/healthy.yaml @@ -0,0 +1,27 @@ +apiVersion: tower.ansible.com/v1alpha1 +kind: AnsibleJob +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + creationTimestamp: "2023-06-27T20:22:22Z" + generateName: prehook-test- + generation: 1 + labels: + app.kubernetes.io/instance: ansible-hooks + tower_job_id: "1" + name: prehook-test-dfcff01-presync-1687897341 + namespace: argocd + resourceVersion: "6536518" + uid: 09fa0d39-a170-4c37-a3b0-6e140e029868 +spec: + job_template_name: Demo Job Template + tower_auth_secret: toweraccess +status: + ansibleJobResult: + changed: true + elapsed: "5.21" + failed: false + finished: "2023-06-27T20:22:40.116381Z" + started: "2023-06-27T20:22:34.906399Z" + status: successful + url: https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1 diff --git a/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_new.yaml b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_new.yaml new file mode 100644 index 0000000000000..2e700d3708c58 --- /dev/null +++ b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_new.yaml @@ -0,0 +1,25 @@ +apiVersion: tower.ansible.com/v1alpha1 +kind: AnsibleJob +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + creationTimestamp: "2023-06-27T20:22:22Z" + generateName: prehook-test- + generation: 1 + labels: + app.kubernetes.io/instance: ansible-hooks + tower_job_id: "1" + name: prehook-test-dfcff01-presync-1687897341 + namespace: argocd + resourceVersion: "6536518" + uid: 09fa0d39-a170-4c37-a3b0-6e140e029868 +spec: + job_template_name: Demo Job Template + tower_auth_secret: toweraccess +status: + ansibleJobResult: + changed: true + failed: false + started: "2023-06-27T20:22:34.906399Z" + status: new + url: https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1 diff --git a/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_noStatus.yaml b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_noStatus.yaml new file mode 100644 index 0000000000000..a6e1701bf3268 --- /dev/null +++ b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_noStatus.yaml @@ -0,0 +1,17 @@ +apiVersion: tower.ansible.com/v1alpha1 +kind: AnsibleJob +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + creationTimestamp: "2023-06-27T20:22:22Z" + generateName: prehook-test- + generation: 1 + labels: + app.kubernetes.io/instance: ansible-hooks + name: prehook-test-dfcff01-presync-1687897341 + namespace: argocd + resourceVersion: "6536518" + uid: 09fa0d39-a170-4c37-a3b0-6e140e029868 +spec: + job_template_name: Demo Job Template + tower_auth_secret: toweraccess diff --git a/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_pending.yaml b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_pending.yaml new file mode 100644 index 0000000000000..ffabaf4ee64e6 --- /dev/null +++ b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_pending.yaml @@ -0,0 +1,25 @@ +apiVersion: tower.ansible.com/v1alpha1 +kind: AnsibleJob +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + creationTimestamp: "2023-06-27T20:22:22Z" + generateName: prehook-test- + generation: 1 + labels: + app.kubernetes.io/instance: ansible-hooks + tower_job_id: "1" + name: prehook-test-dfcff01-presync-1687897341 + namespace: argocd + resourceVersion: "6536518" + uid: 09fa0d39-a170-4c37-a3b0-6e140e029868 +spec: + job_template_name: Demo Job Template + tower_auth_secret: toweraccess +status: + ansibleJobResult: + changed: true + failed: false + started: "2023-06-27T20:22:34.906399Z" + status: pending + url: https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1 diff --git a/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_running.yaml b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_running.yaml new file mode 100644 index 0000000000000..6e369f0aaecea --- /dev/null +++ b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_running.yaml @@ -0,0 +1,25 @@ +apiVersion: tower.ansible.com/v1alpha1 +kind: AnsibleJob +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + creationTimestamp: "2023-06-27T20:22:22Z" + generateName: prehook-test- + generation: 1 + labels: + app.kubernetes.io/instance: ansible-hooks + tower_job_id: "1" + name: prehook-test-dfcff01-presync-1687897341 + namespace: argocd + resourceVersion: "6536518" + uid: 09fa0d39-a170-4c37-a3b0-6e140e029868 +spec: + job_template_name: Demo Job Template + tower_auth_secret: toweraccess +status: + ansibleJobResult: + changed: true + failed: false + started: "2023-06-27T20:22:34.906399Z" + status: running + url: https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1 diff --git a/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_waiting.yaml b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_waiting.yaml new file mode 100644 index 0000000000000..c6f192cca70b0 --- /dev/null +++ b/resource_customizations/tower.ansible.com/AnsibleJob/testdata/progressing_waiting.yaml @@ -0,0 +1,25 @@ +apiVersion: tower.ansible.com/v1alpha1 +kind: AnsibleJob +metadata: + annotations: + argocd.argoproj.io/hook: PreSync + creationTimestamp: "2023-06-27T20:22:22Z" + generateName: prehook-test- + generation: 1 + labels: + app.kubernetes.io/instance: ansible-hooks + tower_job_id: "1" + name: prehook-test-dfcff01-presync-1687897341 + namespace: argocd + resourceVersion: "6536518" + uid: 09fa0d39-a170-4c37-a3b0-6e140e029868 +spec: + job_template_name: Demo Job Template + tower_auth_secret: toweraccess +status: + ansibleJobResult: + changed: true + failed: false + started: "2023-06-27T20:22:34.906399Z" + status: waiting + url: https://argocd.test.ansiblejob.custom.health.com/#/jobs/playbook/1 diff --git a/resource_customizations/trident.netapp.io/TridentBackendConfig/health.lua b/resource_customizations/trident.netapp.io/TridentBackendConfig/health.lua index 8f8b0ca9c5d83..614915a4edc00 100644 --- a/resource_customizations/trident.netapp.io/TridentBackendConfig/health.lua +++ b/resource_customizations/trident.netapp.io/TridentBackendConfig/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.phase == "Bound" and obj.status.lastOperationStatus == "Success" then hs.status = "Healthy" diff --git a/resource_customizations/trident.netapp.io/TridentOrchestrator/health.lua b/resource_customizations/trident.netapp.io/TridentOrchestrator/health.lua index 4ceecef2f0ec0..18c3b89fab0de 100644 --- a/resource_customizations/trident.netapp.io/TridentOrchestrator/health.lua +++ b/resource_customizations/trident.netapp.io/TridentOrchestrator/health.lua @@ -1,4 +1,4 @@ -hs = {} +local hs = {} if obj.status ~= nil then if obj.status.status == "Installed" then hs.status = "Healthy" diff --git a/resource_customizations/work.karmada.io/ClusterResourceBinding/health.lua b/resource_customizations/work.karmada.io/ClusterResourceBinding/health.lua index 5098ae9fe2e56..5e94523c1f043 100644 --- a/resource_customizations/work.karmada.io/ClusterResourceBinding/health.lua +++ b/resource_customizations/work.karmada.io/ClusterResourceBinding/health.lua @@ -1,4 +1,4 @@ -health_status = {} +local health_status = {} if obj.status == nil then health_status.status = "Progressing" health_status.message = "Current resource status is insufficient" diff --git a/resource_customizations/work.karmada.io/ResourceBinding/health.lua b/resource_customizations/work.karmada.io/ResourceBinding/health.lua index 5098ae9fe2e56..5e94523c1f043 100644 --- a/resource_customizations/work.karmada.io/ResourceBinding/health.lua +++ b/resource_customizations/work.karmada.io/ResourceBinding/health.lua @@ -1,4 +1,4 @@ -health_status = {} +local health_status = {} if obj.status == nil then health_status.status = "Progressing" health_status.message = "Current resource status is insufficient" diff --git a/resource_customizations/zookeeper.pravega.io/ZookeeperCluster/health.lua b/resource_customizations/zookeeper.pravega.io/ZookeeperCluster/health.lua index 5228f53fb682c..9e597ae097f27 100644 --- a/resource_customizations/zookeeper.pravega.io/ZookeeperCluster/health.lua +++ b/resource_customizations/zookeeper.pravega.io/ZookeeperCluster/health.lua @@ -1,4 +1,4 @@ -health_status = {} +local health_status = {} if obj.status ~= nil then if obj.status.readyReplicas ~= 0 and obj.status.readyReplicas == obj.status.replicas then health_status.status = "Healthy" diff --git a/server/application/application.go b/server/application/application.go index dbdce9cafce2e..ec0db45a11f22 100644 --- a/server/application/application.go +++ b/server/application/application.go @@ -28,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes" @@ -46,10 +47,10 @@ import ( "github.com/argoproj/argo-cd/v2/server/rbacpolicy" "github.com/argoproj/argo-cd/v2/util/argo" argoutil "github.com/argoproj/argo-cd/v2/util/argo" + "github.com/argoproj/argo-cd/v2/util/collections" "github.com/argoproj/argo-cd/v2/util/db" "github.com/argoproj/argo-cd/v2/util/env" "github.com/argoproj/argo-cd/v2/util/git" - "github.com/argoproj/argo-cd/v2/util/glob" ioutil "github.com/argoproj/argo-cd/v2/util/io" "github.com/argoproj/argo-cd/v2/util/lua" "github.com/argoproj/argo-cd/v2/util/manifeststream" @@ -57,6 +58,8 @@ import ( "github.com/argoproj/argo-cd/v2/util/security" "github.com/argoproj/argo-cd/v2/util/session" "github.com/argoproj/argo-cd/v2/util/settings" + + applicationType "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) type AppResourceTreeFn func(ctx context.Context, app *appv1.Application) (*appv1.ApplicationTree, error) @@ -68,7 +71,8 @@ const ( ) var ( - watchAPIBufferSize = env.ParseNumFromEnv(argocommon.EnvWatchAPIBufferSize, 1000, 0, math.MaxInt32) + watchAPIBufferSize = env.ParseNumFromEnv(argocommon.EnvWatchAPIBufferSize, 1000, 0, math.MaxInt32) + permissionDeniedErr = status.Error(codes.PermissionDenied, "permission denied") ) // Server provides an Application service @@ -78,7 +82,7 @@ type Server struct { appclientset appclientset.Interface appLister applisters.ApplicationLister appInformer cache.SharedIndexInformer - appBroadcaster *broadcasterHandler + appBroadcaster Broadcaster repoClientset apiclient.Clientset kubectl kube.Kubectl db db.ArgoDB @@ -98,6 +102,7 @@ func NewServer( appclientset appclientset.Interface, appLister applisters.ApplicationLister, appInformer cache.SharedIndexInformer, + appBroadcaster Broadcaster, repoClientset apiclient.Clientset, cache *servercache.Cache, kubectl kube.Kubectl, @@ -108,8 +113,13 @@ func NewServer( projInformer cache.SharedIndexInformer, enabledNamespaces []string, ) (application.ApplicationServiceServer, AppResourceTreeFn) { - appBroadcaster := &broadcasterHandler{} - appInformer.AddEventHandler(appBroadcaster) + if appBroadcaster == nil { + appBroadcaster = &broadcasterHandler{} + } + _, err := appInformer.AddEventHandler(appBroadcaster) + if err != nil { + log.Error(err) + } s := &Server{ ns: namespace, appclientset: appclientset, @@ -131,6 +141,115 @@ func NewServer( return s, s.getAppResources } +// getAppEnforceRBAC gets the Application with the given name in the given namespace. If no namespace is +// specified, the Application is fetched from the default namespace (the one in which the API server is running). +// +// If the user does not provide a "project," then we have to be very careful how we respond. If an app with the given +// name exists, and the user has access to that app in the app's project, we return the app. If the app exists but the +// user does not have access, we return "permission denied." If the app does not exist, we return "permission denied" - +// if we responded with a 404, then the user could infer that the app exists when they get "permission denied." +// +// If the user does provide a "project," we can respond more specifically. If the user does not have access to the given +// app name in the given project, we return "permission denied." If the app exists, but the project is different from +func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespace, name string, getApp func() (*appv1.Application, error)) (*appv1.Application, error) { + user := session.Username(ctx) + if user == "" { + user = "Unknown user" + } + logCtx := log.WithFields(map[string]interface{}{ + "user": user, + "application": name, + "namespace": namespace, + }) + if project != "" { + // The user has provided everything we need to perform an initial RBAC check. + givenRBACName := security.RBACName(s.ns, project, namespace, name) + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, action, givenRBACName); err != nil { + logCtx.WithFields(map[string]interface{}{ + "project": project, + argocommon.SecurityField: argocommon.SecurityMedium, + }).Warnf("user tried to %s application which they do not have access to: %s", action, err) + // Do a GET on the app. This ensures that the timing of a "no access" response is the same as a "yes access, + // but the app is in a different project" response. We don't want the user inferring the existence of the + // app from response time. + _, _ = getApp() + return nil, permissionDeniedErr + } + } + a, err := getApp() + if err != nil { + if apierr.IsNotFound(err) { + if project != "" { + // We know that the user was allowed to get the Application, but the Application does not exist. Return 404. + return nil, status.Errorf(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error()) + } + // We don't know if the user was allowed to get the Application, and we don't want to leak information about + // the Application's existence. Return 403. + logCtx.Warn("application does not exist") + return nil, permissionDeniedErr + } + logCtx.Errorf("failed to get application: %s", err) + return nil, permissionDeniedErr + } + // Even if we performed an initial RBAC check (because the request was fully parameterized), we still need to + // perform a second RBAC check to ensure that the user has access to the actual Application's project (not just the + // project they specified in the request). + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, action, a.RBACName(s.ns)); err != nil { + logCtx.WithFields(map[string]interface{}{ + "project": a.Spec.Project, + argocommon.SecurityField: argocommon.SecurityMedium, + }).Warnf("user tried to %s application which they do not have access to: %s", action, err) + if project != "" { + // The user specified a project. We would have returned a 404 if the user had access to the app, but the app + // did not exist. So we have to return a 404 when the app does exist, but the user does not have access. + // Otherwise, they could infer that the app exists based on the error code. + return nil, status.Errorf(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error()) + } + // The user didn't specify a project. We always return permission denied for both lack of access and lack of + // existence. + return nil, permissionDeniedErr + } + effectiveProject := "default" + if a.Spec.Project != "" { + effectiveProject = a.Spec.Project + } + if project != "" && effectiveProject != project { + logCtx.WithFields(map[string]interface{}{ + "project": a.Spec.Project, + argocommon.SecurityField: argocommon.SecurityMedium, + }).Warnf("user tried to %s application in project %s, but the application is in project %s", action, project, effectiveProject) + // The user has access to the app, but the app is in a different project. Return 404, meaning "app doesn't + // exist in that project". + return nil, status.Errorf(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error()) + } + return a, nil +} + +// getApplicationEnforceRBACInformer uses an informer to get an Application. If the app does not exist, permission is +// denied, or any other error occurs when getting the app, we return a permission denied error to obscure any sensitive +// information. +func (s *Server) getApplicationEnforceRBACInformer(ctx context.Context, action, project, namespace, name string) (*appv1.Application, error) { + namespaceOrDefault := s.appNamespaceOrDefault(namespace) + return s.getAppEnforceRBAC(ctx, action, project, namespaceOrDefault, name, func() (*appv1.Application, error) { + return s.appLister.Applications(namespaceOrDefault).Get(name) + }) +} + +// getApplicationEnforceRBACClient uses a client to get an Application. If the app does not exist, permission is denied, +// or any other error occurs when getting the app, we return a permission denied error to obscure any sensitive +// information. +func (s *Server) getApplicationEnforceRBACClient(ctx context.Context, action, project, namespace, name, resourceVersion string) (*appv1.Application, error) { + namespaceOrDefault := s.appNamespaceOrDefault(namespace) + return s.getAppEnforceRBAC(ctx, action, project, namespaceOrDefault, name, func() (*appv1.Application, error) { + if !s.isNamespaceEnabled(namespaceOrDefault) { + return nil, security.NamespaceNotPermittedError(namespaceOrDefault) + } + return s.appclientset.ArgoprojV1alpha1().Applications(namespaceOrDefault).Get(ctx, name, metav1.GetOptions{ + ResourceVersion: resourceVersion, + }) + }) +} + // List returns list of applications func (s *Server) List(ctx context.Context, q *application.ApplicationQuery) (*appv1.ApplicationList, error) { selector, err := labels.Parse(q.GetSelector()) @@ -146,11 +265,24 @@ func (s *Server) List(ctx context.Context, q *application.ApplicationQuery) (*ap if err != nil { return nil, fmt.Errorf("error listing apps with selectors: %w", err) } + + filteredApps := apps + // Filter applications by name + if q.Name != nil { + filteredApps = argoutil.FilterByNameP(filteredApps, *q.Name) + } + + // Filter applications by projects + filteredApps = argoutil.FilterByProjectsP(filteredApps, getProjectsFromApplicationQuery(*q)) + + // Filter applications by source repo URL + filteredApps = argoutil.FilterByRepoP(filteredApps, q.GetRepo()) + newItems := make([]appv1.Application, 0) - for _, a := range apps { - // Skip any application that is neither in the conrol plane's namespace + for _, a := range filteredApps { + // Skip any application that is neither in the control plane's namespace // nor in the list of enabled namespaces. - if a.Namespace != s.ns && !glob.MatchStringInList(s.enabledNamespaces, a.Namespace, false) { + if !s.isNamespaceEnabled(a.Namespace) { continue } if s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)) { @@ -158,19 +290,6 @@ func (s *Server) List(ctx context.Context, q *application.ApplicationQuery) (*ap } } - if q.Name != nil { - newItems, err = argoutil.FilterByName(newItems, *q.Name) - if err != nil { - return nil, fmt.Errorf("error filtering applications by name: %w", err) - } - } - - // Filter applications by name - newItems = argoutil.FilterByProjects(newItems, q.Projects) - - // Filter applications by source repo URL - newItems = argoutil.FilterByRepo(newItems, q.GetRepo()) - // Sort found applications by name sort.Slice(newItems, func(i, j int) bool { return newItems[i].Name < newItems[j].Name @@ -214,15 +333,17 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq return nil, security.NamespaceNotPermittedError(appNs) } + // Don't let the app creator set the operation explicitly. Those requests should always go through the Sync API. + if a.Operation != nil { + log.WithFields(log.Fields{ + "application": a.Name, + argocommon.SecurityField: argocommon.SecurityLow, + }).Warn("User attempted to set operation on application creation. This could have allowed them to bypass branch protection rules by setting manifests directly. Ignoring the set operation.") + a.Operation = nil + } + created, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Create(ctx, a, metav1.CreateOptions{}) if err == nil { - if a.Spec.GetSource().Plugin != nil && a.Spec.GetSource().Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": a.Spec.GetSource().Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } - s.logAppEvent(created, ctx, argo.EventReasonResourceCreated, "created application") s.waitSync(created) return created, nil @@ -325,23 +446,12 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan if q.Name == nil || *q.Name == "" { return nil, fmt.Errorf("invalid request: application name is missing") } - appName := q.GetName() - appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) - a, err := s.appLister.Applications(appNs).Get(appName) + a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName()) if err != nil { - return nil, fmt.Errorf("error getting application: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, err } source := a.Spec.GetSource() - if source.Plugin != nil && source.Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": source.Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } if !s.isNamespaceEnabled(a.Namespace) { return nil, security.NamespaceNotPermittedError(a.Namespace) @@ -359,10 +469,6 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan return fmt.Errorf("error getting app instance label key from settings: %w", err) } - plugins, err := s.plugins() - if err != nil { - return fmt.Errorf("error getting plugins: %w", err) - } config, err := s.getApplicationClusterConfig(ctx, a) if err != nil { return fmt.Errorf("error getting application cluster config: %w", err) @@ -378,6 +484,11 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan return fmt.Errorf("error getting API resources: %w", err) } + proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) + if err != nil { + return fmt.Errorf("error getting app project: %w", err) + } + manifestInfo, err = client.GenerateManifest(ctx, &apiclient.ManifestRequest{ Repo: repo, Revision: revision, @@ -386,7 +497,6 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan Namespace: a.Spec.Destination.Namespace, ApplicationSource: &source, Repos: helmRepos, - Plugins: plugins, KustomizeOptions: kustomizeOptions, KubeVersion: serverVersion, ApiVersions: argo.APIResourcesToStrings(apiResources, true), @@ -394,6 +504,8 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan HelmOptions: helmOptions, TrackingMethod: string(argoutil.GetTrackingMethod(s.settingsMgr)), EnabledSourceTypes: enableGenerateManifests, + ProjectName: proj.Name, + ProjectSourceRepos: proj.Spec.SourceRepos, }) if err != nil { return fmt.Errorf("error generating manifests: %w", err) @@ -439,14 +551,8 @@ func (s *Server) GetManifestsWithFiles(stream application.ApplicationService_Get return fmt.Errorf("invalid request: application name is missing") } - appName := query.GetName() - appNs := s.appNamespaceOrDefault(query.GetAppNamespace()) - a, err := s.appLister.Applications(appNs).Get(appName) - + a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, query.GetProject(), query.GetAppNamespace(), query.GetName()) if err != nil { - return fmt.Errorf("error getting application: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return err } @@ -459,10 +565,6 @@ func (s *Server) GetManifestsWithFiles(stream application.ApplicationService_Get return fmt.Errorf("error getting app instance label key from settings: %w", err) } - plugins, err := s.plugins() - if err != nil { - return fmt.Errorf("error getting plugins: %w", err) - } config, err := s.getApplicationClusterConfig(ctx, a) if err != nil { return fmt.Errorf("error getting application cluster config: %w", err) @@ -479,6 +581,12 @@ func (s *Server) GetManifestsWithFiles(stream application.ApplicationService_Get } source := a.Spec.GetSource() + + proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) + if err != nil { + return fmt.Errorf("error getting app project: %w", err) + } + req := &apiclient.ManifestRequest{ Repo: repo, Revision: source.TargetRevision, @@ -487,7 +595,6 @@ func (s *Server) GetManifestsWithFiles(stream application.ApplicationService_Get Namespace: a.Spec.Destination.Namespace, ApplicationSource: &source, Repos: helmRepos, - Plugins: plugins, KustomizeOptions: kustomizeOptions, KubeVersion: serverVersion, ApiVersions: argo.APIResourcesToStrings(apiResources, true), @@ -495,6 +602,8 @@ func (s *Server) GetManifestsWithFiles(stream application.ApplicationService_Get HelmOptions: helmOptions, TrackingMethod: string(argoutil.GetTrackingMethod(s.settingsMgr)), EnabledSourceTypes: enableGenerateManifests, + ProjectName: proj.Name, + ProjectSourceRepos: proj.Spec.SourceRepos, } repoStreamClient, err := client.GenerateManifestWithFiles(stream.Context()) @@ -548,27 +657,22 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app appName := q.GetName() appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + project := "" + projects := getProjectsFromApplicationQuery(*q) + if len(projects) == 1 { + project = projects[0] + } else if len(projects) > 1 { + return nil, status.Errorf(codes.InvalidArgument, "multiple projects specified - the get endpoint accepts either zero or one project") + } + // We must use a client Get instead of an informer Get, because it's common to call Get immediately // following a Watch (which is not yet powered by an informer), and the Get must reflect what was // previously seen by the client. - a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{ - ResourceVersion: q.GetResourceVersion(), - }) - + a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, project, appNs, appName, q.GetResourceVersion()) if err != nil { - return nil, fmt.Errorf("error getting application: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, err } - if a.Spec.GetSource().Plugin != nil && a.Spec.GetSource().Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": a.Spec.GetSource().Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } - s.inferResourcesStatusHealth(a) if q.Refresh == nil { @@ -647,23 +751,11 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app // ListResourceEvents returns a list of event resources func (s *Server) ListResourceEvents(ctx context.Context, q *application.ApplicationResourceEventsQuery) (*v1.EventList, error) { - appName := q.GetName() - appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) - a, err := s.appLister.Applications(appNs).Get(appName) + a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName()) if err != nil { - return nil, fmt.Errorf("error getting application: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, err } - if a.Spec.GetSource().Plugin != nil && a.Spec.GetSource().Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": a.Spec.GetSource().Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } - var ( kubeClientset kubernetes.Interface fieldSelector string @@ -721,13 +813,15 @@ func (s *Server) ListResourceEvents(ctx context.Context, q *application.Applicat return list, nil } -func (s *Server) validateAndUpdateApp(ctx context.Context, newApp *appv1.Application, merge bool, validate bool) (*appv1.Application, error) { +// validateAndUpdateApp validates and updates the application. currentProject is the name of the project the app +// currently is under. If not specified, we assume that the app is under the project specified in the app spec. +func (s *Server) validateAndUpdateApp(ctx context.Context, newApp *appv1.Application, merge bool, validate bool, action string, currentProject string) (*appv1.Application, error) { s.projectLock.RLock(newApp.Spec.GetProject()) defer s.projectLock.RUnlock(newApp.Spec.GetProject()) - app, err := s.appclientset.ArgoprojV1alpha1().Applications(newApp.Namespace).Get(ctx, newApp.Name, metav1.GetOptions{}) + app, err := s.getApplicationEnforceRBACClient(ctx, action, currentProject, newApp.Namespace, newApp.Name, "") if err != nil { - return nil, fmt.Errorf("error getting application: %w", err) + return nil, err } err = s.validateAndNormalizeApp(ctx, newApp, validate) @@ -742,19 +836,6 @@ func (s *Server) validateAndUpdateApp(ctx context.Context, newApp *appv1.Applica return a, nil } -func mergeStringMaps(items ...map[string]string) map[string]string { - res := make(map[string]string) - for _, m := range items { - if m == nil { - continue - } - for k, v := range m { - res[k] = v - } - } - return res -} - var informerSyncTimeout = 2 * time.Second // waitSync is a helper to wait until the application informer cache is synced after create/update. @@ -792,8 +873,8 @@ func (s *Server) updateApp(app *appv1.Application, newApp *appv1.Application, ct for i := 0; i < 10; i++ { app.Spec = newApp.Spec if merge { - app.Labels = mergeStringMaps(app.Labels, newApp.Labels) - app.Annotations = mergeStringMaps(app.Annotations, newApp.Annotations) + app.Labels = collections.MergeStringMaps(app.Labels, newApp.Labels) + app.Annotations = collections.MergeStringMaps(app.Annotations, newApp.Annotations) } else { app.Labels = newApp.Labels app.Annotations = newApp.Annotations @@ -823,25 +904,18 @@ func (s *Server) updateApp(app *appv1.Application, newApp *appv1.Application, ct // Update updates an application func (s *Server) Update(ctx context.Context, q *application.ApplicationUpdateRequest) (*appv1.Application, error) { if q.GetApplication() == nil { - return nil, fmt.Errorf("error creating application: application is nil in request") + return nil, fmt.Errorf("error updating application: application is nil in request") } a := q.GetApplication() if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, a.RBACName(s.ns)); err != nil { return nil, err } - if a.Spec.GetSource().Plugin != nil && a.Spec.GetSource().Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": a.Spec.GetSource().Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } - validate := true if q.Validate != nil { validate = *q.Validate } - return s.validateAndUpdateApp(ctx, q.Application, false, validate) + return s.validateAndUpdateApp(ctx, q.Application, false, validate, rbacpolicy.ActionUpdate, q.GetProject()) } // UpdateSpec updates an application spec and filters out any invalid parameter overrides @@ -849,29 +923,17 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat if q.GetSpec() == nil { return nil, fmt.Errorf("error updating application spec: spec is nil in request") } - appName := q.GetName() - appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) - a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{}) + a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionUpdate, q.GetProject(), q.GetAppNamespace(), q.GetName(), "") if err != nil { - return nil, fmt.Errorf("error getting application: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, a.RBACName(s.ns)); err != nil { return nil, err } - if a.Spec.GetSource().Plugin != nil && a.Spec.GetSource().Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": a.Spec.GetSource().Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } - a.Spec = *q.GetSpec() validate := true if q.Validate != nil { validate = *q.Validate } - a, err = s.validateAndUpdateApp(ctx, a, false, validate) + a, err = s.validateAndUpdateApp(ctx, a, false, validate, rbacpolicy.ActionUpdate, q.GetProject()) if err != nil { return nil, fmt.Errorf("error validating and updating app: %w", err) } @@ -880,24 +942,15 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat // Patch patches an application func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchRequest) (*appv1.Application, error) { - appName := q.GetName() - appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) - app, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{}) + app, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName(), "") if err != nil { - return nil, fmt.Errorf("error getting application: %w", err) + return nil, err } if err = s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, app.RBACName(s.ns)); err != nil { return nil, err } - if app.Spec.GetSource().Plugin != nil && app.Spec.GetSource().Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": app.Name, - "plugin": app.Spec.GetSource().Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } - jsonApp, err := json.Marshal(app) if err != nil { return nil, fmt.Errorf("error marshaling application: %w", err) @@ -929,16 +982,16 @@ func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchReque if err != nil { return nil, fmt.Errorf("error unmarshaling patched app: %w", err) } - return s.validateAndUpdateApp(ctx, newApp, false, true) + return s.validateAndUpdateApp(ctx, newApp, false, true, rbacpolicy.ActionUpdate, q.GetProject()) } // Delete removes an application and all associated resources func (s *Server) Delete(ctx context.Context, q *application.ApplicationDeleteRequest) (*application.ApplicationResponse, error) { appName := q.GetName() appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) - a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{}) + a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetProject(), appNs, appName, "") if err != nil { - return nil, fmt.Errorf("error getting application: %w", err) + return nil, err } s.projectLock.RLock(a.Spec.Project) @@ -948,13 +1001,6 @@ func (s *Server) Delete(ctx context.Context, q *application.ApplicationDeleteReq return nil, err } - if a.Spec.GetSource().Plugin != nil && a.Spec.GetSource().Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": a.Spec.GetSource().Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } - if q.Cascade != nil && !*q.Cascade && q.GetPropagationPolicy() != "" { return nil, status.Error(codes.InvalidArgument, "cannot set propagation policy when cascading is disabled") } @@ -1003,6 +1049,31 @@ func (s *Server) Delete(ctx context.Context, q *application.ApplicationDeleteReq return &application.ApplicationResponse{}, nil } +func (s *Server) isApplicationPermitted(selector labels.Selector, minVersion int, claims any, appName, appNs string, projects map[string]bool, a appv1.Application) bool { + if len(projects) > 0 && !projects[a.Spec.GetProject()] { + return false + } + + if appVersion, err := strconv.Atoi(a.ResourceVersion); err == nil && appVersion < minVersion { + return false + } + matchedEvent := (appName == "" || (a.Name == appName && a.Namespace == appNs)) && selector.Matches(labels.Set(a.Labels)) + if !matchedEvent { + return false + } + + if !s.isNamespaceEnabled(a.Namespace) { + return false + } + + if !s.enf.Enforce(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)) { + // do not emit apps user does not have accessing + return false + } + + return true +} + func (s *Server) Watch(q *application.ApplicationQuery, ws application.ApplicationService_WatchServer) error { appName := q.GetName() appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) @@ -1011,8 +1082,8 @@ func (s *Server) Watch(q *application.ApplicationQuery, ws application.Applicati logCtx = logCtx.WithField("application", *q.Name) } projects := map[string]bool{} - for i := range q.Projects { - projects[q.Projects[i]] = true + for _, project := range getProjectsFromApplicationQuery(*q) { + projects[project] = true } claims := ws.Context().Value("claims") selector, err := labels.Parse(q.GetSelector()) @@ -1029,20 +1100,8 @@ func (s *Server) Watch(q *application.ApplicationQuery, ws application.Applicati // sendIfPermitted is a helper to send the application to the client's streaming channel if the // caller has RBAC privileges permissions to view it sendIfPermitted := func(a appv1.Application, eventType watch.EventType) { - if len(projects) > 0 && !projects[a.Spec.GetProject()] { - return - } - - if appVersion, err := strconv.Atoi(a.ResourceVersion); err == nil && appVersion < minVersion { - return - } - matchedEvent := (appName == "" || (a.Name == appName && a.Namespace == appNs)) && selector.Matches(labels.Set(a.Labels)) - if !matchedEvent { - return - } - - if !s.enf.Enforce(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)) { - // do not emit apps user does not have accessing + permitted := s.isApplicationPermitted(selector, minVersion, claims, appName, appNs, projects, a) + if !permitted { return } s.inferResourcesStatusHealth(&a) @@ -1089,7 +1148,9 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica proj, err := argo.GetAppProject(app, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) if err != nil { if apierr.IsNotFound(err) { - return status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", app.Spec.Project) + // Offer no hint that the project does not exist. + log.Warnf("User attempted to create/update application in non-existent project %q", app.Spec.Project) + return permissionDeniedErr } return fmt.Errorf("error getting application's project: %w", err) } @@ -1117,19 +1178,16 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica return err } } - plugins, err := s.plugins() - if err != nil { - return fmt.Errorf("error getting plugins: %w", err) - } if err := argo.ValidateDestination(ctx, &app.Spec.Destination, s.db); err != nil { return status.Errorf(codes.InvalidArgument, "application destination spec for %s is invalid: %s", app.Name, err.Error()) } var conditions []appv1.ApplicationCondition + if validate { conditions := make([]appv1.ApplicationCondition, 0) - condition, err := argo.ValidateRepo(ctx, app, s.repoClientset, s.db, plugins, s.kubectl, proj, s.settingsMgr) + condition, err := argo.ValidateRepo(ctx, app, s.repoClientset, s.db, s.kubectl, proj, s.settingsMgr) if err != nil { return fmt.Errorf("error validating the repo: %w", err) } @@ -1175,9 +1233,9 @@ func (s *Server) getCachedAppState(ctx context.Context, a *appv1.Application, ge return errors.New(argoutil.FormatAppConditions(conditions)) } _, err = s.Get(ctx, &application.ApplicationQuery{ - Name: pointer.StringPtr(a.GetName()), - AppNamespace: pointer.StringPtr(a.GetNamespace()), - Refresh: pointer.StringPtr(string(appv1.RefreshTypeNormal)), + Name: pointer.String(a.GetName()), + AppNamespace: pointer.String(a.GetNamespace()), + Refresh: pointer.String(string(appv1.RefreshTypeNormal)), }) if err != nil { return fmt.Errorf("error getting application by query: %w", err) @@ -1193,22 +1251,16 @@ func (s *Server) getAppResources(ctx context.Context, a *appv1.Application) (*ap return s.cache.GetAppResourcesTree(a.InstanceName(s.ns), &tree) }) if err != nil { - return &tree, fmt.Errorf("error getting cached app state: %w", err) + return &tree, fmt.Errorf("error getting cached app resource tree: %w", err) } return &tree, nil } func (s *Server) getAppLiveResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) { - appName := q.GetName() - appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) - a, err := s.appLister.Applications(appNs).Get(appName) + a, err := s.getApplicationEnforceRBACInformer(ctx, action, q.GetProject(), q.GetAppNamespace(), q.GetName()) if err != nil { - return nil, nil, nil, fmt.Errorf("error getting app by name: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, action, a.RBACName(s.ns)); err != nil { return nil, nil, nil, err } - tree, err := s.getAppResources(ctx, a) if err != nil { return nil, nil, nil, fmt.Errorf("error getting app resources: %w", err) @@ -1228,7 +1280,7 @@ func (s *Server) getAppLiveResource(ctx context.Context, action string, q *appli func (s *Server) GetResource(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ApplicationResourceResponse, error) { res, config, _, err := s.getAppLiveResource(ctx, rbacpolicy.ActionGet, q) if err != nil { - return nil, fmt.Errorf("error getting app live resource: %w", err) + return nil, err } // make sure to use specified resource version if provided @@ -1272,12 +1324,10 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe Kind: q.Kind, Version: q.Version, Group: q.Group, + Project: q.Project, } res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionUpdate, resourceRequest) if err != nil { - return nil, fmt.Errorf("error getting app live resource: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, a.RBACName(s.ns)); err != nil { return nil, err } @@ -1289,6 +1339,9 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe } return nil, fmt.Errorf("error patching resource: %w", err) } + if manifest == nil { + return nil, fmt.Errorf("failed to patch resource: manifest was nil") + } manifest, err = replaceSecretValues(manifest) if err != nil { return nil, fmt.Errorf("error replacing secret values: %w", err) @@ -1314,12 +1367,10 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR Kind: q.Kind, Version: q.Version, Group: q.Group, + Project: q.Project, } res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionDelete, resourceRequest) if err != nil { - return nil, fmt.Errorf("error getting live resource for delete: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionDelete, a.RBACName(s.ns)); err != nil { return nil, err } var deleteOption metav1.DeleteOptions @@ -1343,42 +1394,24 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR } func (s *Server) ResourceTree(ctx context.Context, q *application.ResourcesQuery) (*appv1.ApplicationTree, error) { - appName := q.GetApplicationName() - appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) - a, err := s.appLister.Applications(appNs).Get(appName) + a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetApplicationName()) if err != nil { - return nil, fmt.Errorf("error getting application by name: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, err } - source := a.Spec.GetSource() - if source.Plugin != nil && source.Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": source.Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } - return s.getAppResources(ctx, a) } func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application.ApplicationService_WatchResourceTreeServer) error { - appName := q.GetApplicationName() - appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) - a, err := s.appLister.Applications(appNs).Get(appName) + _, err := s.getApplicationEnforceRBACInformer(ws.Context(), rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetApplicationName()) if err != nil { - return fmt.Errorf("error getting application by name: %w", err) - } - - if err := s.enf.EnforceErr(ws.Context().Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return err } - return s.cache.OnAppResourcesTreeChanged(ws.Context(), q.GetApplicationName(), func() error { + cacheKey := argo.AppInstanceName(q.GetApplicationName(), q.GetAppNamespace(), s.ns) + return s.cache.OnAppResourcesTreeChanged(ws.Context(), cacheKey, func() error { var tree appv1.ApplicationTree - err := s.cache.GetAppResourcesTree(q.GetApplicationName(), &tree) + err := s.cache.GetAppResourcesTree(cacheKey, &tree) if err != nil { return fmt.Errorf("error getting app resource tree: %w", err) } @@ -1387,24 +1420,12 @@ func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application } func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMetadataQuery) (*appv1.RevisionMetadata, error) { - appName := q.GetName() - appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) - a, err := s.appLister.Applications(appNs).Get(appName) + a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName()) if err != nil { - return nil, fmt.Errorf("error getting app by name: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, err } source := a.Spec.GetSource() - if source.Plugin != nil && source.Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": source.Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } - repo, err := s.db.GetRepository(ctx, source.RepoURL) if err != nil { return nil, fmt.Errorf("error getting repository by URL: %w", err) @@ -1427,6 +1448,31 @@ func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMe }) } +// RevisionChartDetails returns the helm chart metadata, as fetched from the reposerver +func (s *Server) RevisionChartDetails(ctx context.Context, q *application.RevisionMetadataQuery) (*appv1.ChartDetails, error) { + a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName()) + if err != nil { + return nil, err + } + if a.Spec.Source.Chart == "" { + return nil, fmt.Errorf("no chart found for application: %v", a.QualifiedName()) + } + repo, err := s.db.GetRepository(ctx, a.Spec.Source.RepoURL) + if err != nil { + return nil, fmt.Errorf("error getting repository by URL: %w", err) + } + conn, repoClient, err := s.repoClientset.NewRepoServerClient() + if err != nil { + return nil, fmt.Errorf("error creating repo server client: %w", err) + } + defer ioutil.Close(conn) + return repoClient.GetRevisionChartDetails(ctx, &apiclient.RepoServerRevisionChartDetailsRequest{ + Repo: repo, + Name: a.Spec.Source.Chart, + Revision: q.GetRevision(), + }) +} + func isMatchingResource(q *application.ResourcesQuery, key kube.ResourceKey) bool { return (q.GetName() == "" || q.GetName() == key.Name) && (q.GetNamespace() == "" || q.GetNamespace() == key.Namespace) && @@ -1435,22 +1481,9 @@ func isMatchingResource(q *application.ResourcesQuery, key kube.ResourceKey) boo } func (s *Server) ManagedResources(ctx context.Context, q *application.ResourcesQuery) (*application.ManagedResourcesResponse, error) { - appName := q.GetApplicationName() - appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) - a, err := s.appLister.Applications(appNs).Get(appName) + a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetApplicationName()) if err != nil { - return nil, fmt.Errorf("error getting application: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { - return nil, fmt.Errorf("error verifying rbac: %w", err) - } - - source := a.Spec.GetSource() - if source.Plugin != nil && source.Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": source.Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) + return nil, err } items := make([]*appv1.ResourceDiff, 0) @@ -1458,12 +1491,12 @@ func (s *Server) ManagedResources(ctx context.Context, q *application.ResourcesQ return s.cache.GetAppManagedResources(a.InstanceName(s.ns), &items) }) if err != nil { - return nil, fmt.Errorf("error getting cached app state: %w", err) + return nil, fmt.Errorf("error getting cached app managed resources: %w", err) } res := &application.ManagedResourcesResponse{} for i := range items { item := items[i] - if isMatchingResource(q, kube.ResourceKey{Name: item.Name, Namespace: item.Namespace, Kind: item.Kind, Group: item.Group}) { + if !item.Hook && isMatchingResource(q, kube.ResourceKey{Name: item.Name, Namespace: item.Namespace, Kind: item.Kind, Group: item.Group}) { res.Items = append(res.Items, item) } } @@ -1505,25 +1538,11 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application. } } - appName := q.GetName() - appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) - a, err := s.appLister.Applications(appNs).Get(appName) + a, err := s.getApplicationEnforceRBACInformer(ws.Context(), rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName()) if err != nil { - return fmt.Errorf("error getting application by name: %w", err) - } - - if err := s.enf.EnforceErr(ws.Context().Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return err } - source := a.Spec.GetSource() - if source.Plugin != nil && source.Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": source.Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } - // Logs RBAC will be enforced only if an internal var serverRBACLogEnforceEnable (representing server.rbac.log.enforce.enable env var) // is defined and has a "true" value // Otherwise, no RBAC enforcement for logs will take place (meaning, PodLogs will return the logs, @@ -1690,8 +1709,8 @@ func isTheSelectedOne(currentNode *appv1.ResourceNode, q *application.Applicatio } for _, parentResource := range currentNode.ParentRefs { - //look up parentResource from resourceNodes - //then check if the parent isTheSelectedOne + // look up parentResource from resourceNodes + // then check if the parent isTheSelectedOne for _, resourceNode := range resourceNodes { if resourceNode.Namespace == parentResource.Namespace && resourceNode.Name == parentResource.Name && @@ -1711,12 +1730,9 @@ func isTheSelectedOne(currentNode *appv1.ResourceNode, q *application.Applicatio // Sync syncs an application to its target state func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncRequest) (*appv1.Application, error) { - appName := syncReq.GetName() - appNs := s.appNamespaceOrDefault(syncReq.GetAppNamespace()) - appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs) - a, err := appIf.Get(ctx, appName, metav1.GetOptions{}) + a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, syncReq.GetProject(), syncReq.GetAppNamespace(), syncReq.GetName(), "") if err != nil { - return nil, fmt.Errorf("error getting application by name: %w", err) + return nil, err } proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) @@ -1738,12 +1754,6 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR } source := a.Spec.GetSource() - if source.Plugin != nil && source.Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": source.Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } if syncReq.Manifests != nil { if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionOverride, a.RBACName(s.ns)); err != nil { @@ -1756,7 +1766,7 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR if a.DeletionTimestamp != nil { return nil, status.Errorf(codes.FailedPrecondition, "application is deleting") } - if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.Automated != nil { + if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.Automated != nil && !syncReq.GetDryRun() { if syncReq.GetRevision() != "" && syncReq.GetRevision() != text.FirstNonEmpty(source.TargetRevision, "HEAD") { return nil, status.Errorf(codes.FailedPrecondition, "Cannot sync to %s: auto-sync currently set to %s", syncReq.GetRevision(), source.TargetRevision) } @@ -1809,6 +1819,9 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR op.Retry = *retry } + appName := syncReq.GetName() + appNs := s.appNamespaceOrDefault(syncReq.GetAppNamespace()) + appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs) a, err = argo.SetAppOperation(appIf, appName, &op) if err != nil { return nil, fmt.Errorf("error setting app operation: %w", err) @@ -1826,25 +1839,11 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR } func (s *Server) Rollback(ctx context.Context, rollbackReq *application.ApplicationRollbackRequest) (*appv1.Application, error) { - appName := rollbackReq.GetName() - appNs := s.appNamespaceOrDefault(rollbackReq.GetAppNamespace()) - appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs) - a, err := appIf.Get(ctx, appName, metav1.GetOptions{}) + a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, rollbackReq.GetProject(), rollbackReq.GetAppNamespace(), rollbackReq.GetName(), "") if err != nil { - return nil, fmt.Errorf("error getting application by name: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionSync, a.RBACName(s.ns)); err != nil { return nil, err } - source := a.Spec.GetSource() - if source.Plugin != nil && source.Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": source.Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } - s.inferResourcesStatusHealth(a) if a.DeletionTimestamp != nil { @@ -1887,6 +1886,9 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat }, InitiatedBy: appv1.OperationInitiator{Username: session.Username(ctx)}, } + appName := rollbackReq.GetName() + appNs := s.appNamespaceOrDefault(rollbackReq.GetAppNamespace()) + appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs) a, err = argo.SetAppOperation(appIf, appName, &op) if err != nil { return nil, fmt.Errorf("error setting app operation: %w", err) @@ -1896,24 +1898,9 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat } func (s *Server) ListLinks(ctx context.Context, req *application.ListAppLinksRequest) (*application.LinksResponse, error) { - appName := req.GetName() - appNs := s.appNamespaceOrDefault(req.GetNamespace()) - - a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{}) + a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, req.GetProject(), req.GetNamespace(), req.GetName(), "") if err != nil { - log.WithFields(map[string]interface{}{ - "application": appName, - "ns": appNs, - }).Errorf("failed to get application, error=%v", err.Error()) - return nil, fmt.Errorf("error getting application") - } - - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { - log.WithFields(map[string]interface{}{ - "application": appName, - "ns": appNs, - }).Warnf("unauthorized access to app, error=%v", err.Error()) - return nil, fmt.Errorf("error getting application") + return nil, err } obj, err := kube.ToUnstructured(a) @@ -1926,7 +1913,14 @@ func (s *Server) ListLinks(ctx context.Context, req *application.ListAppLinksReq return nil, fmt.Errorf("failed to read application deep links from configmap: %w", err) } - finalList, errorList := deeplinks.EvaluateDeepLinksResponse(*obj, deepLinks) + clstObj, _, err := s.getObjectsForDeepLinks(ctx, a) + if err != nil { + return nil, err + } + + deepLinksObject := deeplinks.CreateDeepLinksObject(nil, obj, clstObj, nil) + + finalList, errorList := deeplinks.EvaluateDeepLinksResponse(deepLinksObject, obj.GetName(), deepLinks) if len(errorList) > 0 { log.Errorf("errorList while evaluating application deep links, %v", strings.Join(errorList, ", ")) } @@ -1934,12 +1928,59 @@ func (s *Server) ListLinks(ctx context.Context, req *application.ListAppLinksReq return finalList, nil } +func (s *Server) getObjectsForDeepLinks(ctx context.Context, app *appv1.Application) (cluster *unstructured.Unstructured, project *unstructured.Unstructured, err error) { + proj, err := argo.GetAppProject(app, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) + if err != nil { + return nil, nil, fmt.Errorf("error getting app project: %w", err) + } + + // sanitize project jwt tokens + proj.Status = appv1.AppProjectStatus{} + + project, err = kube.ToUnstructured(proj) + if err != nil { + return nil, nil, err + } + + getProjectClusters := func(project string) ([]*appv1.Cluster, error) { + return s.db.GetProjectClusters(ctx, project) + } + + if err := argo.ValidateDestination(ctx, &app.Spec.Destination, s.db); err != nil { + log.WithFields(map[string]interface{}{ + "application": app.GetName(), + "ns": app.GetNamespace(), + "destination": app.Spec.Destination, + }).Warnf("cannot validate cluster, error=%v", err.Error()) + return nil, nil, nil + } + + permitted, err := proj.IsDestinationPermitted(app.Spec.Destination, getProjectClusters) + if err != nil { + return nil, nil, err + } + if !permitted { + return nil, nil, fmt.Errorf("error getting destination cluster") + } + clst, err := s.db.GetCluster(ctx, app.Spec.Destination.Server) + if err != nil { + log.WithFields(map[string]interface{}{ + "application": app.GetName(), + "ns": app.GetNamespace(), + "destination": app.Spec.Destination, + }).Warnf("cannot get cluster from db, error=%v", err.Error()) + return nil, nil, nil + } + // sanitize cluster, remove cluster config creds and other unwanted fields + cluster, err = deeplinks.SanitizeCluster(clst) + return cluster, project, err +} + func (s *Server) ListResourceLinks(ctx context.Context, req *application.ApplicationResourceRequest) (*application.LinksResponse, error) { - obj, _, _, _, err := s.getUnstructuredLiveResourceOrApp(ctx, rbacpolicy.ActionGet, req) + obj, _, app, _, err := s.getUnstructuredLiveResourceOrApp(ctx, rbacpolicy.ActionGet, req) if err != nil { return nil, err } - deepLinks, err := s.settingsMgr.GetDeepLinks(settings.ResourceDeepLinks) if err != nil { return nil, fmt.Errorf("failed to read application deep links from configmap: %w", err) @@ -1950,7 +1991,18 @@ func (s *Server) ListResourceLinks(ctx context.Context, req *application.Applica return nil, fmt.Errorf("error replacing secret values: %w", err) } - finalList, errorList := deeplinks.EvaluateDeepLinksResponse(*obj, deepLinks) + appObj, err := kube.ToUnstructured(app) + if err != nil { + return nil, err + } + + clstObj, projObj, err := s.getObjectsForDeepLinks(ctx, app) + if err != nil { + return nil, err + } + + deepLinksObject := deeplinks.CreateDeepLinksObject(obj, appObj, clstObj, projObj) + finalList, errorList := deeplinks.EvaluateDeepLinksResponse(deepLinksObject, obj.GetName(), deepLinks) if len(errorList) > 0 { log.Errorf("errors while evaluating resource deep links, %v", strings.Join(errorList, ", ")) } @@ -2000,11 +2052,8 @@ func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, sy func (s *Server) TerminateOperation(ctx context.Context, termOpReq *application.OperationTerminateRequest) (*application.OperationTerminateResponse, error) { appName := termOpReq.GetName() appNs := s.appNamespaceOrDefault(termOpReq.GetAppNamespace()) - a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{}) + a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, termOpReq.GetProject(), appNs, appName, "") if err != nil { - return nil, fmt.Errorf("error getting application by name: %w", err) - } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionSync, a.RBACName(s.ns)); err != nil { return nil, err } @@ -2039,7 +2088,7 @@ func (s *Server) logAppEvent(a *appv1.Application, ctx context.Context, reason s user = "Unknown user" } message := fmt.Sprintf("%s %s", user, action) - s.auditLogger.LogAppEvent(a, eventInfo, message) + s.auditLogger.LogAppEvent(a, eventInfo, message, user) } func (s *Server) logResourceEvent(res *appv1.ResourceNode, ctx context.Context, reason string, action string) { @@ -2049,7 +2098,7 @@ func (s *Server) logResourceEvent(res *appv1.ResourceNode, ctx context.Context, user = "Unknown user" } message := fmt.Sprintf("%s %s", user, action) - s.auditLogger.LogResourceEvent(res, eventInfo, message) + s.auditLogger.LogResourceEvent(res, eventInfo, message, user) } func (s *Server) ListResourceActions(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ResourceActionsListResponse, error) { @@ -2075,11 +2124,10 @@ func (s *Server) ListResourceActions(ctx context.Context, q *application.Applica } func (s *Server) getUnstructuredLiveResourceOrApp(ctx context.Context, rbacRequest string, q *application.ApplicationResourceRequest) (obj *unstructured.Unstructured, res *appv1.ResourceNode, app *appv1.Application, config *rest.Config, err error) { - if q.GetKind() == "Application" && q.GetGroup() == "argoproj.io" && q.GetName() == q.GetResourceName() { - namespace := s.appNamespaceOrDefault(q.GetAppNamespace()) - app, err = s.appLister.Applications(namespace).Get(q.GetName()) + if q.GetKind() == applicationType.ApplicationKind && q.GetGroup() == applicationType.Group && q.GetName() == q.GetResourceName() { + app, err = s.getApplicationEnforceRBACInformer(ctx, rbacRequest, q.GetProject(), q.GetAppNamespace(), q.GetName()) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("error getting app by name: %w", err) + return nil, nil, nil, nil, err } if err = s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacRequest, app.RBACName(s.ns)); err != nil { return nil, nil, nil, nil, err @@ -2092,9 +2140,10 @@ func (s *Server) getUnstructuredLiveResourceOrApp(ctx context.Context, rbacReque } else { res, config, app, err = s.getAppLiveResource(ctx, rbacRequest, q) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("error getting app live resource: %w", err) + return nil, nil, nil, nil, err } obj, err = s.kubectl.GetResource(ctx, config, res.GroupKindVersion(), res.Name, res.Namespace) + } if err != nil { return nil, nil, nil, nil, fmt.Errorf("error getting resource: %w", err) @@ -2131,6 +2180,7 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA Kind: q.Kind, Version: q.Version, Group: q.Group, + Project: q.Project, } actionRequest := fmt.Sprintf("%s/%s/%s/%s", rbacpolicy.ActionAction, q.GetGroup(), q.GetKind(), q.GetAction()) liveObj, res, a, config, err := s.getUnstructuredLiveResourceOrApp(ctx, actionRequest, resourceRequest) @@ -2138,6 +2188,11 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA return nil, err } + liveObjBytes, err := json.Marshal(liveObj) + if err != nil { + return nil, fmt.Errorf("error marshaling live object: %w", err) + } + resourceOverrides, err := s.settingsMgr.GetResourceOverrides() if err != nil { return nil, fmt.Errorf("error getting resource overrides: %w", err) @@ -2151,21 +2206,80 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA return nil, fmt.Errorf("error getting Lua resource action: %w", err) } - newObj, err := luaVM.ExecuteResourceAction(liveObj, action.ActionLua) + newObjects, err := luaVM.ExecuteResourceAction(liveObj, action.ActionLua) if err != nil { return nil, fmt.Errorf("error executing Lua resource action: %w", err) } - newObjBytes, err := json.Marshal(newObj) - if err != nil { - return nil, fmt.Errorf("error marshaling new object: %w", err) + var app *appv1.Application + // Only bother getting the app if we know we're going to need it for a resource permission check. + if len(newObjects) > 0 { + // No need for an RBAC check, we checked above that the user is allowed to run this action. + app, err = s.appLister.Applications(s.appNamespaceOrDefault(q.GetAppNamespace())).Get(q.GetName()) + if err != nil { + return nil, err + } } - liveObjBytes, err := json.Marshal(liveObj) - if err != nil { - return nil, fmt.Errorf("error marshaling live object: %w", err) + // First, make sure all the returned resources are permitted, for each operation. + // Also perform create with dry-runs for all create-operation resources. + // This is performed separately to reduce the risk of only some of the resources being successfully created later. + // TODO: when apply/delete operations would be supported for custom actions, + // the dry-run for relevant apply/delete operation would have to be invoked as well. + for _, impactedResource := range newObjects { + newObj := impactedResource.UnstructuredObj + err := s.verifyResourcePermitted(ctx, app, newObj) + if err != nil { + return nil, err + } + switch impactedResource.K8SOperation { + case lua.CreateOperation: + createOptions := metav1.CreateOptions{DryRun: []string{"All"}} + _, err := s.kubectl.CreateResource(ctx, config, newObj.GroupVersionKind(), newObj.GetName(), newObj.GetNamespace(), newObj, createOptions) + if err != nil { + return nil, err + } + } + } + + // Now, perform the actual operations. + // The creation itself is not transactional. + // TODO: maybe create a k8s list representation of the resources, + // and invoke create on this list resource to make it semi-transactional (there is still patch operation that is separate, + // thus can fail separately from create). + for _, impactedResource := range newObjects { + newObj := impactedResource.UnstructuredObj + newObjBytes, err := json.Marshal(newObj) + + if err != nil { + return nil, fmt.Errorf("error marshaling new object: %w", err) + } + + switch impactedResource.K8SOperation { + // No default case since a not supported operation would have failed upon unmarshaling earlier + case lua.PatchOperation: + _, err := s.patchResource(ctx, config, liveObjBytes, newObjBytes, newObj) + if err != nil { + return nil, err + } + case lua.CreateOperation: + _, err := s.createResource(ctx, config, newObj) + if err != nil { + return nil, err + } + } + } + + if res == nil { + s.logAppEvent(a, ctx, argo.EventReasonResourceActionRan, fmt.Sprintf("ran action %s", q.GetAction())) + } else { + s.logAppEvent(a, ctx, argo.EventReasonResourceActionRan, fmt.Sprintf("ran action %s on resource %s/%s/%s", q.GetAction(), res.Group, res.Kind, res.Name)) + s.logResourceEvent(res, ctx, argo.EventReasonResourceActionRan, fmt.Sprintf("ran action %s", q.GetAction())) } + return &application.ApplicationResponse{}, nil +} +func (s *Server) patchResource(ctx context.Context, config *rest.Config, liveObjBytes, newObjBytes []byte, newObj *unstructured.Unstructured) (*application.ApplicationResponse, error) { diffBytes, err := jsonpatch.CreateMergePatch(liveObjBytes, newObjBytes) if err != nil { return nil, fmt.Errorf("error calculating merge patch: %w", err) @@ -2205,12 +2319,38 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA return nil, fmt.Errorf("error patching resource: %w", err) } } + return &application.ApplicationResponse{}, nil +} - if res == nil { - s.logAppEvent(a, ctx, argo.EventReasonResourceActionRan, fmt.Sprintf("ran action %s", q.GetAction())) - } else { - s.logAppEvent(a, ctx, argo.EventReasonResourceActionRan, fmt.Sprintf("ran action %s on resource %s/%s/%s", q.GetAction(), res.Group, res.Kind, res.Name)) - s.logResourceEvent(res, ctx, argo.EventReasonResourceActionRan, fmt.Sprintf("ran action %s", q.GetAction())) +func (s *Server) verifyResourcePermitted(ctx context.Context, app *appv1.Application, obj *unstructured.Unstructured) error { + proj, err := argo.GetAppProject(app, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) + if err != nil { + if apierr.IsNotFound(err) { + return fmt.Errorf("application references project %s which does not exist", app.Spec.Project) + } + return fmt.Errorf("failed to get project %s: %w", app.Spec.Project, err) + } + permitted, err := proj.IsResourcePermitted(schema.GroupKind{Group: obj.GroupVersionKind().Group, Kind: obj.GroupVersionKind().Kind}, obj.GetNamespace(), app.Spec.Destination, func(project string) ([]*appv1.Cluster, error) { + clusters, err := s.db.GetProjectClusters(context.TODO(), project) + if err != nil { + return nil, fmt.Errorf("failed to get project clusters: %w", err) + } + return clusters, nil + }) + if err != nil { + return fmt.Errorf("error checking resource permissions: %w", err) + } + if !permitted { + return fmt.Errorf("application %s is not permitted to manage %s/%s/%s in %s", app.RBACName(s.ns), obj.GroupVersionKind().Group, obj.GroupVersionKind().Kind, obj.GetName(), obj.GetNamespace()) + } + + return nil +} + +func (s *Server) createResource(ctx context.Context, config *rest.Config, newObj *unstructured.Unstructured) (*application.ApplicationResponse, error) { + _, err := s.kubectl.CreateResource(ctx, config, newObj.GroupVersionKind(), newObj.GetName(), newObj.GetNamespace(), newObj, metav1.CreateOptions{}) + if err != nil { + return nil, fmt.Errorf("error creating resource: %w", err) } return &application.ApplicationResponse{}, nil } @@ -2248,40 +2388,12 @@ func splitStatusPatch(patch []byte) ([]byte, []byte, error) { return nonStatusPatch, statusPatch, nil } -func (s *Server) plugins() ([]*appv1.ConfigManagementPlugin, error) { - plugins, err := s.settingsMgr.GetConfigManagementPlugins() - if err != nil { - return nil, fmt.Errorf("error getting config management plugin: %w", err) - } - tools := make([]*appv1.ConfigManagementPlugin, len(plugins)) - for i, p := range plugins { - p := p - tools[i] = &p - } - return tools, nil -} - func (s *Server) GetApplicationSyncWindows(ctx context.Context, q *application.ApplicationSyncWindowsQuery) (*application.ApplicationSyncWindowsResponse, error) { - appName := q.GetName() - appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) - appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs) - a, err := appIf.Get(ctx, appName, metav1.GetOptions{}) + a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName(), "") if err != nil { - return nil, fmt.Errorf("error getting application by name: %w", err) - } - - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, err } - source := a.Spec.GetSource() - if source.Plugin != nil && source.Plugin.Name != "" { - log.WithFields(map[string]interface{}{ - "application": a.Name, - "plugin": source.Plugin.Name, - }).Warnf(argocommon.ConfigMapPluginDeprecationWarning) - } - proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) if err != nil { return nil, fmt.Errorf("error getting app project: %w", err) @@ -2358,3 +2470,12 @@ func (s *Server) appNamespaceOrDefault(appNs string) string { func (s *Server) isNamespaceEnabled(namespace string) bool { return security.IsNamespaceEnabled(namespace, s.ns, s.enabledNamespaces) } + +// getProjectFromApplicationQuery gets the project names from a query. If the legacy "project" field was specified, use +// that. Otherwise, use the newer "projects" field. +func getProjectsFromApplicationQuery(q application.ApplicationQuery) []string { + if q.Project != nil { + return q.Project + } + return q.Projects +} diff --git a/server/application/application.proto b/server/application/application.proto index 4fb08a7082c0f..4736219cb4594 100644 --- a/server/application/application.proto +++ b/server/application/application.proto @@ -13,11 +13,15 @@ import "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1/generated.p import "github.com/argoproj/argo-cd/v2/reposerver/repository/repository.proto"; -// ApplicationQuery is a query for application resources +// ApplicationQuery is a query for application resources. When getting multiple applications, the "projects" field acts +// as a filter. When getting a single application, you may specify either zero or one project. If you specify zero +// projects, the application will be returned regardless of which project it belongs to (assuming you have access). If +// you specify one project, the application will only be returned if it exists and belongs to the specified project. +// Otherwise you will receive a 404. message ApplicationQuery { // the application's name optional string name = 1; - // forces application reconciliation if set to true + // forces application reconciliation if set to 'hard' optional string refresh = 2; // the project names to restrict returned list applications repeated string projects = 3; @@ -29,6 +33,8 @@ message ApplicationQuery { optional string repo = 6; // the application's namespace optional string appNamespace = 7; + // the project names to restrict returned list applications (legacy name for backwards-compatibility) + repeated string project = 8; } message NodeQuery { @@ -44,6 +50,7 @@ message RevisionMetadataQuery{ required string revision = 2; // the application's namespace optional string appNamespace = 3; + optional string project = 4; } // ApplicationEventsQuery is a query for application resource events @@ -53,6 +60,7 @@ message ApplicationResourceEventsQuery { optional string resourceName = 3; optional string resourceUID = 4; optional string appNamespace = 5; + optional string project = 6; } // ManifestQuery is a query for manifest resources @@ -60,6 +68,7 @@ message ApplicationManifestQuery { required string name = 1; optional string revision = 2; optional string appNamespace = 3; + optional string project = 4; } message FileChunk { @@ -70,6 +79,7 @@ message ApplicationManifestQueryWithFiles { required string name = 1; required string checksum = 2; optional string appNamespace = 3; + optional string project = 4; } message ApplicationManifestQueryWithFilesWrapper { @@ -90,6 +100,7 @@ message ApplicationCreateRequest { message ApplicationUpdateRequest { required github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Application application = 1; optional bool validate = 2; + optional string project = 3; } message ApplicationDeleteRequest { @@ -97,6 +108,7 @@ message ApplicationDeleteRequest { optional bool cascade = 2; optional string propagationPolicy = 3; optional string appNamespace = 4; + optional string project = 5; } message SyncOptions { @@ -116,6 +128,7 @@ message ApplicationSyncRequest { optional github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.RetryStrategy retryStrategy = 10; optional SyncOptions syncOptions = 11; optional string appNamespace = 12; + optional string project = 13; } // ApplicationUpdateSpecRequest is a request to update application spec @@ -124,6 +137,7 @@ message ApplicationUpdateSpecRequest { required github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSpec spec = 2; optional bool validate = 3; optional string appNamespace = 4; + optional string project = 5; } // ApplicationPatchRequest is a request to patch an application @@ -132,6 +146,7 @@ message ApplicationPatchRequest { required string patch = 2; required string patchType = 3; optional string appNamespace = 5; + optional string project = 6; } message ApplicationRollbackRequest { @@ -140,6 +155,7 @@ message ApplicationRollbackRequest { optional bool dryRun = 3; optional bool prune = 4; optional string appNamespace = 6; + optional string project = 7; } message ApplicationResourceRequest { @@ -150,6 +166,7 @@ message ApplicationResourceRequest { optional string group = 5; required string kind = 6; optional string appNamespace = 7; + optional string project = 8; } message ApplicationResourcePatchRequest { @@ -162,6 +179,7 @@ message ApplicationResourcePatchRequest { required string patch = 7; required string patchType = 8; optional string appNamespace = 9; + optional string project = 10; } message ApplicationResourceDeleteRequest { @@ -174,6 +192,7 @@ message ApplicationResourceDeleteRequest { optional bool force = 7; optional bool orphan = 8; optional string appNamespace = 9; + optional string project = 10; } message ResourceActionRunRequest { @@ -185,6 +204,7 @@ message ResourceActionRunRequest { required string kind = 6; required string action = 7; optional string appNamespace = 8; + optional string project = 9; } message ResourceActionsListResponse { @@ -211,6 +231,7 @@ message ApplicationPodLogsQuery { optional string resourceName = 13 ; optional bool previous = 14; optional string appNamespace = 15; + optional string project = 16; } message LogEntry { @@ -225,11 +246,13 @@ message LogEntry { message OperationTerminateRequest { required string name = 1; optional string appNamespace = 2; + optional string project = 3; } message ApplicationSyncWindowsQuery { required string name = 1; optional string appNamespace = 2; + optional string project = 3; } message ApplicationSyncWindowsResponse { @@ -258,6 +281,7 @@ message ResourcesQuery { optional string group = 5; optional string kind = 6; optional string appNamespace = 7; + optional string project = 8; } message ManagedResourcesResponse { @@ -278,6 +302,7 @@ message LinksResponse { message ListAppLinksRequest { required string name = 1; optional string namespace = 3; + optional string project = 4; } @@ -322,6 +347,11 @@ service ApplicationService { option (google.api.http).get = "/api/v1/applications/{name}/revisions/{revision}/metadata"; } + // Get the chart metadata (description, maintainers, home) for a specific revision of the application + rpc RevisionChartDetails (RevisionMetadataQuery) returns (github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ChartDetails) { + option (google.api.http).get = "/api/v1/applications/{name}/revisions/{revision}/chartdetails"; + } + // GetManifests returns application manifests rpc GetManifests (ApplicationManifestQuery) returns (repository.ManifestResponse) { option (google.api.http).get = "/api/v1/applications/{name}/manifests"; diff --git a/server/application/application_test.go b/server/application/application_test.go index 7569734d33b42..51c912ff05109 100644 --- a/server/application/application_test.go +++ b/server/application/application_test.go @@ -4,30 +4,41 @@ import ( "context" coreerrors "errors" "fmt" + "io" + "strconv" "sync/atomic" "testing" "time" + "k8s.io/apimachinery/pkg/labels" + "github.com/argoproj/gitops-engine/pkg/health" synccommon "github.com/argoproj/gitops-engine/pkg/sync/common" + "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest" "github.com/argoproj/pkg/sync" - "github.com/ghodss/yaml" "github.com/golang-jwt/jwt/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" + k8sappsv1 "k8s.io/api/apps/v1" + k8sbatchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/rest" kubetesting "k8s.io/client-go/testing" k8scache "k8s.io/client-go/tools/cache" "k8s.io/utils/pointer" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" @@ -36,6 +47,7 @@ import ( appinformer "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks" + appmocks "github.com/argoproj/argo-cd/v2/server/application/mocks" servercache "github.com/argoproj/argo-cd/v2/server/cache" "github.com/argoproj/argo-cd/v2/server/rbacpolicy" "github.com/argoproj/argo-cd/v2/test" @@ -64,7 +76,7 @@ func fakeRepo() *appsv1.Repository { func fakeCluster() *appsv1.Cluster { return &appsv1.Cluster{ - Server: "https://cluster-api.com", + Server: "https://cluster-api.example.com", Name: "fake-cluster", Config: appsv1.ClusterConfig{}, } @@ -78,14 +90,14 @@ func fakeAppList() *apiclient.AppList { } } -func fakeResolveRevesionResponse() *apiclient.ResolveRevisionResponse { +func fakeResolveRevisionResponse() *apiclient.ResolveRevisionResponse { return &apiclient.ResolveRevisionResponse{ Revision: "f9ba9e98119bf8c1176fbd65dbae26a71d044add", AmbiguousRevision: "HEAD (f9ba9e98119bf8c1176fbd65dbae26a71d044add)", } } -func fakeResolveRevesionResponseHelm() *apiclient.ResolveRevisionResponse { +func fakeResolveRevisionResponseHelm() *apiclient.ResolveRevisionResponse { return &apiclient.ResolveRevisionResponse{ Revision: "0.7.*", AmbiguousRevision: "0.7.* (0.7.2)", @@ -98,26 +110,32 @@ func fakeRepoServerClient(isHelm bool) *mocks.RepoServerServiceClient { mockRepoServiceClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(&apiclient.ManifestResponse{}, nil) mockRepoServiceClient.On("GetAppDetails", mock.Anything, mock.Anything).Return(&apiclient.RepoAppDetailsResponse{}, nil) mockRepoServiceClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) + mockRepoServiceClient.On("GetRevisionMetadata", mock.Anything, mock.Anything).Return(&appsv1.RevisionMetadata{}, nil) + mockWithFilesClient := &mocks.RepoServerService_GenerateManifestWithFilesClient{} + mockWithFilesClient.On("Send", mock.Anything).Return(nil) + mockWithFilesClient.On("CloseAndRecv").Return(&apiclient.ManifestResponse{}, nil) + mockRepoServiceClient.On("GenerateManifestWithFiles", mock.Anything, mock.Anything).Return(mockWithFilesClient, nil) + mockRepoServiceClient.On("GetRevisionChartDetails", mock.Anything, mock.Anything).Return(&appsv1.ChartDetails{}, nil) if isHelm { - mockRepoServiceClient.On("ResolveRevision", mock.Anything, mock.Anything).Return(fakeResolveRevesionResponseHelm(), nil) + mockRepoServiceClient.On("ResolveRevision", mock.Anything, mock.Anything).Return(fakeResolveRevisionResponseHelm(), nil) } else { - mockRepoServiceClient.On("ResolveRevision", mock.Anything, mock.Anything).Return(fakeResolveRevesionResponse(), nil) + mockRepoServiceClient.On("ResolveRevision", mock.Anything, mock.Anything).Return(fakeResolveRevisionResponse(), nil) } return &mockRepoServiceClient } // return an ApplicationServiceServer which returns fake data -func newTestAppServer(objects ...runtime.Object) *Server { +func newTestAppServer(t *testing.T, objects ...runtime.Object) *Server { f := func(enf *rbac.Enforcer) { _ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV) enf.SetDefaultRole("role:admin") } - return newTestAppServerWithEnforcerConfigure(f, objects...) + return newTestAppServerWithEnforcerConfigure(f, t, objects...) } -func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), objects ...runtime.Object) *Server { +func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), t *testing.T, objects ...runtime.Object) *Server { kubeclientset := fake.NewSimpleClientset(&v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: testNamespace, @@ -152,6 +170,7 @@ func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), objects ...ru Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}}, }, } + myProj := &appsv1.AppProject{ ObjectMeta: metav1.ObjectMeta{Name: "my-proj", Namespace: "default"}, Spec: appsv1.AppProjectSpec{ @@ -190,7 +209,187 @@ func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), objects ...ru // populate the app informer with the fake objects appInformer := factory.Argoproj().V1alpha1().Applications().Informer() // TODO(jessesuen): probably should return cancel function so tests can stop background informer - //ctx, cancel := context.WithCancel(context.Background()) + // ctx, cancel := context.WithCancel(context.Background()) + go appInformer.Run(ctx.Done()) + if !k8scache.WaitForCacheSync(ctx.Done(), appInformer.HasSynced) { + panic("Timed out waiting for caches to sync") + } + + projInformer := factory.Argoproj().V1alpha1().AppProjects().Informer() + go projInformer.Run(ctx.Done()) + if !k8scache.WaitForCacheSync(ctx.Done(), projInformer.HasSynced) { + panic("Timed out waiting for caches to sync") + } + + broadcaster := new(appmocks.Broadcaster) + broadcaster.On("Subscribe", mock.Anything, mock.Anything).Return(func() {}).Run(func(args mock.Arguments) { + // Simulate the broadcaster notifying the subscriber of an application update. + // The second parameter to Subscribe is filters. For the purposes of tests, we ignore the filters. Future tests + // might require implementing those. + go func() { + events := args.Get(0).(chan *appsv1.ApplicationWatchEvent) + for _, obj := range objects { + app, ok := obj.(*appsv1.Application) + if ok { + oldVersion, err := strconv.Atoi(app.ResourceVersion) + if err != nil { + oldVersion = 0 + } + clonedApp := app.DeepCopy() + clonedApp.ResourceVersion = fmt.Sprintf("%d", oldVersion+1) + events <- &appsv1.ApplicationWatchEvent{Type: watch.Added, Application: *clonedApp} + } + } + }() + }) + broadcaster.On("OnAdd", mock.Anything).Return() + broadcaster.On("OnUpdate", mock.Anything, mock.Anything).Return() + broadcaster.On("OnDelete", mock.Anything).Return() + + appStateCache := appstate.NewCache(cache.NewCache(cache.NewInMemoryCache(time.Hour)), time.Hour) + // pre-populate the app cache + for _, obj := range objects { + app, ok := obj.(*appsv1.Application) + if ok { + err := appStateCache.SetAppManagedResources(app.Name, []*appsv1.ResourceDiff{}) + require.NoError(t, err) + + // Pre-populate the resource tree based on the app's resources. + nodes := make([]appsv1.ResourceNode, len(app.Status.Resources)) + for i, res := range app.Status.Resources { + nodes[i] = appsv1.ResourceNode{ + ResourceRef: appsv1.ResourceRef{ + Group: res.Group, + Kind: res.Kind, + Version: res.Version, + Name: res.Name, + Namespace: res.Namespace, + UID: "fake", + }, + } + } + err = appStateCache.SetAppResourcesTree(app.Name, &appsv1.ApplicationTree{ + Nodes: nodes, + }) + require.NoError(t, err) + } + } + appCache := servercache.NewCache(appStateCache, time.Hour, time.Hour, time.Hour) + + kubectl := &kubetest.MockKubectlCmd{} + kubectl = kubectl.WithGetResourceFunc(func(_ context.Context, _ *rest.Config, gvk schema.GroupVersionKind, name string, namespace string) (*unstructured.Unstructured, error) { + for _, obj := range objects { + if obj.GetObjectKind().GroupVersionKind().GroupKind() == gvk.GroupKind() { + if obj, ok := obj.(*unstructured.Unstructured); ok && obj.GetName() == name && obj.GetNamespace() == namespace { + return obj, nil + } + } + } + return nil, nil + }) + + server, _ := NewServer( + testNamespace, + kubeclientset, + fakeAppsClientset, + factory.Argoproj().V1alpha1().Applications().Lister(), + appInformer, + broadcaster, + mockRepoClient, + appCache, + kubectl, + db, + enforcer, + sync.NewKeyLock(), + settingsMgr, + projInformer, + []string{}, + ) + return server.(*Server) +} + +// return an ApplicationServiceServer which returns fake data +func newTestAppServerWithBenchmark(b *testing.B, objects ...runtime.Object) *Server { + f := func(enf *rbac.Enforcer) { + _ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV) + enf.SetDefaultRole("role:admin") + } + return newTestAppServerWithEnforcerConfigureWithBenchmark(f, b, objects...) +} + +func newTestAppServerWithEnforcerConfigureWithBenchmark(f func(*rbac.Enforcer), b *testing.B, objects ...runtime.Object) *Server { + kubeclientset := fake.NewSimpleClientset(&v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + Name: "argocd-cm", + Labels: map[string]string{ + "app.kubernetes.io/part-of": "argocd", + }, + }, + }, &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-secret", + Namespace: testNamespace, + }, + Data: map[string][]byte{ + "admin.password": []byte("test"), + "server.secretkey": []byte("test"), + }, + }) + ctx := context.Background() + db := db.NewDB(testNamespace, settings.NewSettingsManager(ctx, kubeclientset, testNamespace), kubeclientset) + _, err := db.CreateRepository(ctx, fakeRepo()) + require.NoError(b, err) + _, err = db.CreateCluster(ctx, fakeCluster()) + require.NoError(b, err) + + mockRepoClient := &mocks.Clientset{RepoServerServiceClient: fakeRepoServerClient(false)} + + defaultProj := &appsv1.AppProject{ + ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "default"}, + Spec: appsv1.AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}}, + }, + } + myProj := &appsv1.AppProject{ + ObjectMeta: metav1.ObjectMeta{Name: "my-proj", Namespace: "default"}, + Spec: appsv1.AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}}, + }, + } + projWithSyncWindows := &appsv1.AppProject{ + ObjectMeta: metav1.ObjectMeta{Name: "proj-maint", Namespace: "default"}, + Spec: appsv1.AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}}, + SyncWindows: appsv1.SyncWindows{}, + }, + } + matchingWindow := &appsv1.SyncWindow{ + Kind: "allow", + Schedule: "* * * * *", + Duration: "1h", + Applications: []string{"test-app"}, + } + projWithSyncWindows.Spec.SyncWindows = append(projWithSyncWindows.Spec.SyncWindows, matchingWindow) + + objects = append(objects, defaultProj, myProj, projWithSyncWindows) + + fakeAppsClientset := apps.NewSimpleClientset(objects...) + factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, appinformer.WithNamespace(""), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {})) + fakeProjLister := factory.Argoproj().V1alpha1().AppProjects().Lister().AppProjects(testNamespace) + + enforcer := rbac.NewEnforcer(kubeclientset, testNamespace, common.ArgoCDRBACConfigMapName, nil) + f(enforcer) + enforcer.SetClaimsEnforcerFunc(rbacpolicy.NewRBACPolicyEnforcer(enforcer, fakeProjLister).EnforceClaims) + + settingsMgr := settings.NewSettingsManager(ctx, kubeclientset, testNamespace) + + // populate the app informer with the fake objects + appInformer := factory.Argoproj().V1alpha1().Applications().Informer() + go appInformer.Run(ctx.Done()) if !k8scache.WaitForCacheSync(ctx.Done(), appInformer.HasSynced) { panic("Timed out waiting for caches to sync") @@ -202,15 +401,83 @@ func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), objects ...ru panic("Timed out waiting for caches to sync") } + broadcaster := new(appmocks.Broadcaster) + broadcaster.On("Subscribe", mock.Anything, mock.Anything).Return(func() {}).Run(func(args mock.Arguments) { + // Simulate the broadcaster notifying the subscriber of an application update. + // The second parameter to Subscribe is filters. For the purposes of tests, we ignore the filters. Future tests + // might require implementing those. + go func() { + events := args.Get(0).(chan *appsv1.ApplicationWatchEvent) + for _, obj := range objects { + app, ok := obj.(*appsv1.Application) + if ok { + oldVersion, err := strconv.Atoi(app.ResourceVersion) + if err != nil { + oldVersion = 0 + } + clonedApp := app.DeepCopy() + clonedApp.ResourceVersion = fmt.Sprintf("%d", oldVersion+1) + events <- &appsv1.ApplicationWatchEvent{Type: watch.Added, Application: *clonedApp} + } + } + }() + }) + broadcaster.On("OnAdd", mock.Anything).Return() + broadcaster.On("OnUpdate", mock.Anything, mock.Anything).Return() + broadcaster.On("OnDelete", mock.Anything).Return() + + appStateCache := appstate.NewCache(cache.NewCache(cache.NewInMemoryCache(time.Hour)), time.Hour) + // pre-populate the app cache + for _, obj := range objects { + app, ok := obj.(*appsv1.Application) + if ok { + err := appStateCache.SetAppManagedResources(app.Name, []*appsv1.ResourceDiff{}) + require.NoError(b, err) + + // Pre-populate the resource tree based on the app's resources. + nodes := make([]appsv1.ResourceNode, len(app.Status.Resources)) + for i, res := range app.Status.Resources { + nodes[i] = appsv1.ResourceNode{ + ResourceRef: appsv1.ResourceRef{ + Group: res.Group, + Kind: res.Kind, + Version: res.Version, + Name: res.Name, + Namespace: res.Namespace, + UID: "fake", + }, + } + } + err = appStateCache.SetAppResourcesTree(app.Name, &appsv1.ApplicationTree{ + Nodes: nodes, + }) + require.NoError(b, err) + } + } + appCache := servercache.NewCache(appStateCache, time.Hour, time.Hour, time.Hour) + + kubectl := &kubetest.MockKubectlCmd{} + kubectl = kubectl.WithGetResourceFunc(func(_ context.Context, _ *rest.Config, gvk schema.GroupVersionKind, name string, namespace string) (*unstructured.Unstructured, error) { + for _, obj := range objects { + if obj.GetObjectKind().GroupVersionKind().GroupKind() == gvk.GroupKind() { + if obj, ok := obj.(*unstructured.Unstructured); ok && obj.GetName() == name && obj.GetNamespace() == namespace { + return obj, nil + } + } + } + return nil, nil + }) + server, _ := NewServer( testNamespace, kubeclientset, fakeAppsClientset, factory.Argoproj().V1alpha1().Applications().Lister(), appInformer, + broadcaster, mockRepoClient, - nil, - &kubetest.MockKubectlCmd{}, + appCache, + kubectl, db, enforcer, sync.NewKeyLock(), @@ -236,7 +503,7 @@ spec: environment: default destination: namespace: ` + test.FakeDestNamespace + ` - server: https://cluster-api.com + server: https://cluster-api.example.com ` const fakeAppWithDestName = ` @@ -274,7 +541,7 @@ spec: environment: default destination: namespace: ` + test.FakeDestNamespace + ` - server: https://cluster-api.com + server: https://cluster-api.example.com ` func newTestAppWithDestName(opts ...func(app *appsv1.Application)) *appsv1.Application { @@ -301,8 +568,512 @@ func createTestApp(testApp string, opts ...func(app *appsv1.Application)) *appsv return &app } +type TestServerStream struct { + ctx context.Context + appName string + headerSent bool + project string +} + +func (t *TestServerStream) SetHeader(metadata.MD) error { + return nil +} + +func (t *TestServerStream) SendHeader(metadata.MD) error { + return nil +} + +func (t *TestServerStream) SetTrailer(metadata.MD) {} + +func (t *TestServerStream) Context() context.Context { + return t.ctx +} + +func (t *TestServerStream) SendMsg(m interface{}) error { + return nil +} + +func (t *TestServerStream) RecvMsg(m interface{}) error { + return nil +} + +func (t *TestServerStream) SendAndClose(r *apiclient.ManifestResponse) error { + return nil +} + +func (t *TestServerStream) Recv() (*application.ApplicationManifestQueryWithFilesWrapper, error) { + if !t.headerSent { + t.headerSent = true + return &application.ApplicationManifestQueryWithFilesWrapper{Part: &application.ApplicationManifestQueryWithFilesWrapper_Query{ + Query: &application.ApplicationManifestQueryWithFiles{ + Name: pointer.String(t.appName), + Project: pointer.String(t.project), + Checksum: pointer.String(""), + }, + }}, nil + } + return nil, io.EOF +} + +func (t *TestServerStream) ServerStream() TestServerStream { + return TestServerStream{} +} + +type TestResourceTreeServer struct { + ctx context.Context +} + +func (t *TestResourceTreeServer) Send(tree *appsv1.ApplicationTree) error { + return nil +} + +func (t *TestResourceTreeServer) SetHeader(metadata.MD) error { + return nil +} + +func (t *TestResourceTreeServer) SendHeader(metadata.MD) error { + return nil +} + +func (t *TestResourceTreeServer) SetTrailer(metadata.MD) {} + +func (t *TestResourceTreeServer) Context() context.Context { + return t.ctx +} + +func (t *TestResourceTreeServer) SendMsg(m interface{}) error { + return nil +} + +func (t *TestResourceTreeServer) RecvMsg(m interface{}) error { + return nil +} + +type TestPodLogsServer struct { + ctx context.Context +} + +func (t *TestPodLogsServer) Send(log *application.LogEntry) error { + return nil +} + +func (t *TestPodLogsServer) SetHeader(metadata.MD) error { + return nil +} + +func (t *TestPodLogsServer) SendHeader(metadata.MD) error { + return nil +} + +func (t *TestPodLogsServer) SetTrailer(metadata.MD) {} + +func (t *TestPodLogsServer) Context() context.Context { + return t.ctx +} + +func (t *TestPodLogsServer) SendMsg(m interface{}) error { + return nil +} + +func (t *TestPodLogsServer) RecvMsg(m interface{}) error { + return nil +} + +func TestNoAppEnumeration(t *testing.T) { + // This test ensures that malicious users can't infer the existence or non-existence of Applications by inspecting + // error messages. The errors for "app does not exist" must be the same as errors for "you aren't allowed to + // interact with this app." + + // These tests are only important on API calls where the full app RBAC name (project, namespace, and name) is _not_ + // known based on the query parameters. For example, the Create call cannot leak existence of Applications, because + // the Application's project, namespace, and name are all specified in the API call. The call can be rejected + // immediately if the user does not have access. But the Delete endpoint may be called with just the Application + // name. So we cannot return a different error message for "does not exist" and "you don't have delete permissions," + // because the user could infer that the Application exists if they do not get the "does not exist" message. For + // endpoints that do not require the full RBAC name, we must return a generic "permission denied" for both "does not + // exist" and "no access." + + f := func(enf *rbac.Enforcer) { + _ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV) + enf.SetDefaultRole("role:none") + } + deployment := k8sappsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + } + testApp := newTestApp(func(app *appsv1.Application) { + app.Name = "test" + app.Status.Resources = []appsv1.ResourceStatus{ + { + Group: deployment.GroupVersionKind().Group, + Kind: deployment.GroupVersionKind().Kind, + Version: deployment.GroupVersionKind().Version, + Name: deployment.Name, + Namespace: deployment.Namespace, + Status: "Synced", + }, + } + app.Status.History = []appsv1.RevisionHistory{ + { + ID: 0, + Source: appsv1.ApplicationSource{ + TargetRevision: "something-old", + }, + }, + } + }) + testHelmApp := newTestApp(func(app *appsv1.Application) { + app.Name = "test-helm" + app.Spec.Source.Path = "" + app.Spec.Source.Chart = "test" + app.Status.Resources = []appsv1.ResourceStatus{ + { + Group: deployment.GroupVersionKind().Group, + Kind: deployment.GroupVersionKind().Kind, + Version: deployment.GroupVersionKind().Version, + Name: deployment.Name, + Namespace: deployment.Namespace, + Status: "Synced", + }, + } + app.Status.History = []appsv1.RevisionHistory{ + { + ID: 0, + Source: appsv1.ApplicationSource{ + TargetRevision: "something-old", + }, + }, + } + }) + testDeployment := kube.MustToUnstructured(&deployment) + appServer := newTestAppServerWithEnforcerConfigure(f, t, testApp, testHelmApp, testDeployment) + + noRoleCtx := context.Background() + // nolint:staticcheck + adminCtx := context.WithValue(noRoleCtx, "claims", &jwt.MapClaims{"groups": []string{"admin"}}) + + t.Run("Get", func(t *testing.T) { + // nolint:staticcheck + _, err := appServer.Get(adminCtx, &application.ApplicationQuery{Name: pointer.String("test")}) + assert.NoError(t, err) + // nolint:staticcheck + _, err = appServer.Get(noRoleCtx, &application.ApplicationQuery{Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + // nolint:staticcheck + _, err = appServer.Get(adminCtx, &application.ApplicationQuery{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + // nolint:staticcheck + _, err = appServer.Get(adminCtx, &application.ApplicationQuery{Name: pointer.String("doest-not-exist"), Project: []string{"test"}}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("GetManifests", func(t *testing.T) { + _, err := appServer.GetManifests(adminCtx, &application.ApplicationManifestQuery{Name: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.GetManifests(noRoleCtx, &application.ApplicationManifestQuery{Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.GetManifests(adminCtx, &application.ApplicationManifestQuery{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.GetManifests(adminCtx, &application.ApplicationManifestQuery{Name: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("ListResourceEvents", func(t *testing.T) { + _, err := appServer.ListResourceEvents(adminCtx, &application.ApplicationResourceEventsQuery{Name: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.ListResourceEvents(noRoleCtx, &application.ApplicationResourceEventsQuery{Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ListResourceEvents(adminCtx, &application.ApplicationResourceEventsQuery{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ListResourceEvents(adminCtx, &application.ApplicationResourceEventsQuery{Name: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("UpdateSpec", func(t *testing.T) { + _, err := appServer.UpdateSpec(adminCtx, &application.ApplicationUpdateSpecRequest{Name: pointer.String("test"), Spec: &appsv1.ApplicationSpec{ + Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.example.com"}, + Source: &appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."}, + }}) + assert.NoError(t, err) + _, err = appServer.UpdateSpec(noRoleCtx, &application.ApplicationUpdateSpecRequest{Name: pointer.String("test"), Spec: &appsv1.ApplicationSpec{ + Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.example.com"}, + Source: &appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."}, + }}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.UpdateSpec(adminCtx, &application.ApplicationUpdateSpecRequest{Name: pointer.String("doest-not-exist"), Spec: &appsv1.ApplicationSpec{ + Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.example.com"}, + Source: &appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."}, + }}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.UpdateSpec(adminCtx, &application.ApplicationUpdateSpecRequest{Name: pointer.String("doest-not-exist"), Project: pointer.String("test"), Spec: &appsv1.ApplicationSpec{ + Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.example.com"}, + Source: &appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."}, + }}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("Patch", func(t *testing.T) { + _, err := appServer.Patch(adminCtx, &application.ApplicationPatchRequest{Name: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/source/path", "value": "foo"}]`)}) + assert.NoError(t, err) + _, err = appServer.Patch(noRoleCtx, &application.ApplicationPatchRequest{Name: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/source/path", "value": "foo"}]`)}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.Patch(adminCtx, &application.ApplicationPatchRequest{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.Patch(adminCtx, &application.ApplicationPatchRequest{Name: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("GetResource", func(t *testing.T) { + _, err := appServer.GetResource(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.GetResource(noRoleCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.GetResource(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("doest-not-exist"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.GetResource(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("doest-not-exist"), Project: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("PatchResource", func(t *testing.T) { + _, err := appServer.PatchResource(adminCtx, &application.ApplicationResourcePatchRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)}) + // This will always throw an error, because the kubectl mock for PatchResource is hard-coded to return nil. + // The best we can do is to confirm we get past the permission check. + assert.NotEqual(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.PatchResource(noRoleCtx, &application.ApplicationResourcePatchRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.PatchResource(adminCtx, &application.ApplicationResourcePatchRequest{Name: pointer.String("doest-not-exist"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.PatchResource(adminCtx, &application.ApplicationResourcePatchRequest{Name: pointer.String("doest-not-exist"), Project: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("DeleteResource", func(t *testing.T) { + _, err := appServer.DeleteResource(adminCtx, &application.ApplicationResourceDeleteRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.DeleteResource(noRoleCtx, &application.ApplicationResourceDeleteRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.DeleteResource(adminCtx, &application.ApplicationResourceDeleteRequest{Name: pointer.String("doest-not-exist"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.DeleteResource(adminCtx, &application.ApplicationResourceDeleteRequest{Name: pointer.String("doest-not-exist"), Project: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("ResourceTree", func(t *testing.T) { + _, err := appServer.ResourceTree(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.ResourceTree(noRoleCtx, &application.ResourcesQuery{ApplicationName: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ResourceTree(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ResourceTree(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("RevisionMetadata", func(t *testing.T) { + _, err := appServer.RevisionMetadata(adminCtx, &application.RevisionMetadataQuery{Name: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.RevisionMetadata(noRoleCtx, &application.RevisionMetadataQuery{Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.RevisionMetadata(adminCtx, &application.RevisionMetadataQuery{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.RevisionMetadata(adminCtx, &application.RevisionMetadataQuery{Name: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("RevisionChartDetails", func(t *testing.T) { + _, err := appServer.RevisionChartDetails(adminCtx, &application.RevisionMetadataQuery{Name: pointer.String("test-helm")}) + assert.NoError(t, err) + _, err = appServer.RevisionChartDetails(noRoleCtx, &application.RevisionMetadataQuery{Name: pointer.String("test-helm")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.RevisionChartDetails(adminCtx, &application.RevisionMetadataQuery{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.RevisionChartDetails(adminCtx, &application.RevisionMetadataQuery{Name: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("ManagedResources", func(t *testing.T) { + _, err := appServer.ManagedResources(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.ManagedResources(noRoleCtx, &application.ResourcesQuery{ApplicationName: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ManagedResources(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ManagedResources(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("Sync", func(t *testing.T) { + _, err := appServer.Sync(adminCtx, &application.ApplicationSyncRequest{Name: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.Sync(noRoleCtx, &application.ApplicationSyncRequest{Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.Sync(adminCtx, &application.ApplicationSyncRequest{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.Sync(adminCtx, &application.ApplicationSyncRequest{Name: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("TerminateOperation", func(t *testing.T) { + // The sync operation is already started from the previous test. We just need to set the field that the + // controller would set if this were an actual Argo CD environment. + setSyncRunningOperationState(t, appServer) + _, err := appServer.TerminateOperation(adminCtx, &application.OperationTerminateRequest{Name: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.TerminateOperation(noRoleCtx, &application.OperationTerminateRequest{Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.TerminateOperation(adminCtx, &application.OperationTerminateRequest{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.TerminateOperation(adminCtx, &application.OperationTerminateRequest{Name: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("Rollback", func(t *testing.T) { + unsetSyncRunningOperationState(t, appServer) + _, err := appServer.Rollback(adminCtx, &application.ApplicationRollbackRequest{Name: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.Rollback(noRoleCtx, &application.ApplicationRollbackRequest{Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.Rollback(adminCtx, &application.ApplicationRollbackRequest{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.Rollback(adminCtx, &application.ApplicationRollbackRequest{Name: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("ListResourceActions", func(t *testing.T) { + _, err := appServer.ListResourceActions(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.ListResourceActions(noRoleCtx, &application.ApplicationResourceRequest{Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ListResourceActions(noRoleCtx, &application.ApplicationResourceRequest{Group: pointer.String("argoproj.io"), Kind: pointer.String("Application"), Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ListResourceActions(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ListResourceActions(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("RunResourceAction", func(t *testing.T) { + _, err := appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Action: pointer.String("restart")}) + assert.NoError(t, err) + _, err = appServer.RunResourceAction(noRoleCtx, &application.ResourceActionRunRequest{Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.RunResourceAction(noRoleCtx, &application.ResourceActionRunRequest{Group: pointer.String("argoproj.io"), Kind: pointer.String("Application"), Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("GetApplicationSyncWindows", func(t *testing.T) { + _, err := appServer.GetApplicationSyncWindows(adminCtx, &application.ApplicationSyncWindowsQuery{Name: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.GetApplicationSyncWindows(noRoleCtx, &application.ApplicationSyncWindowsQuery{Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.GetApplicationSyncWindows(adminCtx, &application.ApplicationSyncWindowsQuery{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.GetApplicationSyncWindows(adminCtx, &application.ApplicationSyncWindowsQuery{Name: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("GetManifestsWithFiles", func(t *testing.T) { + err := appServer.GetManifestsWithFiles(&TestServerStream{ctx: adminCtx, appName: "test"}) + assert.NoError(t, err) + err = appServer.GetManifestsWithFiles(&TestServerStream{ctx: noRoleCtx, appName: "test"}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + err = appServer.GetManifestsWithFiles(&TestServerStream{ctx: adminCtx, appName: "does-not-exist"}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + err = appServer.GetManifestsWithFiles(&TestServerStream{ctx: adminCtx, appName: "does-not-exist", project: "test"}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"does-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("WatchResourceTree", func(t *testing.T) { + err := appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: pointer.String("test")}, &TestResourceTreeServer{ctx: adminCtx}) + assert.NoError(t, err) + err = appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: pointer.String("test")}, &TestResourceTreeServer{ctx: noRoleCtx}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + err = appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: pointer.String("does-not-exist")}, &TestResourceTreeServer{ctx: adminCtx}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + err = appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: pointer.String("does-not-exist"), Project: pointer.String("test")}, &TestResourceTreeServer{ctx: adminCtx}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"does-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("PodLogs", func(t *testing.T) { + err := appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: pointer.String("test")}, &TestPodLogsServer{ctx: adminCtx}) + assert.NoError(t, err) + err = appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: pointer.String("test")}, &TestPodLogsServer{ctx: noRoleCtx}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + err = appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: pointer.String("does-not-exist")}, &TestPodLogsServer{ctx: adminCtx}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + err = appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: pointer.String("does-not-exist"), Project: pointer.String("test")}, &TestPodLogsServer{ctx: adminCtx}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"does-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("ListLinks", func(t *testing.T) { + _, err := appServer.ListLinks(adminCtx, &application.ListAppLinksRequest{Name: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.ListLinks(noRoleCtx, &application.ListAppLinksRequest{Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ListLinks(adminCtx, &application.ListAppLinksRequest{Name: pointer.String("does-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ListLinks(adminCtx, &application.ListAppLinksRequest{Name: pointer.String("does-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"does-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + t.Run("ListResourceLinks", func(t *testing.T) { + _, err := appServer.ListResourceLinks(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.ListResourceLinks(noRoleCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ListResourceLinks(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("does-not-exist"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.ListResourceLinks(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("does-not-exist"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"does-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) + + // Do this last so other stuff doesn't fail. + t.Run("Delete", func(t *testing.T) { + _, err := appServer.Delete(adminCtx, &application.ApplicationDeleteRequest{Name: pointer.String("test")}) + assert.NoError(t, err) + _, err = appServer.Delete(noRoleCtx, &application.ApplicationDeleteRequest{Name: pointer.String("test")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.Delete(adminCtx, &application.ApplicationDeleteRequest{Name: pointer.String("doest-not-exist")}) + assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + _, err = appServer.Delete(adminCtx, &application.ApplicationDeleteRequest{Name: pointer.String("doest-not-exist"), Project: pointer.String("test")}) + assert.Equal(t, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", err.Error(), "when the request specifies a project, we can return the standard k8s error message") + }) +} + +// setSyncRunningOperationState simulates starting a sync operation on the given app. +func setSyncRunningOperationState(t *testing.T, appServer *Server) { + appIf := appServer.appclientset.ArgoprojV1alpha1().Applications("default") + app, err := appIf.Get(context.Background(), "test", metav1.GetOptions{}) + require.NoError(t, err) + // This sets the status that would be set by the controller usually. + app.Status.OperationState = &appsv1.OperationState{Phase: synccommon.OperationRunning, Operation: appsv1.Operation{Sync: &appsv1.SyncOperation{}}} + _, err = appIf.Update(context.Background(), app, metav1.UpdateOptions{}) + require.NoError(t, err) +} + +// unsetSyncRunningOperationState simulates finishing a sync operation on the given app. +func unsetSyncRunningOperationState(t *testing.T, appServer *Server) { + appIf := appServer.appclientset.ArgoprojV1alpha1().Applications("default") + app, err := appIf.Get(context.Background(), "test", metav1.GetOptions{}) + require.NoError(t, err) + app.Operation = nil + app.Status.OperationState = nil + _, err = appIf.Update(context.Background(), app, metav1.UpdateOptions{}) + require.NoError(t, err) +} + func TestListAppsInNamespaceWithLabels(t *testing.T) { - appServer := newTestAppServer(newTestApp(func(app *appsv1.Application) { + appServer := newTestAppServer(t, newTestApp(func(app *appsv1.Application) { app.Name = "App1" app.ObjectMeta.Namespace = "test-namespace" app.SetLabels(map[string]string{"key1": "value1", "key2": "value1"}) @@ -323,7 +1094,7 @@ func TestListAppsInNamespaceWithLabels(t *testing.T) { } func TestListAppsInDefaultNSWithLabels(t *testing.T) { - appServer := newTestAppServer(newTestApp(func(app *appsv1.Application) { + appServer := newTestAppServer(t, newTestApp(func(app *appsv1.Application) { app.Name = "App1" app.SetLabels(map[string]string{"key1": "value1", "key2": "value1"}) }), newTestApp(func(app *appsv1.Application) { @@ -365,7 +1136,7 @@ func testListAppsWithLabels(t *testing.T, appQuery application.ApplicationQuery, label: "!key2", expectedResult: []string{"App2", "App3"}}, } - //test valid scenarios + // test valid scenarios for _, validTest := range validTests { t.Run(validTest.testName, func(t *testing.T) { appQuery.Selector = &validTest.label @@ -391,7 +1162,7 @@ func testListAppsWithLabels(t *testing.T, appQuery application.ApplicationQuery, label: "key1= minVersion { return @@ -344,5 +355,17 @@ func (s *Server) logAppSetEvent(a *v1alpha1.ApplicationSet, ctx context.Context, user = "Unknown user" } message := fmt.Sprintf("%s %s", user, action) - s.auditLogger.LogAppSetEvent(a, eventInfo, message) + s.auditLogger.LogAppSetEvent(a, eventInfo, message, user) +} + +func (s *Server) appsetNamespaceOrDefault(appNs string) string { + if appNs == "" { + return s.ns + } else { + return appNs + } +} + +func (s *Server) isNamespaceEnabled(namespace string) bool { + return security.IsNamespaceEnabled(namespace, s.ns, s.enabledNamespaces) } diff --git a/server/applicationset/applicationset.proto b/server/applicationset/applicationset.proto index 8f6d09cf2b75b..2a857d41a00ce 100644 --- a/server/applicationset/applicationset.proto +++ b/server/applicationset/applicationset.proto @@ -14,6 +14,8 @@ import "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1/generated.p message ApplicationSetGetQuery { // the applicationsets's name string name = 1; + // The application set namespace. Default empty is argocd control plane namespace + string appsetNamespace = 2; } message ApplicationSetListQuery { @@ -21,6 +23,8 @@ message ApplicationSetListQuery { repeated string projects = 1; // the selector to restrict returned list to applications only with matched labels string selector = 2; + // The application set namespace. Default empty is argocd control plane namespace + string appsetNamespace = 3; } @@ -38,6 +42,8 @@ message ApplicationSetCreateRequest { message ApplicationSetDeleteRequest { string name = 1; + // The application set namespace. Default empty is argocd control plane namespace + string appsetNamespace = 2; } diff --git a/server/applicationset/applicationset_test.go b/server/applicationset/applicationset_test.go new file mode 100644 index 0000000000000..c49ddb35a7970 --- /dev/null +++ b/server/applicationset/applicationset_test.go @@ -0,0 +1,476 @@ +package applicationset + +import ( + "context" + "testing" + + "github.com/argoproj/pkg/sync" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/fake" + k8scache "k8s.io/client-go/tools/cache" + + "github.com/argoproj/argo-cd/v2/common" + "github.com/argoproj/argo-cd/v2/pkg/apiclient/applicationset" + appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + apps "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake" + appinformer "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions" + "github.com/argoproj/argo-cd/v2/server/rbacpolicy" + "github.com/argoproj/argo-cd/v2/util/assets" + "github.com/argoproj/argo-cd/v2/util/db" + "github.com/argoproj/argo-cd/v2/util/errors" + "github.com/argoproj/argo-cd/v2/util/rbac" + "github.com/argoproj/argo-cd/v2/util/settings" +) + +const ( + testNamespace = "default" + fakeRepoURL = "https://git.com/repo.git" +) + +func fakeRepo() *appsv1.Repository { + return &appsv1.Repository{ + Repo: fakeRepoURL, + } +} + +func fakeCluster() *appsv1.Cluster { + return &appsv1.Cluster{ + Server: "https://cluster-api.example.com", + Name: "fake-cluster", + Config: appsv1.ClusterConfig{}, + } +} + +// return an ApplicationServiceServer which returns fake data +func newTestAppSetServer(objects ...runtime.Object) *Server { + f := func(enf *rbac.Enforcer) { + _ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV) + enf.SetDefaultRole("role:admin") + } + scopedNamespaces := "" + return newTestAppSetServerWithEnforcerConfigure(f, scopedNamespaces, objects...) +} + +// return an ApplicationServiceServer which returns fake data +func newTestNamespacedAppSetServer(objects ...runtime.Object) *Server { + f := func(enf *rbac.Enforcer) { + _ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV) + enf.SetDefaultRole("role:admin") + } + scopedNamespaces := "argocd" + return newTestAppSetServerWithEnforcerConfigure(f, scopedNamespaces, objects...) +} + +func newTestAppSetServerWithEnforcerConfigure(f func(*rbac.Enforcer), namespace string, objects ...runtime.Object) *Server { + kubeclientset := fake.NewSimpleClientset(&v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + Name: "argocd-cm", + Labels: map[string]string{ + "app.kubernetes.io/part-of": "argocd", + }, + }, + }, &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-secret", + Namespace: testNamespace, + }, + Data: map[string][]byte{ + "admin.password": []byte("test"), + "server.secretkey": []byte("test"), + }, + }) + ctx := context.Background() + db := db.NewDB(testNamespace, settings.NewSettingsManager(ctx, kubeclientset, testNamespace), kubeclientset) + _, err := db.CreateRepository(ctx, fakeRepo()) + errors.CheckError(err) + _, err = db.CreateCluster(ctx, fakeCluster()) + errors.CheckError(err) + + defaultProj := &appsv1.AppProject{ + ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "default"}, + Spec: appsv1.AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}}, + }, + } + myProj := &appsv1.AppProject{ + ObjectMeta: metav1.ObjectMeta{Name: "my-proj", Namespace: "default"}, + Spec: appsv1.AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}}, + }, + } + + objects = append(objects, defaultProj, myProj) + + fakeAppsClientset := apps.NewSimpleClientset(objects...) + factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, appinformer.WithNamespace(namespace), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {})) + fakeProjLister := factory.Argoproj().V1alpha1().AppProjects().Lister().AppProjects(testNamespace) + + enforcer := rbac.NewEnforcer(kubeclientset, testNamespace, common.ArgoCDRBACConfigMapName, nil) + f(enforcer) + enforcer.SetClaimsEnforcerFunc(rbacpolicy.NewRBACPolicyEnforcer(enforcer, fakeProjLister).EnforceClaims) + + settingsMgr := settings.NewSettingsManager(ctx, kubeclientset, testNamespace) + + // populate the app informer with the fake objects + appInformer := factory.Argoproj().V1alpha1().Applications().Informer() + // TODO(jessesuen): probably should return cancel function so tests can stop background informer + //ctx, cancel := context.WithCancel(context.Background()) + go appInformer.Run(ctx.Done()) + if !k8scache.WaitForCacheSync(ctx.Done(), appInformer.HasSynced) { + panic("Timed out waiting for caches to sync") + } + // populate the appset informer with the fake objects + appsetInformer := factory.Argoproj().V1alpha1().ApplicationSets().Informer() + go appsetInformer.Run(ctx.Done()) + if !k8scache.WaitForCacheSync(ctx.Done(), appsetInformer.HasSynced) { + panic("Timed out waiting for caches to sync") + } + + projInformer := factory.Argoproj().V1alpha1().AppProjects().Informer() + go projInformer.Run(ctx.Done()) + if !k8scache.WaitForCacheSync(ctx.Done(), projInformer.HasSynced) { + panic("Timed out waiting for caches to sync") + } + + server := NewServer( + db, + kubeclientset, + enforcer, + fakeAppsClientset, + appInformer, + factory.Argoproj().V1alpha1().ApplicationSets().Lister(), + fakeProjLister, + settingsMgr, + testNamespace, + sync.NewKeyLock(), + []string{testNamespace, "external-namespace"}, + ) + return server.(*Server) +} + +func newTestAppSet(opts ...func(appset *appsv1.ApplicationSet)) *appsv1.ApplicationSet { + appset := appsv1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + }, + Spec: appsv1.ApplicationSetSpec{ + Template: appsv1.ApplicationSetTemplate{ + Spec: appsv1.ApplicationSpec{ + Project: "default", + }, + }, + }, + } + for i := range opts { + opts[i](&appset) + } + return &appset +} + +func testListAppsetsWithLabels(t *testing.T, appsetQuery applicationset.ApplicationSetListQuery, appServer *Server) { + validTests := []struct { + testName string + label string + expectedResult []string + }{ + {testName: "Equality based filtering using '=' operator", + label: "key1=value1", + expectedResult: []string{"AppSet1"}}, + {testName: "Equality based filtering using '==' operator", + label: "key1==value1", + expectedResult: []string{"AppSet1"}}, + {testName: "Equality based filtering using '!=' operator", + label: "key1!=value1", + expectedResult: []string{"AppSet2", "AppSet3"}}, + {testName: "Set based filtering using 'in' operator", + label: "key1 in (value1, value3)", + expectedResult: []string{"AppSet1", "AppSet3"}}, + {testName: "Set based filtering using 'notin' operator", + label: "key1 notin (value1, value3)", + expectedResult: []string{"AppSet2"}}, + {testName: "Set based filtering using 'exists' operator", + label: "key1", + expectedResult: []string{"AppSet1", "AppSet2", "AppSet3"}}, + {testName: "Set based filtering using 'not exists' operator", + label: "!key2", + expectedResult: []string{"AppSet2", "AppSet3"}}, + } + //test valid scenarios + for _, validTest := range validTests { + t.Run(validTest.testName, func(t *testing.T) { + appsetQuery.Selector = validTest.label + res, err := appServer.List(context.Background(), &appsetQuery) + assert.NoError(t, err) + apps := []string{} + for i := range res.Items { + apps = append(apps, res.Items[i].Name) + } + assert.Equal(t, validTest.expectedResult, apps) + }) + } + + invalidTests := []struct { + testName string + label string + errorMesage string + }{ + {testName: "Set based filtering using '>' operator", + label: "key1>value1", + errorMesage: "error parsing the selector"}, + {testName: "Set based filtering using '<' operator", + label: "key1]*>([^<]*)`) rightTextPattern = regexp.MustCompile(`id="rightText" [^>]*>([^<]*)`) revisionTextPattern = regexp.MustCompile(`id="revisionText" [^>]*>([^<]*)`) + titleTextPattern = regexp.MustCompile(`id="titleText" [^>]*>([^<]*)`) + titleRectWidthPattern = regexp.MustCompile(`(id="titleRect" .* width=)("0")`) + rightRectWidthPattern = regexp.MustCompile(`(id="rightRect" .* width=)("\d*")`) + leftRectYCoodPattern = regexp.MustCompile(`(id="leftRect" .* y=)("\d*")`) + rightRectYCoodPattern = regexp.MustCompile(`(id="rightRect" .* y=)("\d*")`) + revisionRectYCoodPattern = regexp.MustCompile(`(id="revisionRect" .* y=)("\d*")`) + leftTextYCoodPattern = regexp.MustCompile(`(id="leftText" .* y=)("\d*")`) + rightTextYCoodPattern = regexp.MustCompile(`(id="rightText" .* y=)("\d*")`) + revisionTextYCoodPattern = regexp.MustCompile(`(id="revisionText" .* y=)("\d*")`) + svgHeightPattern = regexp.MustCompile(`^( 0 && len(errs) != 0 { + w.WriteHeader(http.StatusBadRequest) + return + } + } + if apps, err := h.appClientset.ArgoprojV1alpha1().Applications(reqNs).List(context.Background(), v1.ListOptions{}); err == nil { applicationSet := argo.FilterByProjects(apps.Items, projects) for _, a := range applicationSet { if a.Status.Sync.Status != appv1.SyncStatusCodeSynced { @@ -143,6 +197,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if !notFound && revisionEnabled && revision != "" { // Increase width of SVG and enable display of revision components badge = svgWidthPattern.ReplaceAllString(badge, fmt.Sprintf(`:" + // Example: + // Argocd-Application-Name: "namespace:app-name" + HeaderArgoCDApplicationName = "Argocd-Application-Name" + + // HeaderArgoCDProjectName defines the name of the expected + // project header to be passed to the extension handler. + // Example: + // Argocd-Project-Name: "default" + HeaderArgoCDProjectName = "Argocd-Project-Name" + + // HeaderArgoCDTargetClusterURL defines the target cluster URL + // that the Argo CD application is associated with. This header + // will be populated by the extension proxy and passed to the + // configured backend service. If this header is passed by + // the client, its value will be overriden by the extension + // handler. + // + // Example: + // Argocd-Target-Cluster-URL: "https://kubernetes.default.svc.cluster.local" + HeaderArgoCDTargetClusterURL = "Argocd-Target-Cluster-URL" + + // HeaderArgoCDTargetClusterName defines the target cluster name + // that the Argo CD application is associated with. This header + // will be populated by the extension proxy and passed to the + // configured backend service. If this header is passed by + // the client, its value will be overriden by the extension + // handler. + HeaderArgoCDTargetClusterName = "Argocd-Target-Cluster-Name" ) +// RequestResources defines the authorization scope for +// an incoming request to a given extension. This struct +// is populated from pre-defined Argo CD headers. +type RequestResources struct { + ApplicationName string + ApplicationNamespace string + ProjectName string +} + +// ValidateHeaders will validate the pre-defined Argo CD +// request headers for extensions and extract the resources +// information populating and returning a RequestResources +// object. +// The pre-defined headers are: +// - Argocd-Application-Name +// - Argocd-Project-Name +// +// The headers expected format is documented in each of the constant +// types defined for them. +func ValidateHeaders(r *http.Request) (*RequestResources, error) { + appHeader := r.Header.Get(HeaderArgoCDApplicationName) + if appHeader == "" { + return nil, fmt.Errorf("header %q must be provided", HeaderArgoCDApplicationName) + } + appNamespace, appName, err := getAppName(appHeader) + if err != nil { + return nil, fmt.Errorf("error getting app details: %s", err) + } + if !argo.IsValidNamespaceName(appNamespace) { + return nil, errors.New("invalid value for namespace") + } + if !argo.IsValidAppName(appName) { + return nil, errors.New("invalid value for application name") + } + + projName := r.Header.Get(HeaderArgoCDProjectName) + if projName == "" { + return nil, fmt.Errorf("header %q must be provided", HeaderArgoCDProjectName) + } + if !argo.IsValidProjectName(projName) { + return nil, errors.New("invalid value for project name") + } + return &RequestResources{ + ApplicationName: appName, + ApplicationNamespace: appNamespace, + ProjectName: projName, + }, nil +} + +func getAppName(appHeader string) (string, string, error) { + parts := strings.Split(appHeader, ":") + if len(parts) != 2 { + return "", "", fmt.Errorf("invalid value for %q header: expected format: :", HeaderArgoCDApplicationName) + } + return parts[0], parts[1], nil +} + // ExtensionConfigs defines the configurations for all extensions // retrieved from Argo CD configmap (argocd-cm). type ExtensionConfigs struct { - Extensions []ExtensionConfig `json:"extensions"` + Extensions []ExtensionConfig `yaml:"extensions"` } // ExtensionConfig defines the configuration for one extension. type ExtensionConfig struct { // Name defines the endpoint that will be used to register // the extension route. Mandatory field. - Name string `json:"name"` - Backend BackendConfig `json:"backend"` + Name string `yaml:"name"` + Backend BackendConfig `yaml:"backend"` } // BackendConfig defines the backend service configurations that will @@ -51,7 +144,47 @@ type ExtensionConfig struct { // service. type BackendConfig struct { ProxyConfig - Services []ServiceConfig `json:"services"` + Services []ServiceConfig `yaml:"services"` +} + +// ServiceConfig provides the configuration for a backend service. +type ServiceConfig struct { + // URL is the address where the extension backend must be available. + // Mandatory field. + URL string `yaml:"url"` + + // Cluster if provided, will have to match the application + // destination name to have requests properly forwarded to this + // service URL. + Cluster *ClusterConfig `yaml:"cluster,omitempty"` + + // Headers if provided, the headers list will be added on all + // outgoing requests for this service config. + Headers []Header `yaml:"headers"` +} + +// Header defines the header to be added in the proxy requests. +type Header struct { + // Name defines the name of the header. It is a mandatory field if + // a header is provided. + Name string `yaml:"name"` + // Value defines the value of the header. The actual value can be + // provided as verbatim or as a reference to an Argo CD secret key. + // In order to provide it as a reference, it is necessary to prefix + // it with a dollar sign. + // Example: + // value: '$some.argocd.secret.key' + // In the example above, the value will be replaced with the one from + // the argocd-secret with key 'some.argocd.secret.key'. + Value string `yaml:"value"` +} + +type ClusterConfig struct { + // Server specifies the URL of the target cluster's Kubernetes control plane API. This must be set if Name is not set. + Server string `yaml:"server"` + + // Name is an alternate way of specifying the target cluster by its symbolic name. This must be set if Server is not set. + Name string `yaml:"name"` } // ProxyConfig allows configuring connection behaviour between Argo CD @@ -60,36 +193,24 @@ type ProxyConfig struct { // ConnectionTimeout is the maximum amount of time a dial to // the extension server will wait for a connect to complete. // Default: 2 seconds - ConnectionTimeout time.Duration `json:"connectionTimeout"` + ConnectionTimeout time.Duration `yaml:"connectionTimeout"` // KeepAlive specifies the interval between keep-alive probes // for an active network connection between the API server and // the extension server. // Default: 15 seconds - KeepAlive time.Duration `json:"keepAlive"` + KeepAlive time.Duration `yaml:"keepAlive"` // IdleConnectionTimeout is the maximum amount of time an idle // (keep-alive) connection between the API server and the extension // server will remain idle before closing itself. // Default: 60 seconds - IdleConnectionTimeout time.Duration `json:"idleConnectionTimeout"` + IdleConnectionTimeout time.Duration `yaml:"idleConnectionTimeout"` // MaxIdleConnections controls the maximum number of idle (keep-alive) // connections between the API server and the extension server. // Default: 30 - MaxIdleConnections int `json:"maxIdleConnections"` -} - -// ServiceConfig provides the configuration for a backend service. -type ServiceConfig struct { - // URL is the address where the extension backend must be available. - // Mandatory field. - URL string `json:"url"` - - // Cluster if provided, will have to match the application - // destination name to have requests properly forwarded to this - // service URL. - Cluster string `json:"cluster"` + MaxIdleConnections int `yaml:"maxIdleConnections"` } // SettingsGetter defines the contract to retrieve Argo CD Settings. @@ -114,6 +235,36 @@ func (s *DefaultSettingsGetter) Get() (*settings.ArgoCDSettings, error) { return s.settingsMgr.GetSettings() } +// ProjectGetter defines the contract to retrieve Argo CD Project. +type ProjectGetter interface { + Get(name string) (*v1alpha1.AppProject, error) + GetClusters(project string) ([]*v1alpha1.Cluster, error) +} + +// DefaultProjectGetter is the real ProjectGetter implementation. +type DefaultProjectGetter struct { + projLister applisters.AppProjectNamespaceLister + db db.ArgoDB +} + +// NewDefaultProjectGetter returns a new default project getter +func NewDefaultProjectGetter(lister applisters.AppProjectNamespaceLister, db db.ArgoDB) *DefaultProjectGetter { + return &DefaultProjectGetter{ + projLister: lister, + db: db, + } +} + +// Get will retrieve the live AppProject state. +func (p *DefaultProjectGetter) Get(name string) (*v1alpha1.AppProject, error) { + return p.projLister.Get(name) +} + +// GetClusters will retrieve the clusters configured by a project. +func (p *DefaultProjectGetter) GetClusters(project string) ([]*v1alpha1.Cluster, error) { + return p.db.GetProjectClusters(context.TODO(), project) +} + // ApplicationGetter defines the contract to retrieve the application resource. type ApplicationGetter interface { Get(ns, name string) (*v1alpha1.Application, error) @@ -121,23 +272,24 @@ type ApplicationGetter interface { // DefaultApplicationGetter is the real application getter implementation. type DefaultApplicationGetter struct { - svc applicationpkg.ApplicationServiceServer + appLister applisters.ApplicationLister } // NewDefaultApplicationGetter returns the default application getter. -func NewDefaultApplicationGetter(appSvc applicationpkg.ApplicationServiceServer) *DefaultApplicationGetter { +func NewDefaultApplicationGetter(al applisters.ApplicationLister) *DefaultApplicationGetter { return &DefaultApplicationGetter{ - svc: appSvc, + appLister: al, } } // Get will retrieve the application resorce for the given namespace and name. func (a *DefaultApplicationGetter) Get(ns, name string) (*v1alpha1.Application, error) { - query := &applicationpkg.ApplicationQuery{ - Name: pointer.String(name), - AppNamespace: pointer.String(ns), - } - return a.svc.Get(context.Background(), query) + return a.appLister.Applications(ns).Get(name) +} + +// RbacEnforcer defines the contract to enforce rbac rules +type RbacEnforcer interface { + EnforceErr(rvals ...interface{}) error } // Manager is the object that will be responsible for registering @@ -146,22 +298,91 @@ type Manager struct { log *log.Entry settings SettingsGetter application ApplicationGetter + project ProjectGetter + rbac RbacEnforcer + registry ExtensionRegistry + metricsReg ExtensionMetricsRegistry +} + +// ExtensionMetricsRegistry exposes operations to update http metrics in the Argo CD +// API server. +type ExtensionMetricsRegistry interface { + // IncExtensionRequestCounter will increase the request counter for the given + // extension with the given status. + IncExtensionRequestCounter(extension string, status int) + // ObserveExtensionRequestDuration will register the request roundtrip duration + // between Argo CD API Server and the extension backend service for the given + // extension. + ObserveExtensionRequestDuration(extension string, duration time.Duration) } // NewManager will initialize a new manager. -func NewManager(sg SettingsGetter, ag ApplicationGetter, log *log.Entry) *Manager { +func NewManager(log *log.Entry, sg SettingsGetter, ag ApplicationGetter, pg ProjectGetter, rbac RbacEnforcer) *Manager { return &Manager{ log: log, settings: sg, application: ag, + project: pg, + rbac: rbac, } } -func parseAndValidateConfig(config string) (*ExtensionConfigs, error) { +// ExtensionRegistry is an in memory registry that contains contains all +// proxies for all extensions. The key is the extension name defined in +// the Argo CD configmap. +type ExtensionRegistry map[string]ProxyRegistry + +// ProxyRegistry is an in memory registry that contains all proxies for a +// given extension. Different extensions will have independent proxy registries. +// This is required to address the use case when one extension is configured with +// multiple backend services in different clusters. +type ProxyRegistry map[ProxyKey]*httputil.ReverseProxy + +// NewProxyRegistry will instantiate a new in memory registry for proxies. +func NewProxyRegistry() ProxyRegistry { + r := make(map[ProxyKey]*httputil.ReverseProxy) + return r +} + +// ProxyKey defines the struct used as a key in the proxy registry +// map (ProxyRegistry). +type ProxyKey struct { + extensionName string + clusterName string + clusterServer string +} + +// proxyKey will build the key to be used in the proxyByCluster +// map. +func proxyKey(extName, cName, cServer string) ProxyKey { + return ProxyKey{ + extensionName: extName, + clusterName: cName, + clusterServer: cServer, + } +} + +func parseAndValidateConfig(s *settings.ArgoCDSettings) (*ExtensionConfigs, error) { + if s.ExtensionConfig == "" { + return nil, fmt.Errorf("no extensions configurations found") + } + + extConfigMap := map[string]interface{}{} + err := yaml.Unmarshal([]byte(s.ExtensionConfig), &extConfigMap) + if err != nil { + return nil, fmt.Errorf("invalid extension config: %s", err) + } + + parsedExtConfig := settings.ReplaceMapSecrets(extConfigMap, s.Secrets) + parsedExtConfigBytes, err := yaml.Marshal(parsedExtConfig) + if err != nil { + return nil, fmt.Errorf("error marshaling parsed extension config: %s", err) + } + configs := ExtensionConfigs{} - err := yaml.Unmarshal([]byte(config), &configs) + err = yaml.Unmarshal(parsedExtConfigBytes, &configs) if err != nil { - return nil, fmt.Errorf("invalid yaml: %s", err) + return nil, fmt.Errorf("invalid parsed extension config: %s", err) } err = validateConfigs(&configs) if err != nil { @@ -172,6 +393,7 @@ func parseAndValidateConfig(config string) (*ExtensionConfigs, error) { func validateConfigs(configs *ExtensionConfigs) error { nameSafeRegex := regexp.MustCompile(`^[A-Za-z0-9-_]+$`) + exts := make(map[string]struct{}) for _, ext := range configs.Extensions { if ext.Name == "" { return fmt.Errorf("extensions.name must be configured") @@ -179,28 +401,63 @@ func validateConfigs(configs *ExtensionConfigs) error { if !nameSafeRegex.MatchString(ext.Name) { return fmt.Errorf("invalid extensions.name: only alphanumeric characters, hyphens, and underscores are allowed") } + if _, found := exts[ext.Name]; found { + return fmt.Errorf("duplicated extension found in the configs for %q", ext.Name) + } + exts[ext.Name] = struct{}{} svcTotal := len(ext.Backend.Services) + if svcTotal == 0 { + return fmt.Errorf("no backend service configured for extension %s", ext.Name) + } for _, svc := range ext.Backend.Services { if svc.URL == "" { return fmt.Errorf("extensions.backend.services.url must be configured") } - if svcTotal > 1 && svc.Cluster == "" { + if svcTotal > 1 && svc.Cluster == nil { return fmt.Errorf("extensions.backend.services.cluster must be configured when defining more than one service per extension") } + if svc.Cluster != nil { + if svc.Cluster.Name == "" && svc.Cluster.Server == "" { + return fmt.Errorf("cluster.name or cluster.server must be defined when cluster is provided in the configuration") + } + } + if len(svc.Headers) > 0 { + for _, header := range svc.Headers { + if header.Name == "" { + return fmt.Errorf("header.name must be defined when providing service headers in the configuration") + } + if header.Value == "" { + return fmt.Errorf("header.value must be defined when providing service headers in the configuration") + } + } + } } } return nil } // NewProxy will instantiate a new reverse proxy based on the provided -// targetURL and config. -func NewProxy(targetURL string, config ProxyConfig) (*httputil.ReverseProxy, error) { +// targetURL and config. It will remove sensitive information from the +// incoming request such as the Authorization and Cookie headers. +func NewProxy(targetURL string, headers []Header, config ProxyConfig) (*httputil.ReverseProxy, error) { url, err := url.Parse(targetURL) if err != nil { return nil, fmt.Errorf("failed to parse proxy URL: %s", err) } - proxy := httputil.NewSingleHostReverseProxy(url) - proxy.Transport = newTransport(config) + proxy := &httputil.ReverseProxy{ + Transport: newTransport(config), + Director: func(req *http.Request) { + req.Host = url.Host + req.URL.Scheme = url.Scheme + req.URL.Host = url.Host + req.Header.Set("Host", url.Host) + req.Header.Del("Authorization") + req.Header.Del("Cookie") + for _, header := range headers { + req.Header.Set(header.Name, header.Value) + } + }, + } return proxy, nil } @@ -235,123 +492,247 @@ func applyProxyConfigDefaults(c *ProxyConfig) { } } -// RegisterHandlers will retrieve all configured extensions -// and register the respective http handlers in the given -// router. -func (m *Manager) RegisterHandlers(r *mux.Router) error { - m.log.Info("Registering extension handlers...") - config, err := m.settings.Get() +// RegisterExtensions will retrieve all extensions configurations +// and update the extension registry. +func (m *Manager) RegisterExtensions() error { + settings, err := m.settings.Get() if err != nil { return fmt.Errorf("error getting settings: %s", err) } - - if config.ExtensionConfig == "" { - return fmt.Errorf("No extensions configurations found") + if settings.ExtensionConfig == "" { + m.log.Infof("No extensions configured.") + return nil } - - extConfigs, err := parseAndValidateConfig(config.ExtensionConfig) + err = m.UpdateExtensionRegistry(settings) if err != nil { - return fmt.Errorf("error parsing extension config: %s", err) + return fmt.Errorf("error updating extension registry: %s", err) } - return m.registerExtensions(r, extConfigs) + return nil } -// registerExtensions will iterate over the given extConfigs and register -// http handlers for every extension. It also registers a list extensions -// handler under the "/extensions/" endpoint. -func (m *Manager) registerExtensions(r *mux.Router, extConfigs *ExtensionConfigs) error { - extRouter := r.PathPrefix(fmt.Sprintf("%s/", URLPrefix)).Subrouter() +// UpdateExtensionRegistry will first parse and validate the extensions +// configurations from the given settings. If no errors are found, it will +// iterate over the given configurations building a new extension registry. +// At the end, it will update the manager with the newly created registry. +func (m *Manager) UpdateExtensionRegistry(s *settings.ArgoCDSettings) error { + extConfigs, err := parseAndValidateConfig(s) + if err != nil { + return fmt.Errorf("error parsing extension config: %s", err) + } + extReg := make(map[string]ProxyRegistry) for _, ext := range extConfigs.Extensions { - proxyByCluster := make(map[string]*httputil.ReverseProxy) + proxyReg := NewProxyRegistry() + singleBackend := len(ext.Backend.Services) == 1 for _, service := range ext.Backend.Services { - proxy, err := NewProxy(service.URL, ext.Backend.ProxyConfig) + proxy, err := NewProxy(service.URL, service.Headers, ext.Backend.ProxyConfig) if err != nil { return fmt.Errorf("error creating proxy: %s", err) } - proxyByCluster[service.Cluster] = proxy + err = appendProxy(proxyReg, ext.Name, service, proxy, singleBackend) + if err != nil { + return fmt.Errorf("error appending proxy: %s", err) + } + } + extReg[ext.Name] = proxyReg + } + m.registry = extReg + return nil +} + +// appendProxy will append the given proxy in the given registry. Will use +// the provided extName and service to determine the map key. The key must +// be unique in the map. If the map already has the key and error is returned. +func appendProxy(registry ProxyRegistry, + extName string, + service ServiceConfig, + proxy *httputil.ReverseProxy, + singleBackend bool) error { + + if singleBackend { + key := proxyKey(extName, "", "") + if _, exist := registry[key]; exist { + return fmt.Errorf("duplicated proxy configuration found for extension key %q", key) + } + registry[key] = proxy + return nil + } + + // This is the case where there are more than one backend configured + // for this extension. In this case we need to add the provided cluster + // configurations for proper correlation to find which proxy to use + // while handling requests. + if service.Cluster.Name != "" { + key := proxyKey(extName, service.Cluster.Name, "") + if _, exist := registry[key]; exist { + return fmt.Errorf("duplicated proxy configuration found for extension key %q", key) + } + registry[key] = proxy + } + if service.Cluster.Server != "" { + key := proxyKey(extName, "", service.Cluster.Server) + if _, exist := registry[key]; exist { + return fmt.Errorf("duplicated proxy configuration found for extension key %q", key) } - m.log.Infof("Registering handler for %s/%s...", URLPrefix, ext.Name) - extRouter.PathPrefix(fmt.Sprintf("/%s/", ext.Name)). - HandlerFunc(m.CallExtension(ext.Name, proxyByCluster)) + registry[key] = proxy } return nil } +// authorize will enforce rbac rules are satified for the given RequestResources. +// The following validations are executed: +// - enforce the subject has permission to read application/project provided +// in HeaderArgoCDApplicationName and HeaderArgoCDProjectName. +// - enforce the subject has permission to invoke the extension identified by +// extName. +// - enforce that the project has permission to access the destination cluster. +// +// If all validations are satified it will return the Application resource +func (m *Manager) authorize(ctx context.Context, rr *RequestResources, extName string) (*v1alpha1.Application, error) { + if m.rbac == nil { + return nil, fmt.Errorf("rbac enforcer not set in extension manager") + } + appRBACName := security.RBACName(rr.ApplicationNamespace, rr.ProjectName, rr.ApplicationNamespace, rr.ApplicationName) + if err := m.rbac.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACName); err != nil { + return nil, fmt.Errorf("application authorization error: %s", err) + } + + if err := m.rbac.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceExtensions, rbacpolicy.ActionInvoke, extName); err != nil { + return nil, fmt.Errorf("unauthorized to invoke extension %q: %s", extName, err) + } + + // just retrieve the app after checking if subject has access to it + app, err := m.application.Get(rr.ApplicationNamespace, rr.ApplicationName) + if err != nil { + return nil, fmt.Errorf("error getting application: %s", err) + } + if app == nil { + return nil, fmt.Errorf("invalid Application provided in the %q header", HeaderArgoCDApplicationName) + } + + if app.Spec.GetProject() != rr.ProjectName { + return nil, fmt.Errorf("project mismatch provided in the %q header", HeaderArgoCDProjectName) + } + + proj, err := m.project.Get(app.Spec.GetProject()) + if err != nil { + return nil, fmt.Errorf("error getting project: %s", err) + } + if proj == nil { + return nil, fmt.Errorf("invalid project provided in the %q header", HeaderArgoCDProjectName) + } + permitted, err := proj.IsDestinationPermitted(app.Spec.Destination, m.project.GetClusters) + if err != nil { + return nil, fmt.Errorf("error validating project destinations: %s", err) + } + if !permitted { + return nil, fmt.Errorf("the provided project is not allowed to access the cluster configured in the Application destination") + } + + return app, nil +} + +// findProxy will search the given registry to find the correct proxy to use +// based on the given extName and dest. +func findProxy(registry ProxyRegistry, extName string, dest v1alpha1.ApplicationDestination) (*httputil.ReverseProxy, error) { + + // First try to find the proxy in the registry just by the extension name. + // This is the simple case for extensions with only one backend service. + key := proxyKey(extName, "", "") + if proxy, found := registry[key]; found { + return proxy, nil + } + + // If extension has multiple backend services configured, the correct proxy + // needs to be searched by the ApplicationDestination. + key = proxyKey(extName, dest.Name, dest.Server) + if proxy, found := registry[key]; found { + return proxy, nil + } + + return nil, fmt.Errorf("no proxy found for extension %q", extName) +} + +// ProxyRegistry returns the proxy registry associated for the given +// extension name. +func (m *Manager) ProxyRegistry(name string) (ProxyRegistry, bool) { + pReg, found := m.registry[name] + return pReg, found +} + // CallExtension returns a handler func responsible for forwarding requests to the // extension service. The request will be sanitized by removing sensitive headers. -func (m *Manager) CallExtension(extName string, proxyByCluster map[string]*httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) { +func (m *Manager) CallExtension() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - sanitizeRequest(r, extName) - if len(proxyByCluster) == 1 { - for _, proxy := range proxyByCluster { - proxy.ServeHTTP(w, r) - return - } - } - appHeader := r.Header.Get(HeaderArgoCDApplicationName) - if appHeader == "" { - msg := fmt.Sprintf("Header %q must be provided", HeaderArgoCDApplicationName) - m.writeErrorResponse(http.StatusBadRequest, msg, w) + segments := strings.Split(strings.TrimPrefix(r.URL.Path, "/"), "/") + if segments[0] != "extensions" { + http.Error(w, fmt.Sprintf("Invalid URL: first segment must be %s", URLPrefix), http.StatusBadRequest) return } - appNamespace, appName, err := getAppName(appHeader) - if err != nil { - msg := fmt.Sprintf("Error getting application name: %s", err) - m.writeErrorResponse(http.StatusBadRequest, msg, w) + extName := segments[1] + if extName == "" { + http.Error(w, "Invalid URL: extension name must be provided", http.StatusBadRequest) return } - app, err := m.application.Get(appNamespace, appName) + extName = strings.ReplaceAll(extName, "\n", "") + extName = strings.ReplaceAll(extName, "\r", "") + reqResources, err := ValidateHeaders(r) if err != nil { - msg := fmt.Sprintf("Error getting application: %s", err) - m.writeErrorResponse(http.StatusBadRequest, msg, w) + http.Error(w, fmt.Sprintf("Invalid headers: %s", err), http.StatusBadRequest) return } - if app == nil { - msg := fmt.Sprintf("Invalid Application: %s", appHeader) - m.writeErrorResponse(http.StatusBadRequest, msg, w) + app, err := m.authorize(r.Context(), reqResources, extName) + if err != nil { + m.log.Infof("unauthorized extension request: %s", err) + http.Error(w, "Unauthorized extension request", http.StatusUnauthorized) return } - clusterName := app.Spec.Destination.Name - if clusterName == "" { - clusterName = app.Spec.Destination.Server - } - proxy, ok := proxyByCluster[clusterName] + proxyRegistry, ok := m.ProxyRegistry(extName) if !ok { - msg := fmt.Sprintf("No extension configured for cluster %q", clusterName) - m.writeErrorResponse(http.StatusBadRequest, msg, w) + m.log.Warnf("proxy extension warning: attempt to call unregistered extension: %s", extName) + http.Error(w, "Extension not found", http.StatusNotFound) + return + } + proxy, err := findProxy(proxyRegistry, extName, app.Spec.Destination) + if err != nil { + m.log.Errorf("findProxy error: %s", err) + http.Error(w, "invalid extension", http.StatusBadRequest) return } - proxy.ServeHTTP(w, r) - } -} -func getAppName(appHeader string) (string, string, error) { - parts := strings.Split(appHeader, "/") - if len(parts) != 2 { - return "", "", fmt.Errorf("invalid header value %q: expected format: /", appHeader) + prepareRequest(r, extName, app) + m.log.Debugf("proxing request for extension %q", extName) + // httpsnoop package is used to properly wrap the responseWriter + // and avoid optional intefaces issue: + // https://github.com/felixge/httpsnoop#why-this-package-exists + // CaptureMetrics will call the proxy and return the metrics from it. + metrics := httpsnoop.CaptureMetrics(proxy, w, r) + + go registerMetrics(extName, metrics, m.metricsReg) } - return parts[0], parts[1], nil } -func sanitizeRequest(r *http.Request, extName string) { - r.URL.Path = strings.TrimPrefix(r.URL.String(), fmt.Sprintf("%s/%s", URLPrefix, extName)) +func registerMetrics(extName string, metrics httpsnoop.Metrics, extensionMetricsRegistry ExtensionMetricsRegistry) { + if extensionMetricsRegistry != nil { + extensionMetricsRegistry.IncExtensionRequestCounter(extName, metrics.Code) + extensionMetricsRegistry.ObserveExtensionRequestDuration(extName, metrics.Duration) + } } -func (m *Manager) writeErrorResponse(status int, message string, w http.ResponseWriter) { - w.WriteHeader(status) - w.Header().Set("Content-Type", "application/json") - resp := make(map[string]string) - resp["status"] = http.StatusText(status) - resp["message"] = message - jsonResp, err := json.Marshal(resp) - if err != nil { - m.log.Errorf("Error marshaling response for extension: %s", err) - return +// prepareRequest is reponsible for cleaning the incoming request URL removing +// the Argo CD extension API section from it. It will set the cluster destination name +// and cluster destination server in the headers as it is defined in the given app. +func prepareRequest(r *http.Request, extName string, app *v1alpha1.Application) { + r.URL.Path = strings.TrimPrefix(r.URL.Path, fmt.Sprintf("%s/%s", URLPrefix, extName)) + if app.Spec.Destination.Name != "" { + r.Header.Set(HeaderArgoCDTargetClusterName, app.Spec.Destination.Name) } - _, err = w.Write(jsonResp) - if err != nil { - m.log.Errorf("Error writing response for extension: %s", err) - return + if app.Spec.Destination.Server != "" { + r.Header.Set(HeaderArgoCDTargetClusterURL, app.Spec.Destination.Server) } } + +// AddMetricsRegistry will associate the given metricsReg in the Manager. +func (m *Manager) AddMetricsRegistry(metricsReg ExtensionMetricsRegistry) { + m.metricsReg = metricsReg +} diff --git a/server/extension/extension_test.go b/server/extension/extension_test.go index a6fa7b5d6cf8f..ff287dde80424 100644 --- a/server/extension/extension_test.go +++ b/server/extension/extension_test.go @@ -2,26 +2,144 @@ package extension_test import ( "context" + "errors" "fmt" "io" "net/http" "net/http/httptest" "strings" + "sync" "testing" - "github.com/gorilla/mux" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/server/extension" "github.com/argoproj/argo-cd/v2/server/extension/mocks" + "github.com/argoproj/argo-cd/v2/server/rbacpolicy" "github.com/argoproj/argo-cd/v2/util/settings" ) -func TestRegisterHandlers(t *testing.T) { +func TestValidateHeaders(t *testing.T) { + t.Run("will build RequestResources successfully", func(t *testing.T) { + // given + r, err := http.NewRequest("Get", "http://null", nil) + if err != nil { + t.Fatalf("error initializing request: %s", err) + } + r.Header.Add(extension.HeaderArgoCDApplicationName, "namespace:app-name") + r.Header.Add(extension.HeaderArgoCDProjectName, "project-name") + + // when + rr, err := extension.ValidateHeaders(r) + + // then + require.NoError(t, err) + assert.NotNil(t, rr) + assert.Equal(t, "namespace", rr.ApplicationNamespace) + assert.Equal(t, "app-name", rr.ApplicationName) + assert.Equal(t, "project-name", rr.ProjectName) + }) + t.Run("will return error if application is malformatted", func(t *testing.T) { + // given + r, err := http.NewRequest("Get", "http://null", nil) + if err != nil { + t.Fatalf("error initializing request: %s", err) + } + r.Header.Add(extension.HeaderArgoCDApplicationName, "no-namespace") + + // when + rr, err := extension.ValidateHeaders(r) + + // then + assert.Error(t, err) + assert.Nil(t, rr) + }) + t.Run("will return error if application header is missing", func(t *testing.T) { + // given + r, err := http.NewRequest("Get", "http://null", nil) + if err != nil { + t.Fatalf("error initializing request: %s", err) + } + r.Header.Add(extension.HeaderArgoCDProjectName, "project-name") + + // when + rr, err := extension.ValidateHeaders(r) + + // then + assert.Error(t, err) + assert.Nil(t, rr) + }) + t.Run("will return error if project header is missing", func(t *testing.T) { + // given + r, err := http.NewRequest("Get", "http://null", nil) + if err != nil { + t.Fatalf("error initializing request: %s", err) + } + r.Header.Add(extension.HeaderArgoCDApplicationName, "namespace:app-name") + + // when + rr, err := extension.ValidateHeaders(r) + + // then + assert.Error(t, err) + assert.Nil(t, rr) + }) + t.Run("will return error if invalid namespace", func(t *testing.T) { + // given + r, err := http.NewRequest("Get", "http://null", nil) + if err != nil { + t.Fatalf("error initializing request: %s", err) + } + r.Header.Add(extension.HeaderArgoCDApplicationName, "bad%namespace:app-name") + r.Header.Add(extension.HeaderArgoCDProjectName, "project-name") + + // when + rr, err := extension.ValidateHeaders(r) + + // then + assert.Error(t, err) + assert.Nil(t, rr) + }) + t.Run("will return error if invalid app name", func(t *testing.T) { + // given + r, err := http.NewRequest("Get", "http://null", nil) + if err != nil { + t.Fatalf("error initializing request: %s", err) + } + r.Header.Add(extension.HeaderArgoCDApplicationName, "namespace:bad@app") + r.Header.Add(extension.HeaderArgoCDProjectName, "project-name") + + // when + rr, err := extension.ValidateHeaders(r) + + // then + assert.Error(t, err) + assert.Nil(t, rr) + }) + t.Run("will return error if invalid project name", func(t *testing.T) { + // given + r, err := http.NewRequest("Get", "http://null", nil) + if err != nil { + t.Fatalf("error initializing request: %s", err) + } + r.Header.Add(extension.HeaderArgoCDApplicationName, "namespace:app") + r.Header.Add(extension.HeaderArgoCDProjectName, "bad^project") + + // when + rr, err := extension.ValidateHeaders(r) + + // then + assert.Error(t, err) + assert.Nil(t, rr) + }) +} + +func TestRegisterExtensions(t *testing.T) { type fixture struct { settingsGetterMock *mocks.SettingsGetter manager *extension.Manager @@ -32,56 +150,53 @@ func TestRegisterHandlers(t *testing.T) { logger, _ := test.NewNullLogger() logEntry := logger.WithContext(context.Background()) - m := extension.NewManager(settMock, nil, logEntry) + m := extension.NewManager(logEntry, settMock, nil, nil, nil) return &fixture{ settingsGetterMock: settMock, manager: m, } } - t.Run("will register handlers successfully", func(t *testing.T) { + t.Run("will register extensions successfully", func(t *testing.T) { // given + t.Parallel() f := setup() - router := mux.NewRouter() settings := &settings.ArgoCDSettings{ ExtensionConfig: getExtensionConfigString(), } f.settingsGetterMock.On("Get", mock.Anything).Return(settings, nil) - expectedRegexRoutes := []string{ - "^/extensions/", - "^/extensions/external-backend/", - "^/extensions/some-backend/", - "^/extensions/$"} + expectedProxyRegistries := []string{ + "external-backend", + "some-backend"} // when - err := f.manager.RegisterHandlers(router) + err := f.manager.RegisterExtensions() // then require.NoError(t, err) - walkFn := func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { - pathRegex, err := route.GetPathRegexp() - require.NoError(t, err) - assert.Contains(t, expectedRegexRoutes, pathRegex) - return nil + for _, expectedProxyRegistry := range expectedProxyRegistries { + proxyRegistry, found := f.manager.ProxyRegistry(expectedProxyRegistry) + assert.True(t, found) + assert.NotNil(t, proxyRegistry) } - err = router.Walk(walkFn) - assert.NoError(t, err) + }) t.Run("will return error if extension config is invalid", func(t *testing.T) { // given + t.Parallel() type testCase struct { name string configYaml string } cases := []testCase{ - { - name: "no config", - configYaml: "", - }, { name: "no name", configYaml: getExtensionConfigNoName(), }, + { + name: "no service", + configYaml: getExtensionConfigNoService(), + }, { name: "no URL", configYaml: getExtensionConfigNoURL(), @@ -90,6 +205,14 @@ func TestRegisterHandlers(t *testing.T) { name: "invalid name", configYaml: getExtensionConfigInvalidName(), }, + { + name: "no header name", + configYaml: getExtensionConfigNoHeaderName(), + }, + { + name: "no header value", + configYaml: getExtensionConfigNoHeaderValue(), + }, } // when @@ -97,159 +220,291 @@ func TestRegisterHandlers(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { // given + t.Parallel() f := setup() - router := mux.NewRouter() settings := &settings.ArgoCDSettings{ ExtensionConfig: tc.configYaml, } f.settingsGetterMock.On("Get", mock.Anything).Return(settings, nil) // when - err := f.manager.RegisterHandlers(router) + err := f.manager.RegisterExtensions() // then - assert.Error(t, err) + assert.Error(t, err, fmt.Sprintf("expected error in test %s but got nil", tc.name)) }) } }) } -func TestExtensionsHandlers(t *testing.T) { +func TestCallExtension(t *testing.T) { type fixture struct { - router *mux.Router + mux *http.ServeMux appGetterMock *mocks.ApplicationGetter settingsGetterMock *mocks.SettingsGetter + rbacMock *mocks.RbacEnforcer + projMock *mocks.ProjectGetter + metricsMock *mocks.ExtensionMetricsRegistry manager *extension.Manager } + defaultProjectName := "project-name" setup := func() *fixture { appMock := &mocks.ApplicationGetter{} settMock := &mocks.SettingsGetter{} + rbacMock := &mocks.RbacEnforcer{} + projMock := &mocks.ProjectGetter{} + metricsMock := &mocks.ExtensionMetricsRegistry{} logger, _ := test.NewNullLogger() logEntry := logger.WithContext(context.Background()) - m := extension.NewManager(settMock, appMock, logEntry) + m := extension.NewManager(logEntry, settMock, appMock, projMock, rbacMock) + m.AddMetricsRegistry(metricsMock) - router := mux.NewRouter() + mux := http.NewServeMux() + extHandler := http.HandlerFunc(m.CallExtension()) + mux.Handle(fmt.Sprintf("%s/", extension.URLPrefix), extHandler) return &fixture{ - router: router, + mux: mux, appGetterMock: appMock, settingsGetterMock: settMock, + rbacMock: rbacMock, + projMock: projMock, + metricsMock: metricsMock, manager: m, } } + getApp := func(destName, destServer, projName string) *v1alpha1.Application { + return &v1alpha1.Application{ + TypeMeta: v1.TypeMeta{}, + ObjectMeta: v1.ObjectMeta{}, + Spec: v1alpha1.ApplicationSpec{ + Destination: v1alpha1.ApplicationDestination{ + Name: destName, + Server: destServer, + }, + Project: projName, + }, + Status: v1alpha1.ApplicationStatus{ + Resources: []v1alpha1.ResourceStatus{ + { + Group: "apps", + Version: "v1", + Kind: "Pod", + Namespace: "default", + Name: "some-pod", + }, + }, + }, + } + } + + getProjectWithDestinations := func(prjName string, destNames []string, destURLs []string) *v1alpha1.AppProject { + destinations := []v1alpha1.ApplicationDestination{} + for _, destName := range destNames { + destination := v1alpha1.ApplicationDestination{ + Name: destName, + } + destinations = append(destinations, destination) + } + for _, destURL := range destURLs { + destination := v1alpha1.ApplicationDestination{ + Server: destURL, + } + destinations = append(destinations, destination) + } + return &v1alpha1.AppProject{ + ObjectMeta: v1.ObjectMeta{ + Name: prjName, + }, + Spec: v1alpha1.AppProjectSpec{ + Destinations: destinations, + }, + } + } + + withProject := func(prj *v1alpha1.AppProject, f *fixture) { + f.projMock.On("Get", prj.GetName()).Return(prj, nil) + } + + withMetrics := func(f *fixture) { + f.metricsMock.On("IncExtensionRequestCounter", mock.Anything, mock.Anything) + f.metricsMock.On("ObserveExtensionRequestDuration", mock.Anything, mock.Anything) + } + + withRbac := func(f *fixture, allowApp, allowExt bool) { + var appAccessError error + var extAccessError error + if !allowApp { + appAccessError = errors.New("no app permission") + } + if !allowExt { + extAccessError = errors.New("no extension permission") + } + f.rbacMock.On("EnforceErr", mock.Anything, rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, mock.Anything).Return(appAccessError) + f.rbacMock.On("EnforceErr", mock.Anything, rbacpolicy.ResourceExtensions, rbacpolicy.ActionInvoke, mock.Anything).Return(extAccessError) + } + withExtensionConfig := func(configYaml string, f *fixture) { + secrets := make(map[string]string) + secrets["extension.auth.header"] = "Bearer some-bearer-token" + secrets["extension.auth.header2"] = "Bearer another-bearer-token" + settings := &settings.ArgoCDSettings{ ExtensionConfig: configYaml, + Secrets: secrets, } f.settingsGetterMock.On("Get", mock.Anything).Return(settings, nil) } startTestServer := func(t *testing.T, f *fixture) *httptest.Server { - err := f.manager.RegisterHandlers(f.router) + t.Helper() + err := f.manager.RegisterExtensions() if err != nil { t.Fatalf("error starting test server: %s", err) } - return httptest.NewServer(f.router) + return httptest.NewServer(f.mux) } startBackendTestSrv := func(response string) *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + for k, v := range r.Header { + w.Header().Add(k, strings.Join(v, ",")) + } fmt.Fprintln(w, response) })) } - t.Run("proxy will return 404 if no extension endpoint is registered", func(t *testing.T) { - // given - f := setup() - withExtensionConfig(getExtensionConfigString(), f) - ts := startTestServer(t, f) - defer ts.Close() - nonRegisteredEndpoint := "non-registered" - - // when - resp, err := http.Get(fmt.Sprintf("%s/extensions/%s/", ts.URL, nonRegisteredEndpoint)) + newExtensionRequest := func(t *testing.T, method, url string) *http.Request { + t.Helper() + r, err := http.NewRequest(method, url, nil) + if err != nil { + t.Fatalf("error initializing request: %s", err) + } + r.Header.Add(extension.HeaderArgoCDApplicationName, "namespace:app-name") + r.Header.Add(extension.HeaderArgoCDProjectName, defaultProjectName) + return r + } - // then - require.NoError(t, err) - require.NotNil(t, resp) - assert.Equal(t, http.StatusNotFound, resp.StatusCode) - }) t.Run("will call extension backend successfully", func(t *testing.T) { // given + t.Parallel() f := setup() backendResponse := "some data" backendEndpoint := "some-backend" + clusterName := "clusterName" + clusterURL := "clusterURL" backendSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + for k, v := range r.Header { + w.Header().Add(k, strings.Join(v, ",")) + } fmt.Fprintln(w, backendResponse) })) defer backendSrv.Close() + withRbac(f, true, true) withExtensionConfig(getExtensionConfig(backendEndpoint, backendSrv.URL), f) ts := startTestServer(t, f) defer ts.Close() + r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/%s/", ts.URL, backendEndpoint)) + app := getApp(clusterName, clusterURL, defaultProjectName) + proj := getProjectWithDestinations("project-name", nil, []string{clusterURL}) + f.appGetterMock.On("Get", mock.Anything, mock.Anything).Return(app, nil) + withProject(proj, f) + var wg sync.WaitGroup + wg.Add(2) + f.metricsMock. + On("IncExtensionRequestCounter", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + wg.Done() + }) + f.metricsMock. + On("ObserveExtensionRequestDuration", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + wg.Done() + }) // when - resp, err := http.Get(fmt.Sprintf("%s/extensions/%s/", ts.URL, backendEndpoint)) + resp, err := http.DefaultClient.Do(r) // then require.NoError(t, err) require.NotNil(t, resp) - require.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, http.StatusOK, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) actual := strings.TrimSuffix(string(body), "\n") assert.Equal(t, backendResponse, actual) + assert.Equal(t, clusterURL, resp.Header.Get(extension.HeaderArgoCDTargetClusterURL)) + assert.Equal(t, "Bearer some-bearer-token", resp.Header.Get("Authorization")) + + // waitgroup is necessary to make sure assertions aren't executed before + // the goroutine initiated by extension.CallExtension concludes which would + // lead to flaky test. + wg.Wait() + f.metricsMock.AssertCalled(t, "IncExtensionRequestCounter", backendEndpoint, http.StatusOK) + f.metricsMock.AssertCalled(t, "ObserveExtensionRequestDuration", backendEndpoint, mock.Anything) + }) + t.Run("proxy will return 404 if extension endpoint not registered", func(t *testing.T) { + // given + t.Parallel() + f := setup() + withExtensionConfig(getExtensionConfigString(), f) + withRbac(f, true, true) + withMetrics(f) + cluster1Name := "cluster1" + f.appGetterMock.On("Get", "namespace", "app-name").Return(getApp(cluster1Name, "", defaultProjectName), nil) + withProject(getProjectWithDestinations("project-name", []string{cluster1Name}, []string{"some-url"}), f) + + ts := startTestServer(t, f) + defer ts.Close() + nonRegistered := "non-registered" + r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/%s/", ts.URL, nonRegistered)) + + // when + resp, err := http.DefaultClient.Do(r) + + // then + require.NoError(t, err) + require.NotNil(t, resp) + assert.Equal(t, http.StatusNotFound, resp.StatusCode) }) t.Run("will route requests with 2 backends for the same extension successfully", func(t *testing.T) { // given + t.Parallel() f := setup() extName := "some-extension" response1 := "response backend 1" - cluster1 := "cluster1" + cluster1Name := "cluster1" beSrv1 := startBackendTestSrv(response1) defer beSrv1.Close() response2 := "response backend 2" - cluster2 := "cluster2" + cluster2URL := "cluster2" beSrv2 := startBackendTestSrv(response2) defer beSrv2.Close() - withExtensionConfig(getExtensionConfigWith2Backends(extName, beSrv1.URL, cluster1, beSrv2.URL, cluster2), f) - ts := startTestServer(t, f) - defer ts.Close() + f.appGetterMock.On("Get", "ns1", "app1").Return(getApp(cluster1Name, "", defaultProjectName), nil) + f.appGetterMock.On("Get", "ns2", "app2").Return(getApp("", cluster2URL, defaultProjectName), nil) - app1 := &v1alpha1.Application{ - Spec: v1alpha1.ApplicationSpec{ - Destination: v1alpha1.ApplicationDestination{ - Server: beSrv1.URL, - Name: cluster1, - }, - }, - } - f.appGetterMock.On("Get", "ns1", "app1").Return(app1, nil) + withRbac(f, true, true) + withExtensionConfig(getExtensionConfigWith2Backends(extName, beSrv1.URL, cluster1Name, beSrv2.URL, cluster2URL), f) + withProject(getProjectWithDestinations("project-name", []string{cluster1Name}, []string{cluster2URL}), f) + withMetrics(f) - app2 := &v1alpha1.Application{ - Spec: v1alpha1.ApplicationSpec{ - Destination: v1alpha1.ApplicationDestination{ - Server: beSrv2.URL, - Name: cluster2, - }, - }, - } - f.appGetterMock.On("Get", "ns2", "app2").Return(app2, nil) + ts := startTestServer(t, f) + defer ts.Close() url := fmt.Sprintf("%s/extensions/%s/", ts.URL, extName) - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - t.Fatalf("error creating request: %s", err) - } + req := newExtensionRequest(t, http.MethodGet, url) + req.Header.Del(extension.HeaderArgoCDApplicationName) + req1 := req.Clone(context.Background()) - req1.Header.Add(extension.HeaderArgoCDApplicationName, "ns1/app1") + req1.Header.Add(extension.HeaderArgoCDApplicationName, "ns1:app1") req2 := req.Clone(context.Background()) - req2.Header.Add(extension.HeaderArgoCDApplicationName, "ns2/app2") + req2.Header.Add(extension.HeaderArgoCDApplicationName, "ns2:app2") // when resp1, err := http.DefaultClient.Do(req1) @@ -259,18 +514,204 @@ func TestExtensionsHandlers(t *testing.T) { // then require.NotNil(t, resp1) - require.Equal(t, http.StatusOK, resp1.StatusCode) + assert.Equal(t, http.StatusOK, resp1.StatusCode) body, err := io.ReadAll(resp1.Body) require.NoError(t, err) actual := strings.TrimSuffix(string(body), "\n") assert.Equal(t, response1, actual) + assert.Equal(t, "Bearer some-bearer-token", resp1.Header.Get("Authorization")) require.NotNil(t, resp2) - require.Equal(t, http.StatusOK, resp2.StatusCode) + assert.Equal(t, http.StatusOK, resp2.StatusCode) body, err = io.ReadAll(resp2.Body) require.NoError(t, err) actual = strings.TrimSuffix(string(body), "\n") assert.Equal(t, response2, actual) + assert.Equal(t, "Bearer another-bearer-token", resp2.Header.Get("Authorization")) + }) + t.Run("will return 401 if sub has no access to get application", func(t *testing.T) { + // given + t.Parallel() + f := setup() + allowApp := false + allowExtension := true + extName := "some-extension" + withRbac(f, allowApp, allowExtension) + withExtensionConfig(getExtensionConfig(extName, "http://fake"), f) + withMetrics(f) + ts := startTestServer(t, f) + defer ts.Close() + r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/%s/", ts.URL, extName)) + f.appGetterMock.On("Get", mock.Anything, mock.Anything).Return(getApp("", "", defaultProjectName), nil) + + // when + resp, err := http.DefaultClient.Do(r) + + // then + require.NoError(t, err) + require.NotNil(t, resp) + assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) + }) + t.Run("will return 401 if sub has no access to invoke extension", func(t *testing.T) { + // given + t.Parallel() + f := setup() + allowApp := true + allowExtension := false + extName := "some-extension" + withRbac(f, allowApp, allowExtension) + withExtensionConfig(getExtensionConfig(extName, "http://fake"), f) + withMetrics(f) + ts := startTestServer(t, f) + defer ts.Close() + r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/%s/", ts.URL, extName)) + f.appGetterMock.On("Get", mock.Anything, mock.Anything).Return(getApp("", "", defaultProjectName), nil) + + // when + resp, err := http.DefaultClient.Do(r) + + // then + require.NoError(t, err) + require.NotNil(t, resp) + assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) + }) + t.Run("will return 401 if project has no access to target cluster", func(t *testing.T) { + // given + t.Parallel() + f := setup() + allowApp := true + allowExtension := true + extName := "some-extension" + noCluster := []string{} + withRbac(f, allowApp, allowExtension) + withExtensionConfig(getExtensionConfig(extName, "http://fake"), f) + withMetrics(f) + ts := startTestServer(t, f) + defer ts.Close() + r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/%s/", ts.URL, extName)) + f.appGetterMock.On("Get", mock.Anything, mock.Anything).Return(getApp("", "", defaultProjectName), nil) + proj := getProjectWithDestinations("project-name", nil, noCluster) + withProject(proj, f) + + // when + resp, err := http.DefaultClient.Do(r) + + // then + require.NoError(t, err) + require.NotNil(t, resp) + assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) + }) + t.Run("will return 401 if project in application does not exist", func(t *testing.T) { + // given + t.Parallel() + f := setup() + allowApp := true + allowExtension := true + extName := "some-extension" + withRbac(f, allowApp, allowExtension) + withExtensionConfig(getExtensionConfig(extName, "http://fake"), f) + withMetrics(f) + ts := startTestServer(t, f) + defer ts.Close() + r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/%s/", ts.URL, extName)) + f.appGetterMock.On("Get", mock.Anything, mock.Anything).Return(getApp("", "", defaultProjectName), nil) + f.projMock.On("Get", defaultProjectName).Return(nil, nil) + + // when + resp, err := http.DefaultClient.Do(r) + + // then + require.NoError(t, err) + require.NotNil(t, resp) + assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) + }) + t.Run("will return 401 if project in application does not match with header", func(t *testing.T) { + // given + t.Parallel() + f := setup() + allowApp := true + allowExtension := true + extName := "some-extension" + differentProject := "differentProject" + withRbac(f, allowApp, allowExtension) + withExtensionConfig(getExtensionConfig(extName, "http://fake"), f) + withMetrics(f) + ts := startTestServer(t, f) + defer ts.Close() + r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/%s/", ts.URL, extName)) + f.appGetterMock.On("Get", mock.Anything, mock.Anything).Return(getApp("", "", differentProject), nil) + + // when + resp, err := http.DefaultClient.Do(r) + + // then + require.NoError(t, err) + require.NotNil(t, resp) + assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) + }) + t.Run("will return 400 if application defines name and server destination", func(t *testing.T) { + // This test is to validate a security risk with malicious application + // trying to gain access to execute extensions in clusters it doesn't + // have access. + + // given + t.Parallel() + f := setup() + extName := "some-extension" + maliciousName := "srv1" + destinationServer := "some-valid-server" + + f.appGetterMock.On("Get", "ns1", "app1").Return(getApp(maliciousName, destinationServer, defaultProjectName), nil) + + withRbac(f, true, true) + withExtensionConfig(getExtensionConfigWith2Backends(extName, "url1", "clusterName", "url2", "clusterURL"), f) + withProject(getProjectWithDestinations("project-name", nil, []string{"srv1", destinationServer}), f) + withMetrics(f) + + ts := startTestServer(t, f) + defer ts.Close() + + url := fmt.Sprintf("%s/extensions/%s/", ts.URL, extName) + req := newExtensionRequest(t, http.MethodGet, url) + req.Header.Del(extension.HeaderArgoCDApplicationName) + req1 := req.Clone(context.Background()) + req1.Header.Add(extension.HeaderArgoCDApplicationName, "ns1:app1") + + // when + resp1, err := http.DefaultClient.Do(req1) + require.NoError(t, err) + + // then + require.NotNil(t, resp1) + assert.Equal(t, http.StatusBadRequest, resp1.StatusCode) + body, err := io.ReadAll(resp1.Body) + require.NoError(t, err) + actual := strings.TrimSuffix(string(body), "\n") + assert.Equal(t, "invalid extension", actual) + }) + t.Run("will return 400 if no extension name is provided", func(t *testing.T) { + // given + t.Parallel() + f := setup() + allowApp := true + allowExtension := true + extName := "some-extension" + differentProject := "differentProject" + withRbac(f, allowApp, allowExtension) + withExtensionConfig(getExtensionConfig(extName, "http://fake"), f) + withMetrics(f) + ts := startTestServer(t, f) + defer ts.Close() + r := newExtensionRequest(t, "Get", fmt.Sprintf("%s/extensions/", ts.URL)) + f.appGetterMock.On("Get", mock.Anything, mock.Anything).Return(getApp("", "", differentProject), nil) + + // when + resp, err := http.DefaultClient.Do(r) + + // then + require.NoError(t, err) + require.NotNil(t, resp) + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) } @@ -281,22 +722,36 @@ extensions: backend: services: - url: %s + headers: + - name: Authorization + value: '$extension.auth.header' ` return fmt.Sprintf(cfg, name, url) } -func getExtensionConfigWith2Backends(name, url1, clus1, url2, clus2 string) string { +func getExtensionConfigWith2Backends(name, url1, clusName, url2, clusURL string) string { cfg := ` extensions: - name: %s backend: services: - url: %s - cluster: %s + headers: + - name: Authorization + value: '$extension.auth.header' + cluster: + name: %s - url: %s - cluster: %s + headers: + - name: Authorization + value: '$extension.auth.header2' + cluster: + server: %s ` - return fmt.Sprintf(cfg, name, url1, clus1, url2, clus2) + // second extension is configured with the cluster url rather + // than the cluster name so we can validate that both use-cases + // are working + return fmt.Sprintf(cfg, name, url1, clusName, url2, clusURL) } func getExtensionConfigString() string { @@ -304,8 +759,15 @@ func getExtensionConfigString() string { extensions: - name: external-backend backend: + connectionTimeout: 10s + keepAlive: 11s + idleConnectionTimeout: 12s + maxIdleConnections: 30 services: - url: https://httpbin.org + headers: + - name: some-header + value: '$some.secret.ref' - name: some-backend backend: services: @@ -313,6 +775,13 @@ extensions: ` } +func getExtensionConfigNoService() string { + return ` +extensions: +- backend: + connectionTimeout: 2s +` +} func getExtensionConfigNoName() string { return ` extensions: @@ -340,3 +809,27 @@ extensions: - cluster: some-cluster ` } + +func getExtensionConfigNoHeaderName() string { + return ` +extensions: +- name: some-extension + backend: + services: + - url: https://httpbin.org + headers: + - value: '$some.secret.key' +` +} + +func getExtensionConfigNoHeaderValue() string { + return ` +extensions: +- name: some-extension + backend: + services: + - url: https://httpbin.org + headers: + - name: some-header-name +` +} diff --git a/server/extension/mocks/ExtensionMetricsRegistry.go b/server/extension/mocks/ExtensionMetricsRegistry.go new file mode 100644 index 0000000000000..78e583929f74d --- /dev/null +++ b/server/extension/mocks/ExtensionMetricsRegistry.go @@ -0,0 +1,38 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package mocks + +import ( + time "time" + + mock "github.com/stretchr/testify/mock" +) + +// ExtensionMetricsRegistry is an autogenerated mock type for the ExtensionMetricsRegistry type +type ExtensionMetricsRegistry struct { + mock.Mock +} + +// IncExtensionRequestCounter provides a mock function with given fields: _a0, status +func (_m *ExtensionMetricsRegistry) IncExtensionRequestCounter(_a0 string, status int) { + _m.Called(_a0, status) +} + +// ObserveExtensionRequestDuration provides a mock function with given fields: _a0, duration +func (_m *ExtensionMetricsRegistry) ObserveExtensionRequestDuration(_a0 string, duration time.Duration) { + _m.Called(_a0, duration) +} + +// NewExtensionMetricsRegistry creates a new instance of ExtensionMetricsRegistry. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewExtensionMetricsRegistry(t interface { + mock.TestingT + Cleanup(func()) +}) *ExtensionMetricsRegistry { + mock := &ExtensionMetricsRegistry{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/server/extension/mocks/ProjectGetter.go b/server/extension/mocks/ProjectGetter.go new file mode 100644 index 0000000000000..d70b0c70ccfc6 --- /dev/null +++ b/server/extension/mocks/ProjectGetter.go @@ -0,0 +1,74 @@ +// Code generated by mockery v2.15.0. DO NOT EDIT. + +package mocks + +import ( + v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + mock "github.com/stretchr/testify/mock" +) + +// ProjectGetter is an autogenerated mock type for the ProjectGetter type +type ProjectGetter struct { + mock.Mock +} + +// Get provides a mock function with given fields: name +func (_m *ProjectGetter) Get(name string) (*v1alpha1.AppProject, error) { + ret := _m.Called(name) + + var r0 *v1alpha1.AppProject + if rf, ok := ret.Get(0).(func(string) *v1alpha1.AppProject); ok { + r0 = rf(name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1alpha1.AppProject) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(name) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetClusters provides a mock function with given fields: project +func (_m *ProjectGetter) GetClusters(project string) ([]*v1alpha1.Cluster, error) { + ret := _m.Called(project) + + var r0 []*v1alpha1.Cluster + if rf, ok := ret.Get(0).(func(string) []*v1alpha1.Cluster); ok { + r0 = rf(project) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*v1alpha1.Cluster) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(project) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewProjectGetter interface { + mock.TestingT + Cleanup(func()) +} + +// NewProjectGetter creates a new instance of ProjectGetter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewProjectGetter(t mockConstructorTestingTNewProjectGetter) *ProjectGetter { + mock := &ProjectGetter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/server/extension/mocks/RbacEnforcer.go b/server/extension/mocks/RbacEnforcer.go new file mode 100644 index 0000000000000..01fb0c7421c69 --- /dev/null +++ b/server/extension/mocks/RbacEnforcer.go @@ -0,0 +1,41 @@ +// Code generated by mockery v2.15.0. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// RbacEnforcer is an autogenerated mock type for the RbacEnforcer type +type RbacEnforcer struct { + mock.Mock +} + +// EnforceErr provides a mock function with given fields: rvals +func (_m *RbacEnforcer) EnforceErr(rvals ...interface{}) error { + var _ca []interface{} + _ca = append(_ca, rvals...) + ret := _m.Called(_ca...) + + var r0 error + if rf, ok := ret.Get(0).(func(...interface{}) error); ok { + r0 = rf(rvals...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewRbacEnforcer interface { + mock.TestingT + Cleanup(func()) +} + +// NewRbacEnforcer creates a new instance of RbacEnforcer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewRbacEnforcer(t mockConstructorTestingTNewRbacEnforcer) *RbacEnforcer { + mock := &RbacEnforcer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/server/logout/logout.go b/server/logout/logout.go index e2bfa81f28bfb..e49f815931596 100644 --- a/server/logout/logout.go +++ b/server/logout/logout.go @@ -19,7 +19,7 @@ import ( "github.com/argoproj/argo-cd/v2/util/settings" ) -//NewHandler creates handler serving to do api/logout endpoint +// NewHandler creates handler serving to do api/logout endpoint func NewHandler(appClientset versioned.Interface, settingsMrg *settings.SettingsManager, sessionMgr *session.SessionManager, rootPath, baseHRef, namespace string) *Handler { return &Handler{ appClientset: appClientset, diff --git a/server/logout/logout_test.go b/server/logout/logout_test.go index 692e741aa3c2f..e20d35837f475 100644 --- a/server/logout/logout_test.go +++ b/server/logout/logout_test.go @@ -251,17 +251,17 @@ func TestHandlerConstructLogoutURL(t *testing.T) { invalidHeader := make(map[string][]string) invalidHeader["Cookie"] = []string{"argocd.token=" + invalidToken} - oidcRequest, err := http.NewRequest("GET", "http://localhost:4000/api/logout", nil) + oidcRequest, err := http.NewRequest(http.MethodGet, "http://localhost:4000/api/logout", nil) assert.NoError(t, err) oidcRequest.Header = oidcTokenHeader - nonoidcRequest, err := http.NewRequest("GET", "http://localhost:4000/api/logout", nil) + nonoidcRequest, err := http.NewRequest(http.MethodGet, "http://localhost:4000/api/logout", nil) assert.NoError(t, err) nonoidcRequest.Header = nonOidcTokenHeader assert.NoError(t, err) - requestWithInvalidToken, err := http.NewRequest("GET", "http://localhost:4000/api/logout", nil) + requestWithInvalidToken, err := http.NewRequest(http.MethodGet, "http://localhost:4000/api/logout", nil) assert.NoError(t, err) requestWithInvalidToken.Header = invalidHeader - invalidRequest, err := http.NewRequest("GET", "http://localhost:4000/api/logout", nil) + invalidRequest, err := http.NewRequest(http.MethodGet, "http://localhost:4000/api/logout", nil) assert.NoError(t, err) tests := []struct { diff --git a/server/metrics/metrics.go b/server/metrics/metrics.go index 40698e742b093..4afac9da26c02 100644 --- a/server/metrics/metrics.go +++ b/server/metrics/metrics.go @@ -14,8 +14,10 @@ import ( type MetricsServer struct { *http.Server - redisRequestCounter *prometheus.CounterVec - redisRequestHistogram *prometheus.HistogramVec + redisRequestCounter *prometheus.CounterVec + redisRequestHistogram *prometheus.HistogramVec + extensionRequestCounter *prometheus.CounterVec + extensionRequestDuration *prometheus.HistogramVec } var ( @@ -34,6 +36,21 @@ var ( }, []string{"initiator"}, ) + extensionRequestCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "argocd_proxy_extension_request_total", + Help: "Number of requests sent to configured proxy extensions.", + }, + []string{"extension", "status"}, + ) + extensionRequestDuration = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "argocd_proxy_extension_request_duration_seconds", + Help: "Request duration in seconds between the Argo CD API server and the extension backend.", + Buckets: []float64{0.1, 0.25, .5, 1, 2, 5, 10}, + }, + []string{"extension"}, + ) ) // NewMetricsServer returns a new prometheus server which collects api server metrics @@ -48,14 +65,18 @@ func NewMetricsServer(host string, port int) *MetricsServer { registry.MustRegister(redisRequestCounter) registry.MustRegister(redisRequestHistogram) + registry.MustRegister(extensionRequestCounter) + registry.MustRegister(extensionRequestDuration) return &MetricsServer{ Server: &http.Server{ Addr: fmt.Sprintf("%s:%d", host, port), Handler: mux, }, - redisRequestCounter: redisRequestCounter, - redisRequestHistogram: redisRequestHistogram, + redisRequestCounter: redisRequestCounter, + redisRequestHistogram: redisRequestHistogram, + extensionRequestCounter: extensionRequestCounter, + extensionRequestDuration: extensionRequestDuration, } } @@ -67,3 +88,11 @@ func (m *MetricsServer) IncRedisRequest(failed bool) { func (m *MetricsServer) ObserveRedisRequestDuration(duration time.Duration) { m.redisRequestHistogram.WithLabelValues("argocd-server").Observe(duration.Seconds()) } + +func (m *MetricsServer) IncExtensionRequestCounter(extension string, status int) { + m.extensionRequestCounter.WithLabelValues(extension, strconv.Itoa(status)).Inc() +} + +func (m *MetricsServer) ObserveExtensionRequestDuration(extension string, duration time.Duration) { + m.extensionRequestDuration.WithLabelValues(extension).Observe(duration.Seconds()) +} diff --git a/server/notification/notification_test.go b/server/notification/notification_test.go index 47606b24ea855..ee913926bc010 100644 --- a/server/notification/notification_test.go +++ b/server/notification/notification_test.go @@ -41,7 +41,7 @@ func TestNotificationServer(t *testing.T) { Name: "argocd-notifications-cm", }, Data: map[string]string{ - "service.webhook.test": "url: https://test.com", + "service.webhook.test": "url: https://test.example.com", "template.app-created": "email:\n subject: Application {{.app.metadata.name}} has been created.\nmessage: Application {{.app.metadata.name}} has been created.\nteams:\n title: Application {{.app.metadata.name}} has been created.\n", "trigger.on-created": "- description: Application is created.\n oncePer: app.metadata.name\n send:\n - app-created\n when: \"true\"\n", }, @@ -70,7 +70,7 @@ func TestNotificationServer(t *testing.T) { argocdService, err := service.NewArgoCDService(kubeclientset, testNamespace, mockRepoClient) require.NoError(t, err) defer argocdService.Close() - apiFactory := api.NewFactory(settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm"), testNamespace, secretInformer, configMapInformer) + apiFactory := api.NewFactory(settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm", false), testNamespace, secretInformer, configMapInformer) t.Run("TestListServices", func(t *testing.T) { server := NewServer(apiFactory) diff --git a/server/project/project.go b/server/project/project.go index cdb2214c1607f..44ddee95eaaff 100644 --- a/server/project/project.go +++ b/server/project/project.go @@ -172,6 +172,9 @@ func (s *Server) ListLinks(ctx context.Context, q *project.ListProjectLinksReque return nil, err } + // sanitize project jwt tokens + proj.Status = v1alpha1.AppProjectStatus{} + obj, err := kube.ToUnstructured(proj) if err != nil { return nil, fmt.Errorf("error getting application: %w", err) @@ -182,7 +185,8 @@ func (s *Server) ListLinks(ctx context.Context, q *project.ListProjectLinksReque return nil, fmt.Errorf("failed to read application deep links from configmap: %w", err) } - finalList, errorList := deeplinks.EvaluateDeepLinksResponse(*obj, deepLinks) + deeplinksObj := deeplinks.CreateDeepLinksObject(nil, nil, nil, obj) + finalList, errorList := deeplinks.EvaluateDeepLinksResponse(deeplinksObj, obj.GetName(), deepLinks) if len(errorList) > 0 { log.Errorf("errorList while evaluating project deep links, %v", strings.Join(errorList, ", ")) } @@ -503,7 +507,7 @@ func (s *Server) logEvent(a *v1alpha1.AppProject, ctx context.Context, reason st user = "Unknown user" } message := fmt.Sprintf("%s %s", user, action) - s.auditLogger.LogAppProjEvent(a, eventInfo, message) + s.auditLogger.LogAppProjEvent(a, eventInfo, message, user) } func (s *Server) GetSyncWindowsState(ctx context.Context, q *project.SyncWindowsQuery) (*project.SyncWindowsResponse, error) { diff --git a/server/rbacpolicy/rbacpolicy.go b/server/rbacpolicy/rbacpolicy.go index 1ec420b642fdb..940f5bfe70844 100644 --- a/server/rbacpolicy/rbacpolicy.go +++ b/server/rbacpolicy/rbacpolicy.go @@ -3,7 +3,7 @@ package rbacpolicy import ( "strings" - jwt "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v4" log "github.com/sirupsen/logrus" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -24,6 +24,7 @@ const ( ResourceGPGKeys = "gpgkeys" ResourceLogs = "logs" ResourceExec = "exec" + ResourceExtensions = "extensions" // please add new items to Actions ActionGet = "get" @@ -33,6 +34,7 @@ const ( ActionSync = "sync" ActionOverride = "override" ActionAction = "action" + ActionInvoke = "invoke" ) var ( @@ -152,7 +154,7 @@ func (p *RBACPolicyEnforcer) EnforceClaims(claims jwt.Claims, rvals ...interface } } } - logCtx := log.WithField("claims", claims).WithField("rval", rvals) + logCtx := log.WithFields(log.Fields{"claims": claims, "rval": rvals, "subject": subject, "groups": groups, "project": projName, "scopes": scopes}) logCtx.Debug("enforce failed") return false } diff --git a/server/repository/repository.go b/server/repository/repository.go index f623c11b2c023..417a41ee306ef 100644 --- a/server/repository/repository.go +++ b/server/repository/repository.go @@ -1,11 +1,10 @@ package repository import ( + "context" "fmt" "reflect" - "context" - "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/argoproj/gitops-engine/pkg/utils/text" log "github.com/sirupsen/logrus" @@ -164,6 +163,7 @@ func (s *Server) Get(ctx context.Context, q *repositorypkg.RepoQuery) (*appsv1.R GitHubAppEnterpriseBaseURL: repo.GitHubAppEnterpriseBaseURL, Proxy: repo.Proxy, Project: repo.Project, + InheritedCreds: repo.InheritedCreds, } item.ConnectionState = s.getConnectionState(ctx, item.Repo, q.ForceRefresh) @@ -187,15 +187,17 @@ func (s *Server) ListRepositories(ctx context.Context, q *repositorypkg.RepoQuer } // remove secrets items = append(items, &appsv1.Repository{ - Repo: repo.Repo, - Type: rType, - Name: repo.Name, - Username: repo.Username, - Insecure: repo.IsInsecure(), - EnableLFS: repo.EnableLFS, - EnableOCI: repo.EnableOCI, - Proxy: repo.Proxy, - Project: repo.Project, + Repo: repo.Repo, + Type: rType, + Name: repo.Name, + Username: repo.Username, + Insecure: repo.IsInsecure(), + EnableLFS: repo.EnableLFS, + EnableOCI: repo.EnableOCI, + Proxy: repo.Proxy, + Project: repo.Project, + ForceHttpBasicAuth: repo.ForceHttpBasicAuth, + InheritedCreds: repo.InheritedCreds, }) } } @@ -292,7 +294,7 @@ func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDeta if err := s.enf.EnforceErr(claims, rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil { return nil, err } - appName, appNs := argo.ParseAppQualifiedName(q.AppName, s.settings.GetNamespace()) + appName, appNs := argo.ParseFromQualifiedName(q.AppName, s.settings.GetNamespace()) app, err := s.appLister.Applications(appNs).Get(appName) appRBACObj := createRBACObject(q.AppProject, q.AppName) // ensure caller has read privileges to app @@ -558,16 +560,23 @@ func (s *Server) isRepoPermittedInProject(ctx context.Context, repo string, proj // isSourceInHistory checks if the supplied application source is either our current application // source, or was something which we synced to previously. func isSourceInHistory(app *v1alpha1.Application, source v1alpha1.ApplicationSource) bool { - if source.Equals(app.Spec.GetSource()) { + appSource := app.Spec.GetSource() + if source.Equals(&appSource) { return true } + appSources := app.Spec.GetSources() + for _, s := range appSources { + if source.Equals(&s) { + return true + } + } // Iterate history. When comparing items in our history, use the actual synced revision to // compare with the supplied source.targetRevision in the request. This is because // history[].source.targetRevision is ambiguous (e.g. HEAD), whereas // history[].revision will contain the explicit SHA for _, h := range app.Status.History { h.Source.TargetRevision = h.Revision - if source.Equals(h.Source) { + if source.Equals(&h.Source) { return true } } diff --git a/server/repository/repository.proto b/server/repository/repository.proto index f941932cf2dae..6466967702e85 100644 --- a/server/repository/repository.proto +++ b/server/repository/repository.proto @@ -81,6 +81,8 @@ message RepoAccessQuery { string project = 17; // Google Cloud Platform service account key string gcpServiceAccountKey = 18; + // Whether to force HTTP basic auth + bool forceHttpBasicAuth = 19; } message RepoResponse {} diff --git a/server/repository/repository_test.go b/server/repository/repository_test.go index cdd7ba89c30b7..55bf7ab7220ac 100644 --- a/server/repository/repository_test.go +++ b/server/repository/repository_test.go @@ -34,6 +34,8 @@ import ( dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" "github.com/argoproj/argo-cd/v2/util/rbac" "github.com/argoproj/argo-cd/v2/util/settings" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) const testNamespace = "default" @@ -60,7 +62,7 @@ var ( } defaultProj = &appsv1.AppProject{ TypeMeta: metav1.TypeMeta{ - Kind: "AppProject", + Kind: application.AppProjectKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -75,7 +77,7 @@ var ( defaultProjNoSources = &appsv1.AppProject{ TypeMeta: metav1.TypeMeta{ - Kind: "AppProject", + Kind: application.AppProjectKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -87,10 +89,21 @@ var ( Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}}, }, } - + fakeRepo = appsv1.Repository{ + Repo: "https://test", + Type: "test", + Name: "test", + Username: "argo", + Insecure: false, + EnableLFS: false, + EnableOCI: false, + Proxy: "test", + Project: "argocd", + InheritedCreds: true, + } guestbookApp = &appsv1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ @@ -122,6 +135,96 @@ var ( }, }, } + multiSourceApp001AppName = "msa-two-helm-types" + multiSourceApp001 = &appsv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: multiSourceApp001AppName, + Namespace: testNamespace, + }, + Spec: appsv1.ApplicationSpec{ + Project: "default", + Sources: []appsv1.ApplicationSource{ + { + RepoURL: "https://helm.elastic.co", + TargetRevision: "7.7.0", + Chart: "elasticsearch", + Helm: &appsv1.ApplicationSourceHelm{ + ValueFiles: []string{"values.yaml"}, + }, + }, + { + RepoURL: "https://helm.elastic.co", + TargetRevision: "7.6.0", + Chart: "elasticsearch", + Helm: &appsv1.ApplicationSourceHelm{ + ValueFiles: []string{"values.yaml"}, + }, + }, + }, + }, + Status: appsv1.ApplicationStatus{ + History: appsv1.RevisionHistories{ + { + Revision: "HEAD", + Sources: []appsv1.ApplicationSource{ + { + RepoURL: "https://helm.elastic.co", + TargetRevision: "7.6.0", + Helm: &appsv1.ApplicationSourceHelm{ + ValueFiles: []string{"values-old.yaml"}, + }, + }, + }, + }, + }, + }, + } + multiSourceApp002AppName = "msa-one-plugin-one-helm" + multiSourceApp002 = &appsv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: multiSourceApp002AppName, + Namespace: testNamespace, + }, + Spec: appsv1.ApplicationSpec{ + Project: "default", + Sources: []appsv1.ApplicationSource{ + { + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + Path: "sock-shop", + TargetRevision: "HEAD", + }, + { + RepoURL: "https://helm.elastic.co", + TargetRevision: "7.7.0", + Chart: "elasticsearch", + Helm: &appsv1.ApplicationSourceHelm{ + ValueFiles: []string{"values.yaml"}, + }, + }, + }, + }, + Status: appsv1.ApplicationStatus{ + History: appsv1.RevisionHistories{ + { + Revision: "HEAD", + Sources: []appsv1.ApplicationSource{ + { + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "1.0.0", + }, + }, + }, + }, + }, + } ) func newAppAndProjLister(objects ...runtime.Object) (applisters.ApplicationLister, k8scache.SharedIndexInformer) { @@ -196,6 +299,33 @@ func TestRepositoryServer(t *testing.T) { assert.Equal(t, repo.Repo, url) }) + t.Run("Test_GetInherited", func(t *testing.T) { + repoServerClient := mocks.RepoServerServiceClient{} + repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) + repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} + + url := "https://test" + db := &dbmocks.ArgoDB{} + testRepo := &appsv1.Repository{ + Repo: url, + Type: "git", + Username: "foo", + InheritedCreds: true, + } + db.On("GetRepository", context.TODO(), url).Return(testRepo, nil) + db.On("RepositoryExists", context.TODO(), url).Return(true, nil) + + s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr) + repo, err := s.Get(context.TODO(), &repository.RepoQuery{ + Repo: url, + }) + assert.Nil(t, err) + + testRepo.ConnectionState = repo.ConnectionState // overwrite connection state on our test object to simplify comparison below + + assert.Equal(t, testRepo, repo) + }) + t.Run("Test_GetWithErrorShouldReturn403", func(t *testing.T) { repoServerClient := mocks.RepoServerServiceClient{} repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} @@ -279,6 +409,23 @@ func TestRepositoryServer(t *testing.T) { assert.Equal(t, repo.Repo, "test") }) + t.Run("Test_ListRepositories", func(t *testing.T) { + repoServerClient := mocks.RepoServerServiceClient{} + repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) + repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} + enforcer := newEnforcer(kubeclientset) + + url := "https://test" + db := &dbmocks.ArgoDB{} + db.On("GetRepository", context.TODO(), url).Return(nil, nil) + db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil) + db.On("ListRepositories", context.TODO()).Return([]*appsv1.Repository{&fakeRepo, &fakeRepo}, nil) + + s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr) + resp, err := s.ListRepositories(context.TODO(), &repository.RepoQuery{}) + assert.NoError(t, err) + assert.Equal(t, 2, len(resp.Items)) + }) } func TestRepositoryServerListApps(t *testing.T) { @@ -507,13 +654,92 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ - Source: guestbookApp.Spec.GetSourcePtr(), + Source: guestbookApp.Spec.GetSourcePtr(0), AppName: "guestbook", AppProject: "default", }) assert.NoError(t, err) assert.Equal(t, expectedResp, *resp) }) + t.Run("Test_ExistingMultiSourceApp001", func(t *testing.T) { + repoServerClient := mocks.RepoServerServiceClient{} + repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} + enforcer := newEnforcer(kubeclientset) + + url := "https://helm.elastic.co" + helmRepos := []*appsv1.Repository{{Repo: url}, {Repo: url}} + db := &dbmocks.ArgoDB{} + db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(helmRepos, nil) + db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) + db.On("GetProjectRepositories", context.TODO(), "default").Return(nil, nil) + db.On("GetProjectClusters", context.TODO(), "default").Return(nil, nil) + expectedResp := apiclient.RepoAppDetailsResponse{Type: "Helm"} + repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil) + appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp001) + + s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) + sources := multiSourceApp001.Spec.GetSources() + assert.Equal(t, 2, len(sources)) + resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ + Source: &sources[0], + AppName: multiSourceApp001AppName, + AppProject: "default", + }) + assert.NoError(t, err) + assert.Equal(t, expectedResp, *resp) + assert.Equal(t, "Helm", resp.Type) + // Next source + resp, err = s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ + Source: &sources[1], + AppName: multiSourceApp001AppName, + AppProject: "default", + }) + assert.NoError(t, err) + assert.Equal(t, expectedResp, *resp) + assert.Equal(t, "Helm", resp.Type) + }) + t.Run("Test_ExistingMultiSourceApp002", func(t *testing.T) { + repoServerClient := mocks.RepoServerServiceClient{} + repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} + enforcer := newEnforcer(kubeclientset) + + url0 := "https://github.com/argoproj/argocd-example-apps.git" + url1 := "https://helm.elastic.co" + helmRepos := []*appsv1.Repository{{Repo: url0}, {Repo: url1}} + db := &dbmocks.ArgoDB{} + db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(helmRepos, nil) + db.On("GetRepository", context.TODO(), url0).Return(&appsv1.Repository{Repo: url0}, nil) + db.On("GetRepository", context.TODO(), url1).Return(&appsv1.Repository{Repo: url1}, nil) + db.On("GetProjectRepositories", context.TODO(), "default").Return(nil, nil) + db.On("GetProjectClusters", context.TODO(), "default").Return(nil, nil) + expectedResp0 := apiclient.RepoAppDetailsResponse{Type: "Plugin"} + expectedResp1 := apiclient.RepoAppDetailsResponse{Type: "Helm"} + repoServerClient.On("GetAppDetails", context.TODO(), mock.MatchedBy(func(req *apiclient.RepoServerAppDetailsQuery) bool { return req.Source.RepoURL == url0 })).Return(&expectedResp0, nil) + repoServerClient.On("GetAppDetails", context.TODO(), mock.MatchedBy(func(req *apiclient.RepoServerAppDetailsQuery) bool { return req.Source.RepoURL == url1 })).Return(&expectedResp1, nil) + appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp002) + + s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) + sources := multiSourceApp002.Spec.GetSources() + assert.Equal(t, 2, len(sources)) + + resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ + Source: &sources[0], + AppName: multiSourceApp002AppName, + AppProject: "default", + }) + assert.NoError(t, err) + assert.Equal(t, "Plugin", resp.Type) + assert.Equal(t, expectedResp0, *resp) + // Next source + resp, err = s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ + Source: &sources[1], + AppName: multiSourceApp002AppName, + AppProject: "default", + }) + assert.NoError(t, err) + assert.Equal(t, expectedResp1, *resp) + assert.Equal(t, "Helm", resp.Type) + }) t.Run("Test_ExistingAppMismatchedProjectName", func(t *testing.T) { repoServerClient := mocks.RepoServerServiceClient{} repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} @@ -526,7 +752,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ - Source: guestbookApp.Spec.GetSourcePtr(), + Source: guestbookApp.Spec.GetSourcePtr(0), AppName: "guestbook", AppProject: "mismatch", }) diff --git a/server/server.go b/server/server.go index f1302a0f12f45..625fa2053023e 100644 --- a/server/server.go +++ b/server/server.go @@ -2,7 +2,6 @@ package server import ( "context" - netCtx "context" "crypto/tls" "errors" "fmt" @@ -25,19 +24,20 @@ import ( // nolint:staticcheck golang_proto "github.com/golang/protobuf/proto" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" "github.com/argoproj/notifications-engine/pkg/api" "github.com/argoproj/pkg/sync" - "github.com/go-redis/redis/v8" "github.com/golang-jwt/jwt/v4" "github.com/gorilla/handlers" - gmux "github.com/gorilla/mux" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/improbable-eng/grpc-web/go/grpcweb" + "github.com/redis/go-redis/v9" log "github.com/sirupsen/logrus" "github.com/soheilhy/cmux" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" @@ -61,7 +61,6 @@ import ( "github.com/argoproj/argo-cd/v2/pkg/apiclient" accountpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/account" applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" - applicationsetpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/applicationset" certificatepkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/certificate" clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster" @@ -102,7 +101,6 @@ import ( "github.com/argoproj/argo-cd/v2/util/assets" cacheutil "github.com/argoproj/argo-cd/v2/util/cache" "github.com/argoproj/argo-cd/v2/util/db" - "github.com/argoproj/argo-cd/v2/util/dex" dexutil "github.com/argoproj/argo-cd/v2/util/dex" "github.com/argoproj/argo-cd/v2/util/env" errorsutil "github.com/argoproj/argo-cd/v2/util/errors" @@ -161,7 +159,7 @@ func init() { if replicasCount > 0 { maxConcurrentLoginRequestsCount = maxConcurrentLoginRequestsCount / replicasCount } - enableGRPCTimeHistogram = os.Getenv(common.EnvEnableGRPCTimeHistogramEnv) == "true" + enableGRPCTimeHistogram = env.ParseBoolFromEnv(common.EnvEnableGRPCTimeHistogramEnv, false) } // ArgoCDServer is the API server for Argo CD @@ -180,7 +178,7 @@ type ArgoCDServer struct { appInformer cache.SharedIndexInformer appLister applisters.ApplicationLister appsetInformer cache.SharedIndexInformer - appsetLister applisters.ApplicationSetNamespaceLister + appsetLister applisters.ApplicationSetLister db db.ArgoDB // stopCh is the channel which when closed, will shutdown the Argo CD server @@ -194,33 +192,49 @@ type ArgoCDServer struct { secretInformer cache.SharedIndexInformer configMapInformer cache.SharedIndexInformer serviceSet *ArgoCDServiceSet + extensionManager *extension.Manager } type ArgoCDServerOpts struct { DisableAuth bool + ContentTypes []string EnableGZip bool Insecure bool StaticAssetsDir string ListenPort int + ListenHost string MetricsPort int + MetricsHost string Namespace string DexServerAddr string - DexTLSConfig *dex.DexTLSConfig + DexTLSConfig *dexutil.DexTLSConfig BaseHRef string RootPath string KubeClientset kubernetes.Interface AppClientset appclientset.Interface RepoClientset repoapiclient.Clientset Cache *servercache.Cache + RepoServerCache *repocache.Cache RedisClient *redis.Client TLSConfigCustomizer tlsutil.ConfigCustomizer XFrameOptions string ContentSecurityPolicy string - ListenHost string ApplicationNamespaces []string EnableProxyExtension bool } +// HTTPMetricsRegistry exposes operations to update http metrics in the Argo CD +// API server. +type HTTPMetricsRegistry interface { + // IncExtensionRequestCounter will increase the request counter for the given + // extension with the given status. + IncExtensionRequestCounter(extension string, status int) + // ObserveExtensionRequestDuration will register the request roundtrip duration + // between Argo CD API Server and the extension backend service for the given + // extension. + ObserveExtensionRequestDuration(extension string, duration time.Duration) +} + // initializeDefaultProject creates the default project if it does not already exist func initializeDefaultProject(opts ArgoCDServerOpts) error { defaultProj := &v1alpha1.AppProject{ @@ -264,7 +278,7 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer { appLister := appFactory.Argoproj().V1alpha1().Applications().Lister() appsetInformer := appFactory.Argoproj().V1alpha1().ApplicationSets().Informer() - appsetLister := appFactory.Argoproj().V1alpha1().ApplicationSets().Lister().ApplicationSets(opts.Namespace) + appsetLister := appFactory.Argoproj().V1alpha1().ApplicationSets().Lister() userStateStorage := util_session.NewUserStateStorage(opts.RedisClient) sessionMgr := util_session.NewSessionManager(settingsMgr, projLister, opts.DexServerAddr, opts.DexTLSConfig, userStateStorage) @@ -288,11 +302,19 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer { secretInformer := k8s.NewSecretInformer(opts.KubeClientset, opts.Namespace, "argocd-notifications-secret") configMapInformer := k8s.NewConfigMapInformer(opts.KubeClientset, opts.Namespace, "argocd-notifications-cm") - apiFactory := api.NewFactory(settings_notif.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm"), opts.Namespace, secretInformer, configMapInformer) + apiFactory := api.NewFactory(settings_notif.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm", false), opts.Namespace, secretInformer, configMapInformer) - return &ArgoCDServer{ + dbInstance := db.NewDB(opts.Namespace, settingsMgr, opts.KubeClientset) + logger := log.NewEntry(log.StandardLogger()) + + sg := extension.NewDefaultSettingsGetter(settingsMgr) + ag := extension.NewDefaultApplicationGetter(appLister) + pg := extension.NewDefaultProjectGetter(projLister, dbInstance) + em := extension.NewManager(logger, sg, ag, pg, enf) + + a := &ArgoCDServer{ ArgoCDServerOpts: opts, - log: log.NewEntry(log.StandardLogger()), + log: logger, settings: settings, sessionMgr: sessionMgr, settingsMgr: settingsMgr, @@ -306,11 +328,20 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer { policyEnforcer: policyEnf, userStateStorage: userStateStorage, staticAssets: http.FS(staticFS), - db: db.NewDB(opts.Namespace, settingsMgr, opts.KubeClientset), + db: dbInstance, apiFactory: apiFactory, secretInformer: secretInformer, configMapInformer: configMapInformer, + extensionManager: em, + } + + err = a.logInClusterWarnings() + if err != nil { + // Just log. It's not critical. + log.Warnf("Failed to log in-cluster warnings: %v", err) } + + return a } const ( @@ -357,6 +388,47 @@ func (l *Listeners) Close() error { return nil } +// logInClusterWarnings checks the in-cluster configuration and prints out any warnings. +func (a *ArgoCDServer) logInClusterWarnings() error { + labelSelector := labels.NewSelector() + req, err := labels.NewRequirement(common.LabelKeySecretType, selection.Equals, []string{common.LabelValueSecretTypeCluster}) + if err != nil { + return fmt.Errorf("failed to construct cluster-type label selector: %w", err) + } + labelSelector = labelSelector.Add(*req) + secretsLister, err := a.settingsMgr.GetSecretsLister() + if err != nil { + return fmt.Errorf("failed to get secrets lister: %w", err) + } + clusterSecrets, err := secretsLister.Secrets(a.ArgoCDServerOpts.Namespace).List(labelSelector) + if err != nil { + return fmt.Errorf("failed to list cluster secrets: %w", err) + } + var inClusterSecrets []string + for _, clusterSecret := range clusterSecrets { + cluster, err := db.SecretToCluster(clusterSecret) + if err != nil { + return fmt.Errorf("could not unmarshal cluster secret %q: %w", clusterSecret.Name, err) + } + if cluster.Server == v1alpha1.KubernetesInternalAPIServerAddr { + inClusterSecrets = append(inClusterSecrets, clusterSecret.Name) + } + } + if len(inClusterSecrets) > 0 { + // Don't make this call unless we actually have in-cluster secrets, to save time. + dbSettings, err := a.settingsMgr.GetSettings() + if err != nil { + return fmt.Errorf("could not get DB settings: %w", err) + } + if !dbSettings.InClusterEnabled { + for _, clusterName := range inClusterSecrets { + log.Warnf("cluster %q uses in-cluster server address but it's disabled in Argo CD settings", clusterName) + } + } + } + return nil +} + func startListener(host string, port int) (net.Listener, error) { var conn net.Listener var realErr error @@ -383,8 +455,8 @@ func (a *ArgoCDServer) Listen() (*Listeners, error) { var dOpts []grpc.DialOption dOpts = append(dOpts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(apiclient.MaxGRPCMessageSize))) dOpts = append(dOpts, grpc.WithUserAgent(fmt.Sprintf("%s/%s", common.ArgoCDUserAgentName, common.GetVersion().Version))) - dOpts = append(dOpts, grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor())) - dOpts = append(dOpts, grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor())) + dOpts = append(dOpts, grpc.WithUnaryInterceptor(grpc_util.OTELUnaryClientInterceptor())) + dOpts = append(dOpts, grpc.WithStreamInterceptor(grpc_util.OTELStreamClientInterceptor())) if a.useTLS() { // The following sets up the dial Options for grpc-gateway to talk to gRPC server over TLS. // grpc-gateway is just translating HTTP/HTTPS requests as gRPC requests over localhost, @@ -413,6 +485,7 @@ func (a *ArgoCDServer) Listen() (*Listeners, error) { func (a *ArgoCDServer) Init(ctx context.Context) { go a.projInformer.Run(ctx.Done()) go a.appInformer.Run(ctx.Done()) + go a.appsetInformer.Run(ctx.Done()) go a.configMapInformer.Run(ctx.Done()) go a.secretInformer.Run(ctx.Done()) } @@ -423,6 +496,12 @@ func (a *ArgoCDServer) Init(ctx context.Context) { // golang/protobuf). func (a *ArgoCDServer) Run(ctx context.Context, listeners *Listeners) { a.userStateStorage.Init(ctx) + + metricsServ := metrics.NewMetricsServer(a.MetricsHost, a.MetricsPort) + if a.RedisClient != nil { + cacheutil.CollectMetrics(a.RedisClient, metricsServ) + } + svcSet := newArgoCDServiceSet(a) a.serviceSet = svcSet grpcS, appResourceTreeFn := a.newGRPCServer() @@ -431,9 +510,9 @@ func (a *ArgoCDServer) Run(ctx context.Context, listeners *Listeners) { var httpsS *http.Server if a.useTLS() { httpS = newRedirectServer(a.ListenPort, a.RootPath) - httpsS = a.newHTTPServer(ctx, a.ListenPort, grpcWebS, appResourceTreeFn, listeners.GatewayConn) + httpsS = a.newHTTPServer(ctx, a.ListenPort, grpcWebS, appResourceTreeFn, listeners.GatewayConn, metricsServ) } else { - httpS = a.newHTTPServer(ctx, a.ListenPort, grpcWebS, appResourceTreeFn, listeners.GatewayConn) + httpS = a.newHTTPServer(ctx, a.ListenPort, grpcWebS, appResourceTreeFn, listeners.GatewayConn, metricsServ) } if a.RootPath != "" { httpS.Handler = withRootPath(httpS.Handler, a) @@ -447,11 +526,6 @@ func (a *ArgoCDServer) Run(ctx context.Context, listeners *Listeners) { httpsS.Handler = &bug21955Workaround{handler: httpsS.Handler} } - metricsServ := metrics.NewMetricsServer(a.ListenHost, a.MetricsPort) - if a.RedisClient != nil { - cacheutil.CollectMetrics(a.RedisClient, metricsServ) - } - // CMux is used to support servicing gRPC and HTTP1.1+JSON on the same port tcpm := cmux.New(listeners.Main) var tlsm cmux.CMux @@ -459,17 +533,18 @@ func (a *ArgoCDServer) Run(ctx context.Context, listeners *Listeners) { var httpL net.Listener var httpsL net.Listener if !a.useTLS() { - httpL = tcpm.Match(cmux.HTTP1Fast()) + httpL = tcpm.Match(cmux.HTTP1Fast("PATCH")) grpcL = tcpm.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc")) } else { // We first match on HTTP 1.1 methods. - httpL = tcpm.Match(cmux.HTTP1Fast()) + httpL = tcpm.Match(cmux.HTTP1Fast("PATCH")) // If not matched, we assume that its TLS. tlsl := tcpm.Match(cmux.Any()) - tlsConfig := tls.Config{ - Certificates: []tls.Certificate{*a.settings.Certificate}, + tlsConfig := tls.Config{} + tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + return a.settings.Certificate, nil } if a.TLSConfigCustomizer != nil { a.TLSConfigCustomizer(&tlsConfig) @@ -478,7 +553,7 @@ func (a *ArgoCDServer) Run(ctx context.Context, listeners *Listeners) { // Now, we build another mux recursively to match HTTPS and gRPC. tlsm = cmux.New(tlsl) - httpsL = tlsm.Match(cmux.HTTP1Fast()) + httpsL = tlsm.Match(cmux.HTTP1Fast("PATCH")) grpcL = tlsm.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc")) } @@ -557,13 +632,14 @@ func (a *ArgoCDServer) watchSettings() { prevURL := a.settings.URL prevOIDCConfig := a.settings.OIDCConfig() - prevDexCfgBytes, err := dex.GenerateDexConfigYAML(a.settings, a.DexTLSConfig == nil || a.DexTLSConfig.DisableTLS) + prevDexCfgBytes, err := dexutil.GenerateDexConfigYAML(a.settings, a.DexTLSConfig == nil || a.DexTLSConfig.DisableTLS) errorsutil.CheckError(err) prevGitHubSecret := a.settings.WebhookGitHubSecret prevGitLabSecret := a.settings.WebhookGitLabSecret prevBitbucketUUID := a.settings.WebhookBitbucketUUID prevBitbucketServerSecret := a.settings.WebhookBitbucketServerSecret prevGogsSecret := a.settings.WebhookGogsSecret + prevExtConfig := a.settings.ExtensionConfig var prevCert, prevCertKey string if a.settings.Certificate != nil && !a.ArgoCDServerOpts.Insecure { prevCert, prevCertKey = tlsutil.EncodeX509KeyPairString(*a.settings.Certificate) @@ -572,7 +648,7 @@ func (a *ArgoCDServer) watchSettings() { for { newSettings := <-updateCh a.settings = newSettings - newDexCfgBytes, err := dex.GenerateDexConfigYAML(a.settings, a.DexTLSConfig == nil || a.DexTLSConfig.DisableTLS) + newDexCfgBytes, err := dexutil.GenerateDexConfigYAML(a.settings, a.DexTLSConfig == nil || a.DexTLSConfig.DisableTLS) errorsutil.CheckError(err) if string(newDexCfgBytes) != string(prevDexCfgBytes) { log.Infof("dex config modified. restarting") @@ -606,14 +682,24 @@ func (a *ArgoCDServer) watchSettings() { log.Infof("gogs secret modified. restarting") break } + if prevExtConfig != a.settings.ExtensionConfig { + prevExtConfig = a.settings.ExtensionConfig + log.Infof("extensions configs modified. Updating proxy registry...") + err := a.extensionManager.UpdateExtensionRegistry(a.settings) + if err != nil { + log.Errorf("error updating extensions configs: %s", err) + } else { + log.Info("extensions configs updated successfully") + } + } if !a.ArgoCDServerOpts.Insecure { var newCert, newCertKey string if a.settings.Certificate != nil { newCert, newCertKey = tlsutil.EncodeX509KeyPairString(*a.settings.Certificate) } if newCert != prevCert || newCertKey != prevCertKey { - log.Infof("tls certificate modified. restarting") - break + log.Infof("tls certificate modified. reloading certificate") + // No need to break out of this loop since TlsConfig.GetCertificate will automagically reload the cert. } } } @@ -661,7 +747,7 @@ func (a *ArgoCDServer) newGRPCServer() (*grpc.Server, application.AppResourceTre grpc.ConnectionTimeout(300 * time.Second), grpc.KeepaliveEnforcementPolicy( keepalive.EnforcementPolicy{ - MinTime: common.GRPCKeepAliveEnforcementMinimum, + MinTime: common.GetGRPCKeepAliveEnforcementMinimum(), }, ), } @@ -679,6 +765,8 @@ func (a *ArgoCDServer) newGRPCServer() (*grpc.Server, application.AppResourceTre "/repocreds.RepoCredsService/CreateRepositoryCredentials": true, "/repocreds.RepoCredsService/UpdateRepositoryCredentials": true, "/application.ApplicationService/PatchResource": true, + // Remove from logs both because the contents are sensitive and because they may be very large. + "/application.ApplicationService/GetManifestsWithFiles": true, } // NOTE: notice we do not configure the gRPC server here with TLS (e.g. grpc.Creds(creds)) // This is because TLS handshaking occurs in cmux handling @@ -688,7 +776,7 @@ func (a *ArgoCDServer) newGRPCServer() (*grpc.Server, application.AppResourceTre grpc_prometheus.StreamServerInterceptor, grpc_auth.StreamServerInterceptor(a.Authenticate), grpc_util.UserAgentStreamServerInterceptor(common.ArgoCDUserAgentName, clientConstraint), - grpc_util.PayloadStreamServerInterceptor(a.log, true, func(ctx netCtx.Context, fullMethodName string, servingObject interface{}) bool { + grpc_util.PayloadStreamServerInterceptor(a.log, true, func(ctx context.Context, fullMethodName string, servingObject interface{}) bool { return !sensitiveMethods[fullMethodName] }), grpc_util.ErrorCodeK8sStreamServerInterceptor(), @@ -702,7 +790,7 @@ func (a *ArgoCDServer) newGRPCServer() (*grpc.Server, application.AppResourceTre grpc_prometheus.UnaryServerInterceptor, grpc_auth.UnaryServerInterceptor(a.Authenticate), grpc_util.UserAgentUnaryServerInterceptor(common.ArgoCDUserAgentName, clientConstraint), - grpc_util.PayloadUnaryServerInterceptor(a.log, true, func(ctx netCtx.Context, fullMethodName string, servingObject interface{}) bool { + grpc_util.PayloadUnaryServerInterceptor(a.log, true, func(ctx context.Context, fullMethodName string, servingObject interface{}) bool { return !sensitiveMethods[fullMethodName] }), grpc_util.ErrorCodeK8sUnaryServerInterceptor(), @@ -765,6 +853,7 @@ func newArgoCDServiceSet(a *ArgoCDServer) *ArgoCDServiceSet { a.AppClientset, a.appLister, a.appInformer, + nil, a.RepoClientset, a.Cache, kubectl, @@ -775,7 +864,19 @@ func newArgoCDServiceSet(a *ArgoCDServer) *ArgoCDServiceSet { a.projInformer, a.ApplicationNamespaces) - applicationSetService := applicationset.NewServer(a.db, a.KubeClientset, a.enf, a.Cache, a.AppClientset, a.appLister, a.appsetInformer, a.appsetLister, a.projLister, a.settingsMgr, a.Namespace, projectLock) + applicationSetService := applicationset.NewServer( + a.db, + a.KubeClientset, + a.enf, + a.AppClientset, + a.appsetInformer, + a.appsetLister, + a.projLister, + a.settingsMgr, + a.Namespace, + projectLock, + a.ApplicationNamespaces) + projectService := project.NewServer(a.Namespace, a.KubeClientset, a.AppClientset, a.enf, projectLock, a.sessionMgr, a.policyEnforcer, a.projInformer, a.settingsMgr, a.db) appsInAnyNamespaceEnabled := len(a.ArgoCDServerOpts.ApplicationNamespaces) > 0 settingsService := settings.NewServer(a.settingsMgr, a.RepoClientset, a, a.DisableAuth, appsInAnyNamespaceEnabled) @@ -872,7 +973,7 @@ func compressHandler(handler http.Handler) http.Handler { // newHTTPServer returns the HTTP server to serve HTTP/HTTPS requests. This is implemented // using grpc-gateway as a proxy to the gRPC server. -func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandler http.Handler, appResourceTreeFn application.AppResourceTreeFn, conn *grpc.ClientConn) *http.Server { +func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandler http.Handler, appResourceTreeFn application.AppResourceTreeFn, conn *grpc.ClientConn, metricsReg HTTPMetricsRegistry) *http.Server { endpoint := fmt.Sprintf("localhost:%d", port) mux := http.NewServeMux() httpS := http.Server{ @@ -880,7 +981,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl Handler: &handlerSwitcher{ handler: mux, urlToHandler: map[string]http.Handler{ - "/api/badge": badge.NewHandler(a.AppClientset, a.settingsMgr, a.Namespace), + "/api/badge": badge.NewHandler(a.AppClientset, a.settingsMgr, a.Namespace, a.ApplicationNamespaces), common.LogoutEndpoint: logout.NewHandler(a.AppClientset, a.settingsMgr, a.sessionMgr, a.ArgoCDServerOpts.RootPath, a.ArgoCDServerOpts.BaseHRef, a.Namespace), }, contentTypeToHandler: map[string]http.Handler{ @@ -903,22 +1004,27 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl if a.EnableGZip { handler = compressHandler(handler) } + if len(a.ContentTypes) > 0 { + handler = enforceContentTypes(handler, a.ContentTypes) + } else { + log.WithField(common.SecurityField, common.SecurityHigh).Warnf("Content-Type enforcement is disabled, which may make your API vulnerable to CSRF attacks") + } mux.Handle("/api/", handler) - terminal := application.NewHandler(a.appLister, a.Namespace, a.ApplicationNamespaces, a.db, a.enf, a.Cache, appResourceTreeFn, a.settings.ExecShells). + terminal := application.NewHandler(a.appLister, a.Namespace, a.ApplicationNamespaces, a.db, a.enf, a.Cache, appResourceTreeFn, a.settings.ExecShells, a.sessionMgr). WithFeatureFlagMiddleware(a.settingsMgr.GetSettings) th := util_session.WithAuthMiddleware(a.DisableAuth, a.sessionMgr, terminal) mux.Handle("/terminal", th) - // Dead code for now - // Proxy extension is currently an experimental feature and is disabled + // Proxy extension is currently an alpha feature and is disabled // by default. - // if a.EnableProxyExtension { - // // API server won't panic if extensions fail to register. In - // // this case an error log will be sent and no extension route - // // will be added in mux. - // registerExtensions(mux, a) - // } + if a.EnableProxyExtension { + // API server won't panic if extensions fail to register. In + // this case an error log will be sent and no extension route + // will be added in mux. + registerExtensions(mux, a, metricsReg) + } + mustRegisterGWHandler(versionpkg.RegisterVersionServiceHandler, ctx, gwmux, conn) mustRegisterGWHandler(clusterpkg.RegisterClusterServiceHandler, ctx, gwmux, conn) mustRegisterGWHandler(applicationpkg.RegisterApplicationServiceHandler, ctx, gwmux, conn) @@ -942,7 +1048,8 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl // Webhook handler for git events (Note: cache timeouts are hardcoded because API server does not write to cache and not really using them) argoDB := db.NewDB(a.Namespace, a.settingsMgr, a.KubeClientset) - acdWebhookHandler := webhook.NewHandler(a.Namespace, a.AppClientset, a.settings, a.settingsMgr, repocache.NewCache(a.Cache.GetCache(), 24*time.Hour, 3*time.Minute), a.Cache, argoDB) + acdWebhookHandler := webhook.NewHandler(a.Namespace, a.ArgoCDServerOpts.ApplicationNamespaces, a.AppClientset, a.settings, a.settingsMgr, a.RepoServerCache, a.Cache, argoDB) + mux.HandleFunc("/api/webhook", acdWebhookHandler.Handler) // Serve cli binaries directly from API server @@ -951,9 +1058,13 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl // Serve extensions var extensionsSharedPath = "/tmp/extensions/" - mux.HandleFunc("/extensions.js", func(writer http.ResponseWriter, _ *http.Request) { + var extensionsHandler http.Handler = http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) { a.serveExtensions(extensionsSharedPath, writer) }) + if a.ArgoCDServerOpts.EnableGZip { + extensionsHandler = compressHandler(extensionsHandler) + } + mux.Handle("/extensions.js", extensionsHandler) // Serve UI static assets var assetsHandler http.Handler = http.HandlerFunc(a.newStaticAssetsHandler()) @@ -964,22 +1075,36 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl return &httpS } +func enforceContentTypes(handler http.Handler, types []string) http.Handler { + allowedTypes := map[string]bool{} + for _, t := range types { + allowedTypes[strings.ToLower(t)] = true + } + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet || allowedTypes[strings.ToLower(r.Header.Get("Content-Type"))] { + handler.ServeHTTP(w, r) + } else { + http.Error(w, "Invalid content type", http.StatusUnsupportedMediaType) + } + }) +} + // registerExtensions will try to register all configured extensions // in the given mux. If any error is returned while registering // extensions handlers, no route will be added in the given mux. -// nolint:deadcode,unused,staticcheck -func registerExtensions(mux *http.ServeMux, a *ArgoCDServer) { - sg := extension.NewDefaultSettingsGetter(a.settingsMgr) - ag := extension.NewDefaultApplicationGetter(a.serviceSet.ApplicationService) - em := extension.NewManager(sg, ag, a.log) - r := gmux.NewRouter() - - err := em.RegisterHandlers(r) +func registerExtensions(mux *http.ServeMux, a *ArgoCDServer, metricsReg HTTPMetricsRegistry) { + a.log.Info("Registering extensions...") + extHandler := http.HandlerFunc(a.extensionManager.CallExtension()) + authMiddleware := a.sessionMgr.AuthMiddlewareFunc(a.DisableAuth) + // auth middleware ensures that requests to all extensions are authenticated first + mux.Handle(fmt.Sprintf("%s/", extension.URLPrefix), authMiddleware(extHandler)) + + a.extensionManager.AddMetricsRegistry(metricsReg) + + err := a.extensionManager.RegisterExtensions() if err != nil { - a.log.Errorf("error registering extension handlers: %s", err) - return + a.log.Errorf("Error registering extensions: %s", err) } - mux.Handle(fmt.Sprintf("%s/", extension.URLPrefix), r) } var extensionsPattern = regexp.MustCompile(`^extension(.*)\.js$`) @@ -1032,7 +1157,7 @@ func (a *ArgoCDServer) registerDexHandlers(mux *http.ServeMux) { // Run dex OpenID Connect Identity Provider behind a reverse proxy (served at /api/dex) var err error mux.HandleFunc(common.DexAPIEndpoint+"/", dexutil.NewDexHTTPReverseProxy(a.DexServerAddr, a.BaseHRef, a.DexTLSConfig)) - a.ssoClientApp, err = oidc.NewClientApp(a.settings, a.DexServerAddr, a.DexTLSConfig, a.BaseHRef) + a.ssoClientApp, err = oidc.NewClientApp(a.settings, a.DexServerAddr, a.DexTLSConfig, a.BaseHRef, cacheutil.NewRedisCache(a.RedisClient, a.settings.UserInfoCacheExpiration(), cacheutil.RedisCompressionNone)) errorsutil.CheckError(err) mux.HandleFunc(common.LoginEndpoint, a.ssoClientApp.HandleLogin) mux.HandleFunc(common.CallbackEndpoint, a.ssoClientApp.HandleCallback) @@ -1141,7 +1266,11 @@ func (server *ArgoCDServer) newStaticAssetsHandler() func(http.ResponseWriter, * http.ServeContent(w, r, "index.html", modTime, io.NewByteReadSeeker(data)) } else { if isMainJsBundle(r.URL) { - w.Header().Set("Cache-Control", "public, max-age=31536000, immutable") + cacheControl := "public, max-age=31536000, immutable" + if !fileRequest { + cacheControl = "no-cache" + } + w.Header().Set("Cache-Control", cacheControl) } http.FileServer(server.staticAssets).ServeHTTP(w, r) } @@ -1222,7 +1351,35 @@ func (a *ArgoCDServer) getClaims(ctx context.Context) (jwt.Claims, string, error if err != nil { return claims, "", status.Errorf(codes.Unauthenticated, "invalid session: %v", err) } - return claims, newToken, nil + + // Some SSO implementations (Okta) require a call to + // the OIDC user info path to get attributes like groups + // we assume that everywhere in argocd jwt.MapClaims is used as type for interface jwt.Claims + // otherwise this would cause a panic + var groupClaims jwt.MapClaims + if groupClaims, ok = claims.(jwt.MapClaims); !ok { + if tmpClaims, ok := claims.(*jwt.MapClaims); ok { + groupClaims = *tmpClaims + } + } + iss := jwtutil.StringField(groupClaims, "iss") + if iss != util_session.SessionManagerClaimsIssuer && a.settings.UserInfoGroupsEnabled() && a.settings.UserInfoPath() != "" { + userInfo, unauthorized, err := a.ssoClientApp.GetUserInfo(groupClaims, a.settings.IssuerURL(), a.settings.UserInfoPath()) + if unauthorized { + log.Errorf("error while quering userinfo endpoint: %v", err) + return claims, "", status.Errorf(codes.Unauthenticated, "invalid session") + } + if err != nil { + log.Errorf("error fetching user info endpoint: %v", err) + return claims, "", status.Errorf(codes.Internal, "invalid userinfo response") + } + if groupClaims["sub"] != userInfo["sub"] { + return claims, "", status.Error(codes.Unknown, "subject of claims from user info endpoint didn't match subject of idToken, see https://openid.net/specs/openid-connect-core-1_0.html#UserInfo") + } + groupClaims["groups"] = userInfo["groups"] + } + + return groupClaims, newToken, nil } // getToken extracts the token from gRPC metadata or cookie headers diff --git a/server/server_norace_test.go b/server/server_norace_test.go index 5238351c03720..eaef2e67257d1 100644 --- a/server/server_norace_test.go +++ b/server/server_norace_test.go @@ -27,7 +27,7 @@ func TestUserAgent(t *testing.T) { // the data race, it APPEARS to be intentional, but in any case it's nothing we are doing in Argo CD // that is causing this issue. - s, closer := fakeServer() + s, closer := fakeServer(t) defer closer() lns, err := s.Listen() assert.NoError(t, err) @@ -94,7 +94,7 @@ func Test_StaticHeaders(t *testing.T) { // Test default policy "sameorigin" and "frame-ancestors 'self';" { - s, closer := fakeServer() + s, closer := fakeServer(t) defer closer() lns, err := s.Listen() assert.NoError(t, err) @@ -111,7 +111,7 @@ func Test_StaticHeaders(t *testing.T) { client := http.Client{} url := fmt.Sprintf("http://127.0.0.1:%d/test.html", s.ListenPort) - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) assert.NoError(t, err) resp, err := client.Do(req) assert.NoError(t, err) @@ -121,7 +121,7 @@ func Test_StaticHeaders(t *testing.T) { // Test custom policy for X-Frame-Options and Content-Security-Policy { - s, closer := fakeServer() + s, closer := fakeServer(t) defer closer() s.XFrameOptions = "deny" s.ContentSecurityPolicy = "frame-ancestors 'none';" @@ -140,7 +140,7 @@ func Test_StaticHeaders(t *testing.T) { client := http.Client{} url := fmt.Sprintf("http://127.0.0.1:%d/test.html", s.ListenPort) - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) assert.NoError(t, err) resp, err := client.Do(req) assert.NoError(t, err) @@ -150,7 +150,7 @@ func Test_StaticHeaders(t *testing.T) { // Test disabled X-Frame-Options and Content-Security-Policy { - s, closer := fakeServer() + s, closer := fakeServer(t) defer closer() s.XFrameOptions = "" s.ContentSecurityPolicy = "" @@ -172,7 +172,7 @@ func Test_StaticHeaders(t *testing.T) { client := http.Client{} url := fmt.Sprintf("http://127.0.0.1:%d/test.html", s.ListenPort) - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) assert.NoError(t, err) resp, err := client.Do(req) assert.NoError(t, err) diff --git a/server/server_test.go b/server/server_test.go index e4fdafee8bcd7..c4f4153f24d89 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -7,11 +7,12 @@ import ( "net/http" "net/http/httptest" "net/url" + "os" + "path/filepath" "strings" "testing" "time" - "github.com/ghodss/yaml" "github.com/golang-jwt/jwt/v4" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" @@ -19,6 +20,7 @@ import ( "google.golang.org/grpc/metadata" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apiclient" @@ -30,13 +32,21 @@ import ( "github.com/argoproj/argo-cd/v2/server/rbacpolicy" "github.com/argoproj/argo-cd/v2/test" "github.com/argoproj/argo-cd/v2/util/assets" + "github.com/argoproj/argo-cd/v2/util/cache" cacheutil "github.com/argoproj/argo-cd/v2/util/cache" appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate" + "github.com/argoproj/argo-cd/v2/util/oidc" "github.com/argoproj/argo-cd/v2/util/rbac" settings_util "github.com/argoproj/argo-cd/v2/util/settings" + testutil "github.com/argoproj/argo-cd/v2/util/test" ) -func fakeServer() (*ArgoCDServer, func()) { +type FakeArgoCDServer struct { + *ArgoCDServer + TmpAssetsDir string +} + +func fakeServer(t *testing.T) (*FakeArgoCDServer, func()) { cm := test.NewFakeConfigMap() secret := test.NewFakeSecret() kubeclientset := fake.NewSimpleClientset(cm, secret) @@ -44,6 +54,7 @@ func fakeServer() (*ArgoCDServer, func()) { redis, closer := test.NewInMemoryRedis() port, err := test.GetFreePort() mockRepoClient := &mocks.Clientset{RepoServerServiceClient: &mocks.RepoServerServiceClient{}} + tmpAssetsDir := t.TempDir() if err != nil { panic(err) @@ -67,11 +78,13 @@ func fakeServer() (*ArgoCDServer, func()) { 1*time.Minute, 1*time.Minute, ), - RedisClient: redis, - RepoClientset: mockRepoClient, + RedisClient: redis, + RepoClientset: mockRepoClient, + StaticAssetsDir: tmpAssetsDir, } srv := NewServer(context.Background(), argoCDOpts) - return srv, closer + fakeSrv := &FakeArgoCDServer{srv, tmpAssetsDir} + return fakeSrv, closer } func TestEnforceProjectToken(t *testing.T) { @@ -392,7 +405,7 @@ func TestRevokedToken(t *testing.T) { } func TestCertsAreNotGeneratedInInsecureMode(t *testing.T) { - s, closer := fakeServer() + s, closer := fakeServer(t) defer closer() assert.True(t, s.Insecure) assert.Nil(t, s.settings.Certificate) @@ -517,12 +530,12 @@ func dexMockHandler(t *testing.T, url string) func(http.ResponseWriter, *http.Re t.Fail() } default: - w.WriteHeader(404) + w.WriteHeader(http.StatusNotFound) } } } -func getTestServer(t *testing.T, anonymousEnabled bool, withFakeSSO bool) (argocd *ArgoCDServer, dexURL string) { +func getTestServer(t *testing.T, anonymousEnabled bool, withFakeSSO bool, useDexForSSO bool, additionalOIDCConfig settings_util.OIDCConfig) (argocd *ArgoCDServer, oidcURL string) { cm := test.NewFakeConfigMap() if anonymousEnabled { cm.Data["users.anonymous.enabled"] = "true" @@ -533,9 +546,14 @@ func getTestServer(t *testing.T, anonymousEnabled bool, withFakeSSO bool) (argoc ts.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { dexMockHandler(t, ts.URL)(w, r) }) + oidcServer := ts + if !useDexForSSO { + oidcServer = testutil.GetOIDCTestServer(t) + } if withFakeSSO { cm.Data["url"] = ts.URL - cm.Data["dex.config"] = ` + if useDexForSSO { + cm.Data["dex.config"] = ` connectors: # OIDC - type: OIDC @@ -545,6 +563,18 @@ connectors: issuer: https://auth.example.gom clientID: test-client clientSecret: $dex.oidc.clientSecret` + } else { + // override required oidc config fields but keep other configs as passed in + additionalOIDCConfig.Name = "Okta" + additionalOIDCConfig.Issuer = oidcServer.URL + additionalOIDCConfig.ClientID = "argo-cd" + additionalOIDCConfig.ClientSecret = "$oidc.okta.clientSecret" + oidcConfigString, err := yaml.Marshal(additionalOIDCConfig) + require.NoError(t, err) + cm.Data["oidc.config"] = string(oidcConfigString) + // Avoid bothering with certs for local tests. + cm.Data["oidc.tls.insecure.skip.verify"] = "true" + } } secret := test.NewFakeSecret() kubeclientset := fake.NewSimpleClientset(cm, secret) @@ -556,27 +586,132 @@ connectors: AppClientset: appClientSet, RepoClientset: mockRepoClient, } - if withFakeSSO { + if withFakeSSO && useDexForSSO { argoCDOpts.DexServerAddr = ts.URL } argocd = NewServer(context.Background(), argoCDOpts) - return argocd, ts.URL + var err error + argocd.ssoClientApp, err = oidc.NewClientApp(argocd.settings, argocd.DexServerAddr, argocd.DexTLSConfig, argocd.BaseHRef, cache.NewInMemoryCache(24*time.Hour)) + require.NoError(t, err) + return argocd, oidcServer.URL +} + +func TestGetClaims(t *testing.T) { + + defaultExpiry := jwt.NewNumericDate(time.Now().Add(time.Hour * 24)) + defaultExpiryUnix := float64(defaultExpiry.Unix()) + + type testData struct { + test string + claims jwt.MapClaims + expectedErrorContains string + expectedClaims jwt.MapClaims + expectNewToken bool + additionalOIDCConfig settings_util.OIDCConfig + } + var tests = []testData{ + { + test: "GetClaims", + claims: jwt.MapClaims{ + "aud": "argo-cd", + "exp": defaultExpiry, + "sub": "randomUser", + }, + expectedErrorContains: "", + expectedClaims: jwt.MapClaims{ + "aud": "argo-cd", + "exp": defaultExpiryUnix, + "sub": "randomUser", + }, + expectNewToken: false, + additionalOIDCConfig: settings_util.OIDCConfig{}, + }, + { + // note: a passing test with user info groups can never be achieved since the user never logged in properly + // therefore the oidcClient's cache contains no accessToken for the user info endpoint + // and since the oidcClient cache is unexported (for good reasons) we can't mock this behaviour + test: "GetClaimsWithUserInfoGroupsEnabled", + claims: jwt.MapClaims{ + "aud": common.ArgoCDClientAppID, + "exp": defaultExpiry, + "sub": "randomUser", + }, + expectedErrorContains: "invalid session", + expectedClaims: jwt.MapClaims{ + "aud": common.ArgoCDClientAppID, + "exp": defaultExpiryUnix, + "sub": "randomUser", + }, + expectNewToken: false, + additionalOIDCConfig: settings_util.OIDCConfig{ + EnableUserInfoGroups: true, + UserInfoPath: "/userinfo", + UserInfoCacheExpiration: "5m", + }, + }, + } + + for _, testData := range tests { + testDataCopy := testData + + t.Run(testDataCopy.test, func(t *testing.T) { + t.Parallel() + + // Must be declared here to avoid race. + ctx := context.Background() //nolint:ineffassign,staticcheck + + argocd, oidcURL := getTestServer(t, false, true, false, testDataCopy.additionalOIDCConfig) + + // create new JWT and store it on the context to simulate an incoming request + testDataCopy.claims["iss"] = oidcURL + testDataCopy.expectedClaims["iss"] = oidcURL + token := jwt.NewWithClaims(jwt.SigningMethodRS512, testDataCopy.claims) + key, err := jwt.ParseRSAPrivateKeyFromPEM(testutil.PrivateKey) + require.NoError(t, err) + tokenString, err := token.SignedString(key) + require.NoError(t, err) + ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs(apiclient.MetaDataTokenKey, tokenString)) + + gotClaims, newToken, err := argocd.getClaims(ctx) + + // Note: testutil.oidcMockHandler currently doesn't implement reissuing expired tokens + // so newToken will always be empty + if testDataCopy.expectNewToken { + assert.NotEmpty(t, newToken) + } + if testDataCopy.expectedClaims == nil { + assert.Nil(t, gotClaims) + } else { + assert.Equal(t, testDataCopy.expectedClaims, gotClaims) + } + if testDataCopy.expectedErrorContains != "" { + assert.ErrorContains(t, err, testDataCopy.expectedErrorContains, "getClaims should have thrown an error and return an error") + } else { + assert.NoError(t, err) + } + }) + } } func TestAuthenticate_3rd_party_JWTs(t *testing.T) { + // Marshaling single strings to strings is typical, so we test for this relatively common behavior. + jwt.MarshalSingleStringAsArray = false + type testData struct { test string anonymousEnabled bool claims jwt.RegisteredClaims expectedErrorContains string expectedClaims interface{} + useDex bool } var tests = []testData{ + // Dex { test: "anonymous disabled, no audience", anonymousEnabled: false, - claims: jwt.RegisteredClaims{}, - expectedErrorContains: "no audience found in the token", + claims: jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}, + expectedErrorContains: common.TokenVerificationError, expectedClaims: nil, }, { @@ -589,31 +724,95 @@ func TestAuthenticate_3rd_party_JWTs(t *testing.T) { { test: "anonymous disabled, unexpired token, admin claim", anonymousEnabled: false, - claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}, - expectedErrorContains: "id token signed with unsupported algorithm", + claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}, + expectedErrorContains: common.TokenVerificationError, expectedClaims: nil, }, { test: "anonymous enabled, unexpired token, admin claim", anonymousEnabled: true, - claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}, + claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}, expectedErrorContains: "", expectedClaims: "", }, { test: "anonymous disabled, expired token, admin claim", anonymousEnabled: false, - claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())}, - expectedErrorContains: "token is expired", + claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())}, + expectedErrorContains: common.TokenVerificationError, expectedClaims: jwt.RegisteredClaims{Issuer: "sso"}, }, { test: "anonymous enabled, expired token, admin claim", anonymousEnabled: true, - claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())}, + claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())}, + expectedErrorContains: "", + expectedClaims: "", + }, + { + test: "anonymous disabled, unexpired token, admin claim, incorrect audience", + anonymousEnabled: false, + claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"incorrect-audience"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}, + expectedErrorContains: common.TokenVerificationError, + expectedClaims: nil, + }, + // External OIDC (not bundled Dex) + { + test: "external OIDC: anonymous disabled, no audience", + anonymousEnabled: false, + claims: jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}, + useDex: true, + expectedErrorContains: common.TokenVerificationError, + expectedClaims: nil, + }, + { + test: "external OIDC: anonymous enabled, no audience", + anonymousEnabled: true, + claims: jwt.RegisteredClaims{}, + useDex: true, expectedErrorContains: "", expectedClaims: "", }, + { + test: "external OIDC: anonymous disabled, unexpired token, admin claim", + anonymousEnabled: false, + claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}, + useDex: true, + expectedErrorContains: common.TokenVerificationError, + expectedClaims: nil, + }, + { + test: "external OIDC: anonymous enabled, unexpired token, admin claim", + anonymousEnabled: true, + claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}, + useDex: true, + expectedErrorContains: "", + expectedClaims: "", + }, + { + test: "external OIDC: anonymous disabled, expired token, admin claim", + anonymousEnabled: false, + claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())}, + useDex: true, + expectedErrorContains: common.TokenVerificationError, + expectedClaims: jwt.RegisteredClaims{Issuer: "sso"}, + }, + { + test: "external OIDC: anonymous enabled, expired token, admin claim", + anonymousEnabled: true, + claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{common.ArgoCDClientAppID}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())}, + useDex: true, + expectedErrorContains: "", + expectedClaims: "", + }, + { + test: "external OIDC: anonymous disabled, unexpired token, admin claim, incorrect audience", + anonymousEnabled: false, + claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"incorrect-audience"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}, + useDex: true, + expectedErrorContains: common.TokenVerificationError, + expectedClaims: nil, + }, } for _, testData := range tests { @@ -625,8 +824,13 @@ func TestAuthenticate_3rd_party_JWTs(t *testing.T) { // Must be declared here to avoid race. ctx := context.Background() //nolint:ineffassign,staticcheck - argocd, dexURL := getTestServer(t, testDataCopy.anonymousEnabled, true) - testDataCopy.claims.Issuer = fmt.Sprintf("%s/api/dex", dexURL) + argocd, oidcURL := getTestServer(t, testDataCopy.anonymousEnabled, true, testDataCopy.useDex, settings_util.OIDCConfig{}) + + if testDataCopy.useDex { + testDataCopy.claims.Issuer = fmt.Sprintf("%s/api/dex", oidcURL) + } else { + testDataCopy.claims.Issuer = oidcURL + } token := jwt.NewWithClaims(jwt.SigningMethodHS256, testDataCopy.claims) tokenString, err := token.SignedString([]byte("key")) require.NoError(t, err) @@ -676,7 +880,7 @@ func TestAuthenticate_no_request_metadata(t *testing.T) { t.Run(testDataCopy.test, func(t *testing.T) { t.Parallel() - argocd, _ := getTestServer(t, testDataCopy.anonymousEnabled, true) + argocd, _ := getTestServer(t, testDataCopy.anonymousEnabled, true, true, settings_util.OIDCConfig{}) ctx := context.Background() ctx, err := argocd.Authenticate(ctx) @@ -722,7 +926,7 @@ func TestAuthenticate_no_SSO(t *testing.T) { // Must be declared here to avoid race. ctx := context.Background() //nolint:ineffassign,staticcheck - argocd, dexURL := getTestServer(t, testDataCopy.anonymousEnabled, false) + argocd, dexURL := getTestServer(t, testDataCopy.anonymousEnabled, false, true, settings_util.OIDCConfig{}) token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{Issuer: fmt.Sprintf("%s/api/dex", dexURL)}) tokenString, err := token.SignedString([]byte("key")) require.NoError(t, err) @@ -795,7 +999,7 @@ func TestAuthenticate_bad_request_metadata(t *testing.T) { test: "anonymous disabled, bad auth header", anonymousEnabled: false, metadata: metadata.MD{"authorization": []string{"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.TGGTTHuuGpEU8WgobXxkrBtW3NiR3dgw5LR-1DEW3BQ"}}, - expectedErrorMessage: "no audience found in the token", + expectedErrorMessage: common.TokenVerificationError, expectedClaims: nil, }, { @@ -809,7 +1013,7 @@ func TestAuthenticate_bad_request_metadata(t *testing.T) { test: "anonymous disabled, bad auth cookie", anonymousEnabled: false, metadata: metadata.MD{"grpcgateway-cookie": []string{"argocd.token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.TGGTTHuuGpEU8WgobXxkrBtW3NiR3dgw5LR-1DEW3BQ"}}, - expectedErrorMessage: "no audience found in the token", + expectedErrorMessage: common.TokenVerificationError, expectedClaims: nil, }, { @@ -830,7 +1034,7 @@ func TestAuthenticate_bad_request_metadata(t *testing.T) { // Must be declared here to avoid race. ctx := context.Background() //nolint:ineffassign,staticcheck - argocd, _ := getTestServer(t, testDataCopy.anonymousEnabled, true) + argocd, _ := getTestServer(t, testDataCopy.anonymousEnabled, true, true, settings_util.OIDCConfig{}) ctx = metadata.NewIncomingContext(context.Background(), testDataCopy.metadata) ctx, err := argocd.Authenticate(ctx) @@ -1137,6 +1341,72 @@ func TestIsMainJsBundle(t *testing.T) { } } +func TestCacheControlHeaders(t *testing.T) { + testCases := []struct { + name string + filename string + createFile bool + expectedStatus int + expectedCacheControlHeaders []string + }{ + { + name: "file exists", + filename: "exists.html", + createFile: true, + expectedStatus: 200, + expectedCacheControlHeaders: nil, + }, + { + name: "file does not exist", + filename: "missing.html", + createFile: false, + expectedStatus: 404, + expectedCacheControlHeaders: nil, + }, + { + name: "main js bundle exists", + filename: "main.e4188e5adc97bbfc00c3.js", + createFile: true, + expectedStatus: 200, + expectedCacheControlHeaders: []string{"public, max-age=31536000, immutable"}, + }, + { + name: "main js bundle does not exists", + filename: "main.e4188e5adc97bbfc00c0.js", + createFile: false, + expectedStatus: 404, + expectedCacheControlHeaders: []string{"no-cache"}, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + argocd, closer := fakeServer(t) + defer closer() + + handler := argocd.newStaticAssetsHandler() + + rr := httptest.NewRecorder() + req := httptest.NewRequest("", fmt.Sprintf("/%s", testCase.filename), nil) + + fp := filepath.Join(argocd.TmpAssetsDir, testCase.filename) + + if testCase.createFile { + tmpFile, err := os.Create(fp) + assert.NoError(t, err) + err = tmpFile.Close() + assert.NoError(t, err) + } + + handler(rr, req) + + assert.Equal(t, testCase.expectedStatus, rr.Code) + + cacheControl := rr.Result().Header["Cache-Control"] + assert.Equal(t, testCase.expectedCacheControlHeaders, cacheControl) + }) + } +} func TestReplaceBaseHRef(t *testing.T) { testCases := []struct { name string @@ -1162,7 +1432,7 @@ func TestReplaceBaseHRef(t *testing.T) { @@ -1186,7 +1456,7 @@ func TestReplaceBaseHRef(t *testing.T) { @@ -1214,7 +1484,7 @@ func TestReplaceBaseHRef(t *testing.T) { @@ -1238,7 +1508,7 @@ func TestReplaceBaseHRef(t *testing.T) { @@ -1256,3 +1526,46 @@ func TestReplaceBaseHRef(t *testing.T) { }) } } + +func Test_enforceContentTypes(t *testing.T) { + getBaseHandler := func(t *testing.T, allow bool) http.Handler { + return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assert.True(t, allow, "http handler was hit when it should have been blocked by content type enforcement") + writer.WriteHeader(200) + }) + } + + t.Parallel() + + t.Run("GET - not providing a content type, should still succeed", func(t *testing.T) { + handler := enforceContentTypes(getBaseHandler(t, true), []string{"application/json"}).(http.HandlerFunc) + req := httptest.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + handler(w, req) + resp := w.Result() + assert.Equal(t, 200, resp.StatusCode) + }) + + t.Run("POST", func(t *testing.T) { + handler := enforceContentTypes(getBaseHandler(t, true), []string{"application/json"}).(http.HandlerFunc) + req := httptest.NewRequest("POST", "/", nil) + w := httptest.NewRecorder() + handler(w, req) + resp := w.Result() + assert.Equal(t, 415, resp.StatusCode, "didn't provide a content type, should have gotten an error") + + req = httptest.NewRequest("POST", "/", nil) + req.Header = map[string][]string{"Content-Type": {"application/json"}} + w = httptest.NewRecorder() + handler(w, req) + resp = w.Result() + assert.Equal(t, 200, resp.StatusCode, "should have passed, since an allowed content type was provided") + + req = httptest.NewRequest("POST", "/", nil) + req.Header = map[string][]string{"Content-Type": {"not-allowed"}} + w = httptest.NewRecorder() + handler(w, req) + resp = w.Result() + assert.Equal(t, 415, resp.StatusCode, "should not have passed, since a disallowed content type was provided") + }) +} diff --git a/server/settings/settings.go b/server/settings/settings.go index ba8a582e58cdd..32f5016419b4b 100644 --- a/server/settings/settings.go +++ b/server/settings/settings.go @@ -4,11 +4,11 @@ import ( "context" "fmt" - "github.com/argoproj/argo-cd/v2/reposerver/apiclient" - ioutil "github.com/argoproj/argo-cd/v2/util/io" "github.com/golang/protobuf/ptypes/empty" + "sigs.k8s.io/yaml" - "github.com/ghodss/yaml" + "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + ioutil "github.com/argoproj/argo-cd/v2/util/io" sessionmgr "github.com/argoproj/argo-cd/v2/util/session" @@ -62,10 +62,6 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin if err != nil { return nil, err } - plugins, err := s.plugins(ctx) - if err != nil { - return nil, err - } userLoginsDisabled := true accounts, err := s.mgr.GetAccounts() if err != nil { @@ -110,7 +106,6 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin ChatText: help.ChatText, BinaryUrls: help.BinaryURLs, }, - Plugins: plugins, UserLoginsDisabled: userLoginsDisabled, KustomizeVersions: kustomizeVersions, UiCssURL: argoCDSettings.UiCssURL, @@ -121,15 +116,6 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin } if sessionmgr.LoggedIn(ctx) || s.disableAuth { - configManagementPlugins, err := s.mgr.GetConfigManagementPlugins() - if err != nil { - return nil, err - } - tools := make([]*v1alpha1.ConfigManagementPlugin, len(configManagementPlugins)) - for i := range configManagementPlugins { - tools[i] = &configManagementPlugins[i] - } - set.ConfigManagementPlugins = tools set.UiBannerContent = argoCDSettings.UiBannerContent set.UiBannerURL = argoCDSettings.UiBannerURL set.UiBannerPermanent = argoCDSettings.UiBannerPermanent @@ -145,11 +131,12 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin } if oidcConfig := argoCDSettings.OIDCConfig(); oidcConfig != nil { set.OIDCConfig = &settingspkg.OIDCConfig{ - Name: oidcConfig.Name, - Issuer: oidcConfig.Issuer, - ClientID: oidcConfig.ClientID, - CLIClientID: oidcConfig.CLIClientID, - Scopes: oidcConfig.RequestedScopes, + Name: oidcConfig.Name, + Issuer: oidcConfig.Issuer, + ClientID: oidcConfig.ClientID, + CLIClientID: oidcConfig.CLIClientID, + Scopes: oidcConfig.RequestedScopes, + EnablePKCEAuthentication: oidcConfig.EnablePKCEAuthentication, } if len(argoCDSettings.OIDCConfig().RequestedIDTokenClaims) > 0 { set.OIDCConfig.IDTokenClaims = argoCDSettings.OIDCConfig().RequestedIDTokenClaims @@ -158,11 +145,16 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin return &set, nil } -func (s *Server) plugins(ctx context.Context) ([]*settingspkg.Plugin, error) { - in, err := s.mgr.GetConfigManagementPlugins() +// GetPlugins returns a list of plugins +func (s *Server) GetPlugins(ctx context.Context, q *settingspkg.SettingsQuery) (*settingspkg.SettingsPluginsResponse, error) { + plugins, err := s.plugins(ctx) if err != nil { return nil, err } + return &settingspkg.SettingsPluginsResponse{Plugins: plugins}, nil +} + +func (s *Server) plugins(ctx context.Context) ([]*settingspkg.Plugin, error) { closer, client, err := s.repoClient.NewRepoServerClient() if err != nil { return nil, fmt.Errorf("error creating repo server client: %w", err) @@ -173,11 +165,8 @@ func (s *Server) plugins(ctx context.Context) ([]*settingspkg.Plugin, error) { if err != nil { return nil, fmt.Errorf("failed to list sidecar plugins from reposerver: %w", err) } - out := []*settingspkg.Plugin{} - for _, p := range in { - out = append(out, &settingspkg.Plugin{Name: p.Name}) - } + var out []*settingspkg.Plugin if pluginList != nil && len(pluginList.Items) > 0 { for _, p := range pluginList.Items { out = append(out, &settingspkg.Plugin{Name: p.Name}) @@ -189,7 +178,11 @@ func (s *Server) plugins(ctx context.Context) ([]*settingspkg.Plugin, error) { // AuthFuncOverride disables authentication for settings service func (s *Server) AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error) { - // this authenticates the user, but ignores any error, so that we have claims populated - ctx, _ = s.authenticator.Authenticate(ctx) - return ctx, nil + ctx, err := s.authenticator.Authenticate(ctx) + if fullMethodName == "/cluster.SettingsService/Get" { + // SettingsService/Get API is used by login page. + // This authenticates the user, but ignores any error, so that we have claims populated + err = nil + } + return ctx, err } diff --git a/server/settings/settings.proto b/server/settings/settings.proto index 0e9ab9951ae30..a6aa97120c8de 100644 --- a/server/settings/settings.proto +++ b/server/settings/settings.proto @@ -28,6 +28,7 @@ message Settings { Help help = 9; repeated Plugin plugins = 10; bool userLoginsDisabled = 11; + // Deprecated: use sidecar plugins instead. repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ConfigManagementPlugin configManagementPlugins = 12; repeated string kustomizeVersions = 13; string uiCssURL = 14; @@ -48,6 +49,10 @@ message GoogleAnalyticsConfig { bool anonymizeUsers = 2; } +message SettingsPluginsResponse { + repeated Plugin plugins = 1; +} + // Help settings message Help { // the URL for getting chat help, this will typically be your Slack channel for support @@ -64,7 +69,6 @@ message Plugin { string name = 1; } - message DexConfig { repeated Connector connectors = 1; } @@ -81,6 +85,7 @@ message OIDCConfig { string cliClientID = 4 [(gogoproto.customname) = "CLIClientID"]; repeated string scopes = 5; map idTokenClaims = 6 [(gogoproto.customname) = "IDTokenClaims"]; + bool enablePKCEAuthentication = 7; } // SettingsService @@ -88,7 +93,11 @@ service SettingsService { // Get returns Argo CD settings rpc Get(SettingsQuery) returns (Settings) { - option (google.api.http).get = "/api/v1/settings"; - } + option (google.api.http).get = "/api/v1/settings"; + } + // Get returns Argo CD plugins + rpc GetPlugins(SettingsQuery) returns (SettingsPluginsResponse) { + option (google.api.http).get = "/api/v1/settings/plugins"; + } } diff --git a/server/version/version.go b/server/version/version.go index e899a7ab590d5..cc4eeb24152a6 100644 --- a/server/version/version.go +++ b/server/version/version.go @@ -2,6 +2,7 @@ package version import ( "context" + "github.com/golang/protobuf/ptypes/empty" "github.com/google/go-jsonnet" @@ -67,6 +68,7 @@ func (s *Server) Version(ctx context.Context, _ *empty.Empty) (*version.VersionM HelmVersion: s.helmVersion, JsonnetVersion: s.jsonnetVersion, KubectlVersion: vers.KubectlVersion, + ExtraBuildInfo: vers.ExtraBuildInfo, }, nil } diff --git a/server/version/version.proto b/server/version/version.proto index a2bc1d685661f..f5b887aa29f00 100644 --- a/server/version/version.proto +++ b/server/version/version.proto @@ -23,6 +23,7 @@ message VersionMessage { string HelmVersion = 11; string KubectlVersion = 12; string JsonnetVersion = 13; + string ExtraBuildInfo = 14; } // VersionService returns the version of the API server. diff --git a/test/certificates/ssh_known_hosts b/test/certificates/ssh_known_hosts index 31a7bae3fce5d..0dbb98bd2cacb 100644 --- a/test/certificates/ssh_known_hosts +++ b/test/certificates/ssh_known_hosts @@ -1,6 +1,6 @@ # This file was automatically generated. DO NOT EDIT -bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw== -github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== +bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQeJzhupRu0u0cdegZIa8e86EG2qOCsIsD1Xw0xSeiPDlCr7kq97NLmMbpKTX6Esc30NuoqEEHCuc7yWtwp8dI76EEEB1VqY9QJq6vk+aySyboD5QF61I/1WeTwu+deCbgKMGbUijeXhtfbxSxm6JwGrXrhBdofTsbKRUsrN1WoNgUa8uqN1Vx6WAJw1JHPhglEGGHea6QICwJOAr/6mrui/oB7pkaWKHj3z7d1IC4KWLtY47elvjbaTlkN04Kc/5LFEirorGYVbt15kAUlqGM65pk6ZBxtaO3+30LVlORZkxOh+LKL/BvbZ/iRNhItLqNyieoQj/uh/7Iv4uyH/cV/0b4WDSd3DptigWq84lJubb9t/DnZlrJazxyDCulTmKdOR7vs9gMTo+uoIrPSb8ScTtvw65+odKAlBj59dhnVp9zd7QUojOpXlL62Aw56U4oO+FALuevvMjiWeavKhJqlR7i5n9srYcrNV7ttmDw7kf/97P5zauIhxcjX+xHv4M= +github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 diff --git a/test/cmp/plugin.yaml b/test/cmp/plugin.yaml index e68fc3dee74af..bc4a3ac8ad57d 100644 --- a/test/cmp/plugin.yaml +++ b/test/cmp/plugin.yaml @@ -4,6 +4,8 @@ metadata: name: cmp-plugin spec: version: v1.0 + init: + command: [env] generate: command: [sh, -c, 'kustomize build'] discover: diff --git a/test/container/Dockerfile b/test/container/Dockerfile index dbb5cb719dc1a..5272b7a14f7d8 100644 --- a/test/container/Dockerfile +++ b/test/container/Dockerfile @@ -1,40 +1,41 @@ -FROM docker.io/library/redis:7.0.5 as redis +FROM docker.io/library/redis:7.2.4@sha256:7dd707032d90c6eaafd566f62a00f5b0116ae08fd7d6cbbb0f311b82b47171a2 as redis # There are libraries we will want to copy from here in the final stage of the # build, but the COPY directive does not have a way to determine system # architecture, so we create a symlink here to facilitate copying. -RUN ln -s /usr/lib/$(uname -m)-linux-gnu /usr/lib/linux-gnu +RUN ln -s /usr/lib/$(uname -m)-linux-gnu /usr/lib/linux-gnu -FROM docker.io/library/node:12.18.4-buster as node +# Please make sure to also check the contained yarn version and update the references below when upgrading this image's version +FROM docker.io/library/node:21.7.1@sha256:b9ccc4aca32eebf124e0ca0fd573dacffba2b9236987a1d4d2625ce3c162ecc8 as node -FROM docker.io/library/golang:1.18 as golang +FROM docker.io/library/golang:1.21.8@sha256:856073656d1a517517792e6cdd2f7a5ef080d3ca2dff33e518c8412f140fdd2d as golang -FROM docker.io/library/registry:2.8 as registry +FROM docker.io/library/registry:2.8@sha256:fb9c9aef62af3955f6014613456551c92e88a67dcf1fc51f5f91bcbd1832813f as registry -FROM docker.io/bitnami/kubectl:1.23.6 as kubectl +FROM docker.io/bitnami/kubectl:1.27@sha256:14ab746e857d96c105df4989cc2bf841292f2d143f7c60f9d7f549ae660eab43 as kubectl -FROM ubuntu:22.04 +FROM docker.io/library/ubuntu:22.04@sha256:77906da86b60585ce12215807090eb327e7386c8fafb5402369e421f44eff17e ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install --fix-missing -y \ - ca-certificates \ - curl \ - openssh-server \ - nginx \ - fcgiwrap \ - git \ - git-lfs \ - gpg \ - jq \ - make \ - wget \ - gcc \ - g++ \ - sudo \ - tini \ - zip && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + ca-certificates \ + curl \ + openssh-server \ + nginx \ + fcgiwrap \ + git \ + git-lfs \ + gpg \ + jq \ + make \ + wget \ + gcc \ + g++ \ + sudo \ + tini \ + zip && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* COPY --from=golang /usr/local/go /usr/local/go @@ -48,7 +49,7 @@ ENV GOPATH /go COPY hack/install.sh hack/tool-versions.sh go.* ./ COPY hack/installers installers -RUN ./install.sh helm-linux && \ +RUN ./install.sh helm && \ ./install.sh kustomize && \ ./install.sh codegen-tools && \ ./install.sh codegen-go-tools && \ @@ -71,10 +72,10 @@ COPY --from=redis /usr/local/bin/* /usr/local/bin/ # Copy redis dependencies/shared libraries # Ubuntu 22.04+ has moved to OpenSSL3 and no longer provides these libraries -COPY --from=redis /usr/lib/linux-gnu/libssl.so.1.1 /usr/lib/linux-gnu/ -COPY --from=redis /usr/lib/linux-gnu/libcrypto.so.1.1 /usr/lib/linux-gnu/ -RUN mv /usr/lib/linux-gnu/libssl.so.1.1 /usr/lib/$(uname -m)-linux-gnu/ && \ - mv /usr/lib/linux-gnu/libcrypto.so.1.1 /usr/lib/$(uname -m)-linux-gnu/ && \ +COPY --from=redis /usr/lib/linux-gnu/libssl.so.3 /usr/lib/linux-gnu/ +COPY --from=redis /usr/lib/linux-gnu/libcrypto.so.3 /usr/lib/linux-gnu/ +RUN mv /usr/lib/linux-gnu/libssl.so.3 /usr/lib/$(uname -m)-linux-gnu/ && \ + mv /usr/lib/linux-gnu/libcrypto.so.3 /usr/lib/$(uname -m)-linux-gnu/ && \ rm -rf /usr/lib/linux-gnu/ # Copy registry binaries to the image @@ -84,7 +85,7 @@ COPY --from=registry /etc/docker/registry/config.yml /etc/docker/registry/config # Copy node binaries COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules COPY --from=node /usr/local/bin/node /usr/local/bin -COPY --from=node /opt/yarn-v1.22.4 /opt/yarn-v1.22.4 +COPY --from=node /opt/yarn-v1.22.19 /opt/yarn-v1.22.19 # Entrypoint is required for container's user management COPY ./test/container/entrypoint.sh /usr/local/bin @@ -95,6 +96,7 @@ ARG UID RUN useradd -l -u ${UID} -d /home/user -s /bin/bash user && \ echo "user ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/user && \ mkdir -p /home/user/.kube && \ + mkdir -p /home/user/.cache && \ chown -R user /home/user && \ chgrp -R user /home/user && \ HOME=/home/user git config --global user.name "ArgoCD Test User" && \ @@ -106,12 +108,11 @@ RUN useradd -l -u ${UID} -d /home/user -s /bin/bash user && \ chown root /etc/ssh/ssh_host_*_key* && \ chmod 0600 /etc/ssh/ssh_host_*_key && \ mkdir -p /tmp/go-build-cache && \ - mkdir -p /home/user/.cache && \ ln -s /usr/local/bin/node /usr/local/bin/nodejs && \ ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \ ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx && \ - ln -s /opt/yarn-v1.22.4/bin/yarn /usr/local/bin/yarn && \ - ln -s /opt/yarn-v1.22.4/bin/yarnpkg /usr/local/bin/yarnpkg && \ + ln -s /opt/yarn-v1.22.19/bin/yarn /usr/local/bin/yarn && \ + ln -s /opt/yarn-v1.22.19/bin/yarnpkg /usr/local/bin/yarnpkg && \ mkdir -p /var/lib/registry ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/test/container/Procfile b/test/container/Procfile index bc03df97bd53a..3ec9add44d5a7 100644 --- a/test/container/Procfile +++ b/test/container/Procfile @@ -1,8 +1,8 @@ -controller: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}" -api-server: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} " -dex: sh -c "test $ARGOCD_IN_CI = true && exit 0; ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.30.0 serve /dex.yaml" +controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}" +api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} " +dex: sh -c "test $ARGOCD_IN_CI = true && exit 0; ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.38.0 serve /dex.yaml" redis: sh -c "/usr/local/bin/redis-server --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}" -repo-server: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_BINARY_NAME=argocd-repo-server $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}" +repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_BINARY_NAME=argocd-repo-server $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}" ui: sh -c "test $ARGOCD_IN_CI = true && exit 0; cd ui && ARGOCD_E2E_YARN_HOST=0.0.0.0 ${ARGOCD_E2E_YARN_CMD:-yarn} start" reaper: ./test/container/reaper.sh sshd: sudo sh -c "test $ARGOCD_E2E_TEST = true && /usr/sbin/sshd -p 2222 -D -e" @@ -10,5 +10,5 @@ fcgiwrap: sudo sh -c "test $ARGOCD_E2E_TEST = true && (fcgiwrap -s unix:/var/run nginx: sudo sh -c "test $ARGOCD_E2E_TEST = true && nginx -g 'daemon off;' -c $(pwd)/test/fixture/testrepos/nginx.conf" helm-registry: sudo sh -c "registry serve /etc/docker/registry/config.yml" dev-mounter: test "$ARGOCD_E2E_TEST" != "true" && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} -applicationset-controller: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_ASK_PASS_SOCK=/tmp/applicationset-ask-pass.sock ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}" +applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}" notification: sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications go run ./cmd/main.go --loglevel debug" diff --git a/test/e2e/accounts_test.go b/test/e2e/accounts_test.go index f794dce7a56e9..54eba790af2c5 100644 --- a/test/e2e/accounts_test.go +++ b/test/e2e/accounts_test.go @@ -14,7 +14,6 @@ import ( "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" "github.com/argoproj/argo-cd/v2/pkg/apiclient/account" "github.com/argoproj/argo-cd/v2/pkg/apiclient/session" - "github.com/argoproj/argo-cd/v2/test/e2e/fixture" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture" accountFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/account" "github.com/argoproj/argo-cd/v2/util/io" @@ -77,7 +76,7 @@ func TestCanIGetLogsAllowSwitchOn(t *testing.T) { When(). Create(). Login(). - SetPermissions([]fixture.ACL{ + SetPermissions([]ACL{ { Resource: "logs", Action: "get", diff --git a/test/e2e/api_versions_test.go b/test/e2e/api_versions_test.go new file mode 100644 index 0000000000000..9ef2ba01561d6 --- /dev/null +++ b/test/e2e/api_versions_test.go @@ -0,0 +1,57 @@ +package e2e + +import ( + "testing" + + . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" +) + +func TestAppSyncWrongVersion(t *testing.T) { + // Make sure the error messages are good when there are group or version mismatches between CRDs and resources. + ctx := Given(t) + ctx. + Path("crd-version-differences"). + When(). + CreateApp(). + // Install CRD and one instance of it on v1alpha1 + AppSet("--directory-include", "crd-v1alpha1.yaml"). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + When(). + AppSet("--directory-include", "crd-v1alpha2-instance.yaml"). + IgnoreErrors(). // Ignore errors because we are testing the error message. + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + When(). + DoNotIgnoreErrors(). + Get(). + Then(). + // Technically it's a "success" because we're just doing a "get," but the get output contains the error message. + Expect(SuccessRegex(`The Kubernetes API could not find version "v1alpha2" of argoproj\.io/Fake for requested resource [a-z0-9-]+/fake-crd-instance\. Version "v1alpha1" of argoproj\.io/Fake is installed on the destination cluster\.`)). + When(). + AppSet("--directory-include", "crd-wronggroup-instance.yaml", "--directory-exclude", "crd-v1alpha2-instance.yaml"). + IgnoreErrors(). // Ignore errors because we are testing the error message. + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + When(). + DoNotIgnoreErrors(). + Get(). + Then(). + Expect(SuccessRegex(`The Kubernetes API could not find version "v1alpha1" of wrong\.group/Fake for requested resource [a-z0-9-]+/fake-crd-instance-wronggroup\. Version "v1alpha1" of argoproj\.io/Fake is installed on the destination cluster\.`)). + When(). + AppSet("--directory-include", "crd-does-not-exist-instance.yaml", "--directory-exclude", "crd-wronggroup-instance.yaml"). + IgnoreErrors(). // Ignore errors because we are testing the error message. + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + When(). + DoNotIgnoreErrors(). + Get(). + Then(). + // Not the best error message, but good enough. + Expect(Success(`DoesNotExist.argoproj.io "" not found`)) +} diff --git a/test/e2e/app_deletion_test.go b/test/e2e/app_deletion_test.go index 1194edcb37df3..9158dddffa06a 100644 --- a/test/e2e/app_deletion_test.go +++ b/test/e2e/app_deletion_test.go @@ -67,3 +67,18 @@ func TestDeletingAppByLabel(t *testing.T) { // delete is successful Expect(DoesNotExist()) } + +func TestDeletingAppByLabelWait(t *testing.T) { + Given(t). + Path(guestbookPath). + When(). + CreateApp("--label=foo=bar"). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCode(SyncStatusCodeSynced))). + When(). + DeleteBySelectorWithWait("foo=bar"). + Then(). + // delete is successful + Expect(DoesNotExistNow()) +} diff --git a/test/e2e/app_management_ns_test.go b/test/e2e/app_management_ns_test.go index f9f3a71282644..32636e2b52c49 100644 --- a/test/e2e/app_management_ns_test.go +++ b/test/e2e/app_management_ns_test.go @@ -36,6 +36,7 @@ import ( repoFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/repos" "github.com/argoproj/argo-cd/v2/test/e2e/testdata" + "github.com/argoproj/argo-cd/v2/pkg/apis/application" . "github.com/argoproj/argo-cd/v2/util/argo" . "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/io" @@ -89,7 +90,7 @@ func TestNamespacedGetLogsDenySwitchOn(t *testing.T) { Then(). Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { - _, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") + _, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") assert.Error(t, err) assert.Contains(t, err.Error(), "permission denied") }) @@ -143,17 +144,17 @@ func TestNamespacedGetLogsAllowSwitchOnNS(t *testing.T) { Then(). Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { - out, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") + out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { - out, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Pod") + out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Pod") assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { - out, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Service") + out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Service") assert.NoError(t, err) assert.NotContains(t, out, "Hi") }) @@ -202,17 +203,17 @@ func TestNamespacedGetLogsAllowSwitchOff(t *testing.T) { Then(). Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { - out, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") + out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { - out, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Pod") + out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Pod") assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { - out, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Service") + out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Service") assert.NoError(t, err) assert.NotContains(t, out, "Hi") }) @@ -429,7 +430,9 @@ func TestNamespacedInvalidAppProject(t *testing.T) { IgnoreErrors(). CreateApp(). Then(). - Expect(Error("", "application references project does-not-exist which does not exist")) + // We're not allowed to infer whether the project exists based on this error message. Instead, we get a generic + // permission denied error. + Expect(Error("", "permission denied")) } func TestNamespacedAppDeletion(t *testing.T) { @@ -745,7 +748,7 @@ func TestNamespacedResourceDiffing(t *testing.T) { Then(). Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). And(func(app *Application) { - diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local", "testdata/guestbook") + diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local-repo-root", ".", "--local", "testdata/guestbook") assert.Error(t, err) assert.Contains(t, diffOutput, fmt.Sprintf("===== apps/Deployment %s/guestbook-ui ======", DeploymentNamespace())) }). @@ -758,7 +761,7 @@ func TestNamespacedResourceDiffing(t *testing.T) { Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { - diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local", "testdata/guestbook") + diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local-repo-root", ".", "--local", "testdata/guestbook") assert.NoError(t, err) assert.Empty(t, diffOutput) }). @@ -834,7 +837,7 @@ func TestNamespacedResourceDiffing(t *testing.T) { // } func TestNamespacedKnownTypesInCRDDiffing(t *testing.T) { - dummiesGVR := schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "dummies"} + dummiesGVR := schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: "dummies"} ctx := Given(t) ctx. @@ -894,7 +897,7 @@ func testNSEdgeCasesApplicationResources(t *testing.T, appPath string, statusCod expect. Expect(HealthIs(statusCode)). And(func(app *Application) { - diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local", path.Join("testdata", appPath)) + diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local-repo-root", ".", "--local", path.Join("testdata", appPath)) assert.Empty(t, diffOutput) assert.NoError(t, err) }) @@ -995,7 +998,7 @@ func TestNamespacedLocalManifestSync(t *testing.T) { Given(). LocalPath(guestbookPathLocal). When(). - Sync(). + Sync("--local-repo-root", "."). Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { @@ -1063,7 +1066,7 @@ func TestNamespacedLocalSyncDryRunWithASEnabled(t *testing.T) { assert.NoError(t, err) appBefore := app.DeepCopy() - _, err = RunCli("app", "sync", app.QualifiedName(), "--dry-run", "--local", guestbookPathLocal) + _, err = RunCli("app", "sync", app.QualifiedName(), "--dry-run", "--local-repo-root", ".", "--local", guestbookPathLocal) assert.NoError(t, err) appAfter := app.DeepCopy() @@ -1176,7 +1179,7 @@ func TestNamespacedPermissions(t *testing.T) { Create() sourceError := fmt.Sprintf("application repo %s is not permitted in project 'argo-project'", RepoURL(RepoURLTypeFile)) - destinationError := fmt.Sprintf("application destination {%s %s} is not permitted in project 'argo-project'", KubernetesInternalAPIServerAddr, DeploymentNamespace()) + destinationError := fmt.Sprintf("application destination server '%s' and namespace '%s' do not match any of the allowed destinations in project 'argo-project'", KubernetesInternalAPIServerAddr, DeploymentNamespace()) appCtx. Path("guestbook-logs"). @@ -1480,7 +1483,7 @@ func TestNamespacedOrphanedResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true)}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true)}, SourceNamespaces: []string{AppNamespace()}, }). SetTrackingMethod("annotation"). @@ -1512,7 +1515,7 @@ func TestNamespacedOrphanedResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true), Ignore: []OrphanedResourceKey{{Group: "Test", Kind: "ConfigMap"}}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Group: "Test", Kind: "ConfigMap"}}}, SourceNamespaces: []string{AppNamespace()}, }). When(). @@ -1528,7 +1531,7 @@ func TestNamespacedOrphanedResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap"}}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap"}}}, SourceNamespaces: []string{AppNamespace()}, }). When(). @@ -1545,7 +1548,7 @@ func TestNamespacedOrphanedResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap", Name: "orphaned-configmap"}}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap", Name: "orphaned-configmap"}}}, SourceNamespaces: []string{AppNamespace()}, }). When(). @@ -1603,15 +1606,15 @@ func TestNamespacedNotPermittedResources(t *testing.T) { }, } defer func() { - log.Infof("Ingress 'sample-ingress' deleted from %s", ArgoCDNamespace) - CheckError(KubeClientset.NetworkingV1().Ingresses(ArgoCDNamespace).Delete(context.Background(), "sample-ingress", metav1.DeleteOptions{})) + log.Infof("Ingress 'sample-ingress' deleted from %s", TestNamespace()) + CheckError(KubeClientset.NetworkingV1().Ingresses(TestNamespace()).Delete(context.Background(), "sample-ingress", metav1.DeleteOptions{})) }() svc := &v1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "guestbook-ui", Annotations: map[string]string{ - common.AnnotationKeyAppInstance: fmt.Sprintf("%s_%s:Service:%s/guesbook-ui", ArgoCDNamespace, ctx.AppQualifiedName(), DeploymentNamespace()), + common.AnnotationKeyAppInstance: fmt.Sprintf("%s_%s:Service:%s/guesbook-ui", TestNamespace(), ctx.AppQualifiedName(), DeploymentNamespace()), }, }, Spec: v1.ServiceSpec{ @@ -1633,7 +1636,7 @@ func TestNamespacedNotPermittedResources(t *testing.T) { {Group: "", Kind: "Service"}, }}). And(func() { - FailOnErr(KubeClientset.NetworkingV1().Ingresses(ArgoCDNamespace).Create(context.Background(), ingress, metav1.CreateOptions{})) + FailOnErr(KubeClientset.NetworkingV1().Ingresses(TestNamespace()).Create(context.Background(), ingress, metav1.CreateOptions{})) FailOnErr(KubeClientset.CoreV1().Services(DeploymentNamespace()).Create(context.Background(), svc, metav1.CreateOptions{})) }). Path(guestbookPath). @@ -1659,7 +1662,7 @@ func TestNamespacedNotPermittedResources(t *testing.T) { Expect(DoesNotExist()) // Make sure prohibited resources are not deleted during application deletion - FailOnErr(KubeClientset.NetworkingV1().Ingresses(ArgoCDNamespace).Get(context.Background(), "sample-ingress", metav1.GetOptions{})) + FailOnErr(KubeClientset.NetworkingV1().Ingresses(TestNamespace()).Get(context.Background(), "sample-ingress", metav1.GetOptions{})) FailOnErr(KubeClientset.CoreV1().Services(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})) } @@ -1767,7 +1770,7 @@ func TestNamespacedListResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true)}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true)}, SourceNamespaces: []string{AppNamespace()}, }). Path(guestbookPath). @@ -1860,7 +1863,7 @@ func TestNamespacedNamespaceAutoCreation(t *testing.T) { Then(). Expect(Success("")). And(func(app *Application) { - //Verify delete app does not delete the namespace auto created + // Verify delete app does not delete the namespace auto created output, err := Run("", "kubectl", "get", "namespace", updatedNamespace) assert.NoError(t, err) assert.Contains(t, output, updatedNamespace) @@ -2086,8 +2089,8 @@ metadata: delete(ns.Annotations, "argocd.argoproj.io/tracking-id") delete(ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration") - assert.Equal(t, map[string]string{"test": "true", "foo": "bar"}, ns.Labels) - assert.Equal(t, map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true", "something": "whatevs", "bar": "bat"}, ns.Annotations) + assert.Equal(t, map[string]string{"foo": "bar"}, ns.Labels) + assert.Equal(t, map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true", "bar": "bat"}, ns.Annotations) })). When(). And(func() { @@ -2106,7 +2109,7 @@ metadata: delete(ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration") delete(ns.Annotations, "argocd.argoproj.io/tracking-id") - assert.Equal(t, map[string]string{"test": "true", "foo": "bar"}, ns.Labels) + assert.Equal(t, map[string]string{"foo": "bar"}, ns.Labels) assert.Equal(t, map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true", "something": "hmm", "bar": "bat"}, ns.Annotations) assert.Equal(t, map[string]string{"something": "hmm", "bar": "bat"}, app.Spec.SyncPolicy.ManagedNamespaceMetadata.Annotations) })). @@ -2127,7 +2130,7 @@ metadata: delete(ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration") delete(ns.Annotations, "argocd.argoproj.io/tracking-id") - assert.Equal(t, map[string]string{"test": "true", "foo": "bar"}, ns.Labels) + assert.Equal(t, map[string]string{"foo": "bar"}, ns.Labels) assert.Equal(t, map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true", "bar": "bat"}, ns.Annotations) assert.Equal(t, map[string]string{"bar": "bat"}, app.Spec.SyncPolicy.ManagedNamespaceMetadata.Annotations) })). @@ -2306,17 +2309,17 @@ func TestNamespacedAppLogs(t *testing.T) { Then(). Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { - out, err := RunCli("app", "logs", app.QualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") + out, err := RunCliWithRetry(5, "app", "logs", app.QualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { - out, err := RunCli("app", "logs", app.QualifiedName(), "--kind", "Pod") + out, err := RunCliWithRetry(5, "app", "logs", app.QualifiedName(), "--kind", "Pod") assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { - out, err := RunCli("app", "logs", app.QualifiedName(), "--kind", "Service") + out, err := RunCliWithRetry(5, "app", "logs", app.QualifiedName(), "--kind", "Service") assert.NoError(t, err) assert.NotContains(t, out, "Hi") }) @@ -2445,6 +2448,7 @@ func TestNamespacedDisableManifestGeneration(t *testing.T) { }). When(). And(func() { + time.Sleep(3 * time.Second) SetEnableManifestGeneration(map[ApplicationSourceType]bool{ ApplicationSourceTypeKustomize: false, }) diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index a1152d0f6495b..10b2cf926723c 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -41,6 +41,8 @@ import ( . "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/io" "github.com/argoproj/argo-cd/v2/util/settings" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) const ( @@ -48,6 +50,8 @@ const ( guestbookPathLocal = "./testdata/guestbook_local" globalWithNoNameSpace = "global-with-no-namespace" guestbookWithNamespace = "guestbook-with-namespace" + resourceActions = "resource-actions" + appLogsRetryCount = 5 ) // This empty test is here only for clarity, to conform to logs rbac tests structure in account. This exact usecase is covered in the TestAppLogs test @@ -94,7 +98,7 @@ func TestGetLogsDenySwitchOn(t *testing.T) { Then(). Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { - _, err := RunCli("app", "logs", app.Name, "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") + _, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") assert.Error(t, err) assert.Contains(t, err.Error(), "permission denied") }) @@ -145,17 +149,17 @@ func TestGetLogsAllowSwitchOn(t *testing.T) { Then(). Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { - out, err := RunCli("app", "logs", app.Name, "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") + out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { - out, err := RunCli("app", "logs", app.Name, "--kind", "Pod") + out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Pod") assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { - out, err := RunCli("app", "logs", app.Name, "--kind", "Service") + out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Service") assert.NoError(t, err) assert.NotContains(t, out, "Hi") }) @@ -202,17 +206,17 @@ func TestGetLogsAllowSwitchOff(t *testing.T) { Then(). Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { - out, err := RunCli("app", "logs", app.Name, "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") + out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { - out, err := RunCli("app", "logs", app.Name, "--kind", "Pod") + out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Pod") assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { - out, err := RunCli("app", "logs", app.Name, "--kind", "Service") + out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Service") assert.NoError(t, err) assert.NotContains(t, out, "Hi") }) @@ -249,11 +253,30 @@ func TestSyncToSignedCommitWithoutKnownKey(t *testing.T) { Expect(HealthIs(health.HealthStatusMissing)) } -func TestSyncToSignedCommitKeyWithKnownKey(t *testing.T) { +func TestSyncToSignedCommitWithKnownKey(t *testing.T) { + SkipOnEnv(t, "GPG") + Given(t). + Project("gpg"). + Path(guestbookPath). + GPGPublicKeyAdded(). + Sleep(2). + When(). + AddSignedFile("test.yaml", "null"). + IgnoreErrors(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)) +} + +func TestSyncToSignedBranchWithKnownKey(t *testing.T) { SkipOnEnv(t, "GPG") Given(t). Project("gpg"). Path(guestbookPath). + Revision("master"). GPGPublicKeyAdded(). Sleep(2). When(). @@ -267,6 +290,98 @@ func TestSyncToSignedCommitKeyWithKnownKey(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)) } +func TestSyncToSignedBranchWithUnknownKey(t *testing.T) { + SkipOnEnv(t, "GPG") + Given(t). + Project("gpg"). + Path(guestbookPath). + Revision("master"). + Sleep(2). + When(). + AddSignedFile("test.yaml", "null"). + IgnoreErrors(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationError)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(HealthIs(health.HealthStatusMissing)) +} + +func TestSyncToUnsignedBranch(t *testing.T) { + SkipOnEnv(t, "GPG") + Given(t). + Project("gpg"). + Revision("master"). + Path(guestbookPath). + GPGPublicKeyAdded(). + Sleep(2). + When(). + IgnoreErrors(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationError)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(HealthIs(health.HealthStatusMissing)) +} + +func TestSyncToSignedTagWithKnownKey(t *testing.T) { + SkipOnEnv(t, "GPG") + Given(t). + Project("gpg"). + Revision("signed-tag"). + Path(guestbookPath). + GPGPublicKeyAdded(). + Sleep(2). + When(). + AddSignedTag("signed-tag"). + IgnoreErrors(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)) +} + +func TestSyncToSignedTagWithUnknownKey(t *testing.T) { + SkipOnEnv(t, "GPG") + Given(t). + Project("gpg"). + Revision("signed-tag"). + Path(guestbookPath). + Sleep(2). + When(). + AddSignedTag("signed-tag"). + IgnoreErrors(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationError)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(HealthIs(health.HealthStatusMissing)) +} + +func TestSyncToUnsignedTag(t *testing.T) { + SkipOnEnv(t, "GPG") + Given(t). + Project("gpg"). + Revision("unsigned-tag"). + Path(guestbookPath). + GPGPublicKeyAdded(). + Sleep(2). + When(). + AddTag("unsigned-tag"). + IgnoreErrors(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationError)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(HealthIs(health.HealthStatusMissing)) +} + func TestAppCreation(t *testing.T) { ctx := Given(t) ctx. @@ -298,7 +413,7 @@ func TestAppCreation(t *testing.T) { When(). // ensure that update replaces spec and merge labels and annotations And(func() { - FailOnErr(AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Patch(context.Background(), + FailOnErr(AppClientset.ArgoprojV1alpha1().Applications(TestNamespace()).Patch(context.Background(), ctx.GetName(), types.MergePatchType, []byte(`{"metadata": {"labels": { "test": "label" }, "annotations": { "test": "annotation" }}}`), metav1.PatchOptions{})) }). CreateApp("--upsert"). @@ -361,6 +476,24 @@ func TestDeleteAppResource(t *testing.T) { Expect(HealthIs(health.HealthStatusMissing)) } +// Fix for issue #2677, support PATCH in HTTP service +func TestPatchHttp(t *testing.T) { + ctx := Given(t) + + ctx. + Path(guestbookPath). + When(). + CreateApp(). + Sync(). + PatchAppHttp(`{"metadata": {"labels": { "test": "patch" }, "annotations": { "test": "patch" }}}`). + Then(). + And(func(app *Application) { + assert.Equal(t, "patch", app.Labels["test"]) + assert.Equal(t, "patch", app.Annotations["test"]) + }) + +} + // demonstrate that we cannot use a standard sync when an immutable field is changed, we must use "force" func TestImmutableChange(t *testing.T) { SkipOnEnv(t, "OPENSHIFT") @@ -412,7 +545,9 @@ func TestInvalidAppProject(t *testing.T) { IgnoreErrors(). CreateApp(). Then(). - Expect(Error("", "application references project does-not-exist which does not exist")) + // We're not allowed to infer whether the project exists based on this error message. Instead, we get a generic + // permission denied error. + Expect(Error("", "permission denied")) } func TestAppDeletion(t *testing.T) { @@ -503,7 +638,7 @@ func TestAppRollbackSuccessful(t *testing.T) { }} patch, _, err := diff.CreateTwoWayMergePatch(app, appWithHistory, &Application{}) require.NoError(t, err) - app, err = AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{}) + app, err = AppClientset.ArgoprojV1alpha1().Applications(TestNamespace()).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{}) require.NoError(t, err) // sync app and make sure it reaches InSync state @@ -812,7 +947,7 @@ func TestCRDs(t *testing.T) { } func TestKnownTypesInCRDDiffing(t *testing.T) { - dummiesGVR := schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "dummies"} + dummiesGVR := schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: "dummies"} Given(t). Path("crd-creation"). @@ -878,7 +1013,7 @@ definitions: obj.metadata.labels.sample = 'test' return obj` -func TestResourceAction(t *testing.T) { +func TestOldStyleResourceAction(t *testing.T) { Given(t). Path(guestbookPath). ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": {Actions: actionsConfig}}). @@ -920,6 +1055,224 @@ func TestResourceAction(t *testing.T) { }) } +const newStyleActionsConfig = `discovery.lua: return { sample = {} } +definitions: +- name: sample + action.lua: | + local os = require("os") + + function deepCopy(object) + local lookup_table = {} + local function _copy(obj) + if type(obj) ~= "table" then + return obj + elseif lookup_table[obj] then + return lookup_table[obj] + elseif next(obj) == nil then + return nil + else + local new_table = {} + lookup_table[obj] = new_table + for key, value in pairs(obj) do + new_table[_copy(key)] = _copy(value) + end + return setmetatable(new_table, getmetatable(obj)) + end + end + return _copy(object) + end + + job = {} + job.apiVersion = "batch/v1" + job.kind = "Job" + + job.metadata = {} + job.metadata.name = obj.metadata.name .. "-123" + job.metadata.namespace = obj.metadata.namespace + + ownerRef = {} + ownerRef.apiVersion = obj.apiVersion + ownerRef.kind = obj.kind + ownerRef.name = obj.metadata.name + ownerRef.uid = obj.metadata.uid + job.metadata.ownerReferences = {} + job.metadata.ownerReferences[1] = ownerRef + + job.spec = {} + job.spec.suspend = false + job.spec.template = {} + job.spec.template.spec = deepCopy(obj.spec.jobTemplate.spec.template.spec) + + impactedResource = {} + impactedResource.operation = "create" + impactedResource.resource = job + result = {} + result[1] = impactedResource + + return result` + +func TestNewStyleResourceActionPermitted(t *testing.T) { + Given(t). + Path(resourceActions). + ResourceOverrides(map[string]ResourceOverride{"batch/CronJob": {Actions: newStyleActionsConfig}}). + ProjectSpec(AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, + NamespaceResourceWhitelist: []metav1.GroupKind{ + {Group: "batch", Kind: "Job"}, + {Group: "batch", Kind: "CronJob"}, + }}). + When(). + CreateApp(). + Sync(). + Then(). + And(func(app *Application) { + + closer, client, err := ArgoCDClientset.NewApplicationClient() + assert.NoError(t, err) + defer io.Close(closer) + + actions, err := client.ListResourceActions(context.Background(), &applicationpkg.ApplicationResourceRequest{ + Name: &app.Name, + Group: pointer.String("batch"), + Kind: pointer.String("CronJob"), + Version: pointer.String("v1"), + Namespace: pointer.String(DeploymentNamespace()), + ResourceName: pointer.String("hello"), + }) + assert.NoError(t, err) + assert.Equal(t, []*ResourceAction{{Name: "sample", Disabled: false}}, actions.Actions) + + _, err = client.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{Name: &app.Name, + Group: pointer.String("batch"), + Kind: pointer.String("CronJob"), + Version: pointer.String("v1"), + Namespace: pointer.String(DeploymentNamespace()), + ResourceName: pointer.String("hello"), + Action: pointer.String("sample"), + }) + assert.NoError(t, err) + + _, err = KubeClientset.BatchV1().Jobs(DeploymentNamespace()).Get(context.Background(), "hello-123", metav1.GetOptions{}) + assert.NoError(t, err) + }) +} + +const newStyleActionsConfigMixedOk = `discovery.lua: return { sample = {} } +definitions: +- name: sample + action.lua: | + local os = require("os") + + function deepCopy(object) + local lookup_table = {} + local function _copy(obj) + if type(obj) ~= "table" then + return obj + elseif lookup_table[obj] then + return lookup_table[obj] + elseif next(obj) == nil then + return nil + else + local new_table = {} + lookup_table[obj] = new_table + for key, value in pairs(obj) do + new_table[_copy(key)] = _copy(value) + end + return setmetatable(new_table, getmetatable(obj)) + end + end + return _copy(object) + end + + job = {} + job.apiVersion = "batch/v1" + job.kind = "Job" + + job.metadata = {} + job.metadata.name = obj.metadata.name .. "-123" + job.metadata.namespace = obj.metadata.namespace + + ownerRef = {} + ownerRef.apiVersion = obj.apiVersion + ownerRef.kind = obj.kind + ownerRef.name = obj.metadata.name + ownerRef.uid = obj.metadata.uid + job.metadata.ownerReferences = {} + job.metadata.ownerReferences[1] = ownerRef + + job.spec = {} + job.spec.suspend = false + job.spec.template = {} + job.spec.template.spec = deepCopy(obj.spec.jobTemplate.spec.template.spec) + + impactedResource1 = {} + impactedResource1.operation = "create" + impactedResource1.resource = job + result = {} + result[1] = impactedResource1 + + obj.metadata.labels["aKey"] = 'aValue' + impactedResource2 = {} + impactedResource2.operation = "patch" + impactedResource2.resource = obj + + result[2] = impactedResource2 + + return result` + +func TestNewStyleResourceActionMixedOk(t *testing.T) { + Given(t). + Path(resourceActions). + ResourceOverrides(map[string]ResourceOverride{"batch/CronJob": {Actions: newStyleActionsConfigMixedOk}}). + ProjectSpec(AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, + NamespaceResourceWhitelist: []metav1.GroupKind{ + {Group: "batch", Kind: "Job"}, + {Group: "batch", Kind: "CronJob"}, + }}). + When(). + CreateApp(). + Sync(). + Then(). + And(func(app *Application) { + + closer, client, err := ArgoCDClientset.NewApplicationClient() + assert.NoError(t, err) + defer io.Close(closer) + + actions, err := client.ListResourceActions(context.Background(), &applicationpkg.ApplicationResourceRequest{ + Name: &app.Name, + Group: pointer.String("batch"), + Kind: pointer.String("CronJob"), + Version: pointer.String("v1"), + Namespace: pointer.String(DeploymentNamespace()), + ResourceName: pointer.String("hello"), + }) + assert.NoError(t, err) + assert.Equal(t, []*ResourceAction{{Name: "sample", Disabled: false}}, actions.Actions) + + _, err = client.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{Name: &app.Name, + Group: pointer.String("batch"), + Kind: pointer.String("CronJob"), + Version: pointer.String("v1"), + Namespace: pointer.String(DeploymentNamespace()), + ResourceName: pointer.String("hello"), + Action: pointer.String("sample"), + }) + assert.NoError(t, err) + + // Assert new Job was created + _, err = KubeClientset.BatchV1().Jobs(DeploymentNamespace()).Get(context.Background(), "hello-123", metav1.GetOptions{}) + assert.NoError(t, err) + // Assert the original CronJob was patched + cronJob, err := KubeClientset.BatchV1().CronJobs(DeploymentNamespace()).Get(context.Background(), "hello", metav1.GetOptions{}) + assert.Equal(t, "aValue", cronJob.Labels["aKey"]) + assert.NoError(t, err) + }) +} + func TestSyncResourceByLabel(t *testing.T) { Given(t). Path(guestbookPath). @@ -971,7 +1324,7 @@ func TestLocalManifestSync(t *testing.T) { Given(). LocalPath(guestbookPathLocal). When(). - Sync(). + Sync("--local-repo-root", "."). Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { @@ -1032,7 +1385,7 @@ func TestLocalSyncDryRunWithAutosyncEnabled(t *testing.T) { assert.NoError(t, err) appBefore := app.DeepCopy() - _, err = RunCli("app", "sync", app.Name, "--dry-run", "--local", guestbookPathLocal) + _, err = RunCli("app", "sync", app.Name, "--dry-run", "--local-repo-root", ".", "--local", guestbookPathLocal) assert.NoError(t, err) appAfter := app.DeepCopy() @@ -1137,7 +1490,7 @@ func TestPermissions(t *testing.T) { Create() sourceError := fmt.Sprintf("application repo %s is not permitted in project 'argo-project'", RepoURL(RepoURLTypeFile)) - destinationError := fmt.Sprintf("application destination {%s %s} is not permitted in project 'argo-project'", KubernetesInternalAPIServerAddr, DeploymentNamespace()) + destinationError := fmt.Sprintf("application destination server '%s' and namespace '%s' do not match any of the allowed destinations in project 'argo-project'", KubernetesInternalAPIServerAddr, DeploymentNamespace()) appCtx. Path("guestbook-logs"). @@ -1179,7 +1532,7 @@ func TestPermissions(t *testing.T) { And(func(app *Application) { closer, cdClient := ArgoCDClientset.NewApplicationClientOrDie() defer io.Close(closer) - appName, appNs := argo.ParseAppQualifiedName(app.Name, "") + appName, appNs := argo.ParseFromQualifiedName(app.Name, "") fmt.Printf("APP NAME: %s\n", appName) tree, err := cdClient.ResourceTree(context.Background(), &applicationpkg.ResourcesQuery{ApplicationName: &appName, AppNamespace: &appNs}) require.NoError(t, err) @@ -1293,7 +1646,7 @@ func TestPermissionDeniedWithNegatedNamespace(t *testing.T) { IgnoreErrors(). CreateApp(). Then(). - Expect(Error("", "is not permitted in project")) + Expect(Error("", "do not match any of the allowed destinations in project")) } func TestPermissionDeniedWithNegatedServer(t *testing.T) { @@ -1320,7 +1673,7 @@ func TestPermissionDeniedWithNegatedServer(t *testing.T) { IgnoreErrors(). CreateApp(). Then(). - Expect(Error("", "is not permitted in project")) + Expect(Error("", "do not match any of the allowed destinations in project")) } // make sure that if we deleted a resource from the app, it is not pruned if annotated with Prune=false @@ -1401,6 +1754,40 @@ func TestCompareOptionIgnoreExtraneous(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)) } +func TestSourceNamespaceCanBeMigratedToManagedNamespaceWithoutBeingPrunedOrOutOfSync(t *testing.T) { + Given(t). + Prune(true). + Path("guestbook-with-plain-namespace-manifest"). + When(). + PatchFile("guestbook-ui-namespace.yaml", fmt.Sprintf(`[{"op": "replace", "path": "/metadata/name", "value": "%s"}]`, DeploymentNamespace())). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + When(). + PatchApp(`[{ + "op": "add", + "path": "/spec/syncPolicy", + "value": { "prune": true, "syncOptions": ["PrunePropagationPolicy=foreground"], "managedNamespaceMetadata": { "labels": { "foo": "bar" } } } + }]`). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + assert.Equal(t, &ManagedNamespaceMetadata{Labels: map[string]string{"foo": "bar"}}, app.Spec.SyncPolicy.ManagedNamespaceMetadata) + }). + When(). + DeleteFile("guestbook-ui-namespace.yaml"). + Refresh(RefreshTypeHard). + Sync(). + Wait(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)) +} + func TestSelfManagedApps(t *testing.T) { Given(t). @@ -1477,7 +1864,7 @@ func TestOrphanedResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true)}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true)}, }). Path(guestbookPath). When(). @@ -1506,7 +1893,7 @@ func TestOrphanedResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true), Ignore: []OrphanedResourceKey{{Group: "Test", Kind: "ConfigMap"}}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Group: "Test", Kind: "ConfigMap"}}}, }). When(). Refresh(RefreshTypeNormal). @@ -1521,7 +1908,7 @@ func TestOrphanedResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap"}}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap"}}}, }). When(). Refresh(RefreshTypeNormal). @@ -1537,7 +1924,7 @@ func TestOrphanedResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap", Name: "orphaned-configmap"}}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap", Name: "orphaned-configmap"}}}, }). When(). Refresh(RefreshTypeNormal). @@ -1593,8 +1980,8 @@ func TestNotPermittedResources(t *testing.T) { }, } defer func() { - log.Infof("Ingress 'sample-ingress' deleted from %s", ArgoCDNamespace) - CheckError(KubeClientset.NetworkingV1().Ingresses(ArgoCDNamespace).Delete(context.Background(), "sample-ingress", metav1.DeleteOptions{})) + log.Infof("Ingress 'sample-ingress' deleted from %s", TestNamespace()) + CheckError(KubeClientset.NetworkingV1().Ingresses(TestNamespace()).Delete(context.Background(), "sample-ingress", metav1.DeleteOptions{})) }() svc := &v1.Service{ @@ -1622,7 +2009,7 @@ func TestNotPermittedResources(t *testing.T) { {Group: "", Kind: "Service"}, }}). And(func() { - FailOnErr(KubeClientset.NetworkingV1().Ingresses(ArgoCDNamespace).Create(context.Background(), ingress, metav1.CreateOptions{})) + FailOnErr(KubeClientset.NetworkingV1().Ingresses(TestNamespace()).Create(context.Background(), ingress, metav1.CreateOptions{})) FailOnErr(KubeClientset.CoreV1().Services(DeploymentNamespace()).Create(context.Background(), svc, metav1.CreateOptions{})) }). Path(guestbookPath). @@ -1648,7 +2035,7 @@ func TestNotPermittedResources(t *testing.T) { Expect(DoesNotExist()) // Make sure prohibited resources are not deleted during application deletion - FailOnErr(KubeClientset.NetworkingV1().Ingresses(ArgoCDNamespace).Get(context.Background(), "sample-ingress", metav1.GetOptions{})) + FailOnErr(KubeClientset.NetworkingV1().Ingresses(TestNamespace()).Get(context.Background(), "sample-ingress", metav1.GetOptions{})) FailOnErr(KubeClientset.CoreV1().Services(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})) } @@ -1687,7 +2074,7 @@ func TestCreateAppWithNoNameSpaceForGlobalResource(t *testing.T) { Then(). And(func(app *Application) { time.Sleep(500 * time.Millisecond) - app, err := AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Get(context.Background(), app.Name, metav1.GetOptions{}) + app, err := AppClientset.ArgoprojV1alpha1().Applications(TestNamespace()).Get(context.Background(), app.Name, metav1.GetOptions{}) assert.NoError(t, err) assert.Len(t, app.Status.Conditions, 0) }) @@ -1707,7 +2094,7 @@ func TestCreateAppWithNoNameSpaceWhenRequired(t *testing.T) { Refresh(RefreshTypeNormal). Then(). And(func(app *Application) { - updatedApp, err := AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Get(context.Background(), app.Name, metav1.GetOptions{}) + updatedApp, err := AppClientset.ArgoprojV1alpha1().Applications(TestNamespace()).Get(context.Background(), app.Name, metav1.GetOptions{}) require.NoError(t, err) assert.Len(t, updatedApp.Status.Conditions, 2) @@ -1731,7 +2118,7 @@ func TestCreateAppWithNoNameSpaceWhenRequired2(t *testing.T) { Refresh(RefreshTypeNormal). Then(). And(func(app *Application) { - updatedApp, err := AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Get(context.Background(), app.Name, metav1.GetOptions{}) + updatedApp, err := AppClientset.ArgoprojV1alpha1().Applications(TestNamespace()).Get(context.Background(), app.Name, metav1.GetOptions{}) require.NoError(t, err) assert.Len(t, updatedApp.Status.Conditions, 2) @@ -1746,7 +2133,7 @@ func TestListResource(t *testing.T) { ProjectSpec(AppProjectSpec{ SourceRepos: []string{"*"}, Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, - OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true)}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true)}, }). Path(guestbookPath). When(). @@ -1821,7 +2208,7 @@ func TestNamespaceAutoCreation(t *testing.T) { CreateApp("--sync-option", "CreateNamespace=true"). Then(). And(func(app *Application) { - //Make sure the namespace we are about to update to does not exist + // Make sure the namespace we are about to update to does not exist _, err := Run("", "kubectl", "get", "namespace", updatedNamespace) assert.Error(t, err) assert.Contains(t, err.Error(), "not found") @@ -1840,7 +2227,7 @@ func TestNamespaceAutoCreation(t *testing.T) { Then(). Expect(Success("")). And(func(app *Application) { - //Verify delete app does not delete the namespace auto created + // Verify delete app does not delete the namespace auto created output, err := Run("", "kubectl", "get", "namespace", updatedNamespace) assert.NoError(t, err) assert.Contains(t, output, updatedNamespace) @@ -2007,17 +2394,17 @@ func TestAppLogs(t *testing.T) { Then(). Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { - out, err := RunCli("app", "logs", app.Name, "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") + out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { - out, err := RunCli("app", "logs", app.Name, "--kind", "Pod") + out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Pod") assert.NoError(t, err) assert.Contains(t, out, "Hi") }). And(func(app *Application) { - out, err := RunCli("app", "logs", app.Name, "--kind", "Service") + out, err := RunCliWithRetry(appLogsRetryCount, "app", "logs", app.Name, "--kind", "Service") assert.NoError(t, err) assert.NotContains(t, out, "Hi") }) diff --git a/test/e2e/app_multiple_sources_test.go b/test/e2e/app_multiple_sources_test.go index f7bf00c143d9b..69290edf2a856 100644 --- a/test/e2e/app_multiple_sources_test.go +++ b/test/e2e/app_multiple_sources_test.go @@ -3,11 +3,12 @@ package e2e import ( "testing" + "github.com/stretchr/testify/assert" + . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" . "github.com/argoproj/argo-cd/v2/util/argo" - "github.com/stretchr/testify/assert" ) func TestMultiSourceAppCreation(t *testing.T) { @@ -24,7 +25,6 @@ func TestMultiSourceAppCreation(t *testing.T) { When(). CreateMultiSourceAppFromFile(). Then(). - Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). And(func(app *Application) { assert.Equal(t, Name(), app.Name) for i, source := range app.Spec.GetSources() { @@ -42,7 +42,8 @@ func TestMultiSourceAppCreation(t *testing.T) { assert.Contains(t, output, Name()) }). Expect(Success("")). - When().Refresh(RefreshTypeNormal).Then(). + Given().Timeout(60). + When().Wait().Then(). Expect(Success("")). And(func(app *Application) { statusByName := map[string]SyncStatusCode{} @@ -78,7 +79,6 @@ func TestMultiSourceAppWithHelmExternalValueFiles(t *testing.T) { When(). CreateMultiSourceAppFromFile(). Then(). - Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). And(func(app *Application) { assert.Equal(t, Name(), app.Name) for i, source := range app.Spec.GetSources() { @@ -96,7 +96,8 @@ func TestMultiSourceAppWithHelmExternalValueFiles(t *testing.T) { assert.Contains(t, output, Name()) }). Expect(Success("")). - When().Refresh(RefreshTypeNormal).Then(). + Given().Timeout(60). + When().Wait().Then(). Expect(Success("")). And(func(app *Application) { statusByName := map[string]SyncStatusCode{} @@ -126,7 +127,6 @@ func TestMultiSourceAppWithSourceOverride(t *testing.T) { When(). CreateMultiSourceAppFromFile(). Then(). - Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). And(func(app *Application) { assert.Equal(t, Name(), app.Name) for i, source := range app.Spec.GetSources() { @@ -144,7 +144,8 @@ func TestMultiSourceAppWithSourceOverride(t *testing.T) { assert.Contains(t, output, Name()) }). Expect(Success("")). - When().Refresh(RefreshTypeNormal).Then(). + Given().Timeout(60). + When().Wait().Then(). Expect(Success("")). And(func(app *Application) { statusByName := map[string]SyncStatusCode{} diff --git a/test/e2e/app_namespaces_test.go b/test/e2e/app_namespaces_test.go index d66cbaa1494ef..033c34e9a70d3 100644 --- a/test/e2e/app_namespaces_test.go +++ b/test/e2e/app_namespaces_test.go @@ -20,7 +20,7 @@ func TestAppCreationInOtherNamespace(t *testing.T) { ctx := Given(t) ctx. Path(guestbookPath). - SetAppNamespace(ArgoCDAppNamespace). + SetAppNamespace(AppNamespace()). When(). CreateApp(). Then(). @@ -83,7 +83,7 @@ func TestDeletingNamespacedAppStuckInSync(t *testing.T) { }) }). Async(true). - SetAppNamespace(ArgoCDAppNamespace). + SetAppNamespace(AppNamespace()). Path("hook-custom-health"). When(). CreateApp(). diff --git a/test/e2e/app_skipreconcile_test.go b/test/e2e/app_skipreconcile_test.go new file mode 100644 index 0000000000000..2ea28812756a8 --- /dev/null +++ b/test/e2e/app_skipreconcile_test.go @@ -0,0 +1,45 @@ +package e2e + +import ( + "testing" + + "github.com/argoproj/argo-cd/v2/common" + . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" +) + +func TestAppSkipReconcileTrue(t *testing.T) { + Given(t). + Path(guestbookPath). + When(). + // app should have no status + CreateFromFile(func(app *Application) { + app.Annotations = map[string]string{common.AnnotationKeyAppSkipReconcile: "true"} + }). + Then(). + Expect(NoStatus()) +} + +func TestAppSkipReconcileFalse(t *testing.T) { + Given(t). + Path(guestbookPath). + When(). + // app should have status + CreateFromFile(func(app *Application) { + app.Annotations = map[string]string{common.AnnotationKeyAppSkipReconcile: "false"} + }). + Then(). + Expect(StatusExists()) +} + +func TestAppSkipReconcileNonBooleanValue(t *testing.T) { + Given(t). + Path(guestbookPath). + When(). + // app should have status + CreateFromFile(func(app *Application) { + app.Annotations = map[string]string{common.AnnotationKeyAppSkipReconcile: "not a boolean value"} + }). + Then(). + Expect(StatusExists()) +} diff --git a/test/e2e/app_sync_options_test.go b/test/e2e/app_sync_options_test.go new file mode 100644 index 0000000000000..7d0a0ffeabb99 --- /dev/null +++ b/test/e2e/app_sync_options_test.go @@ -0,0 +1,61 @@ +package e2e + +import ( + "testing" + + "github.com/argoproj/gitops-engine/pkg/health" + . "github.com/argoproj/gitops-engine/pkg/sync/common" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + + . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + . "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" +) + +// Given application is set with --sync-option CreateNamespace=true and --sync-option ServerSideApply=true +// +// application --dest-namespace exists +// +// Then, --dest-namespace is created with server side apply +// application is synced and healthy with resource +// application resources created with server side apply in the newly created namespace. +func TestNamespaceCreationWithSSA(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") + namespace := "guestbook-ui-with-ssa" + defer func() { + if !t.Skipped() { + _, err := Run("", "kubectl", "delete", "namespace", namespace) + assert.NoError(t, err) + } + }() + + ctx := Given(t) + ctx. + SetAppNamespace(AppNamespace()). + Timeout(30). + Path("guestbook"). + When(). + CreateFromFile(func(app *Application) { + app.Spec.SyncPolicy = &SyncPolicy{ + SyncOptions: SyncOptions{"CreateNamespace=true", "ServerSideApply=true"}, + } + }). + Then(). + Expect(NoNamespace(namespace)). + When(). + AppSet("--dest-namespace", namespace). + Sync(). + Then(). + Expect(Success("")). + Expect(Namespace(namespace, func(app *Application, ns *v1.Namespace) { + assert.NotContains(t, ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration") + })). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", namespace, health.HealthStatusHealthy)). + Expect(ResourceSyncStatusWithNamespaceIs("Deployment", "guestbook-ui", namespace, SyncStatusCodeSynced)). + Expect(ResourceHealthWithNamespaceIs("Service", "guestbook-ui", namespace, health.HealthStatusHealthy)). + Expect(ResourceSyncStatusWithNamespaceIs("Service", "guestbook-ui", namespace, SyncStatusCodeSynced)) +} diff --git a/test/e2e/applicationset_test.go b/test/e2e/applicationset_test.go index 913eb73079de1..0d4d8ea3498f5 100644 --- a/test/e2e/applicationset_test.go +++ b/test/e2e/applicationset_test.go @@ -1,7 +1,9 @@ package e2e import ( + "fmt" "io" + "net" "net/http" "net/http/httptest" "strings" @@ -11,13 +13,19 @@ import ( corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + + "github.com/stretchr/testify/assert" + . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" . "github.com/argoproj/argo-cd/v2/util/errors" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) var ( @@ -40,10 +48,890 @@ var ( Message: "ApplicationSet up to date", Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate, }, - } -) + } +) + +func TestSimpleListGeneratorExternalNamespace(t *testing.T) { + + var externalNamespace = string(utils.ArgoCDExternalNamespace) + + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: externalNamespace, + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + } + var expectedAppNewNamespace *argov1alpha1.Application + var expectedAppNewMetadata *argov1alpha1.Application + + Given(t). + // Create a ListGenerator-based ApplicationSet + When(). + SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). + CreateNamespace(externalNamespace).Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator-external", + Namespace: externalNamespace, + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "{{.url}}", + Namespace: "guestbook", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), + }}, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). + + // Update the ApplicationSet template namespace, and verify it updates the Applications + When(). + And(func() { + expectedAppNewNamespace = expectedApp.DeepCopy() + expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). + + // Update the metadata fields in the appset template, and make sure it propagates to the apps + When(). + And(func() { + expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() + expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} + expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{ + "label-key": "label-value", + } + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} + appset.Spec.Template.Labels = map[string]string{ + "label-key": "label-value", + } + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + + // verify the ApplicationSet status conditions were set correctly + Expect(ApplicationSetHasConditions("simple-list-generator-external", ExpectedConditions)). + + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})) + +} + +func TestSimpleListGeneratorExternalNamespaceNoConflict(t *testing.T) { + + var externalNamespace = string(utils.ArgoCDExternalNamespace) + var externalNamespace2 = string(utils.ArgoCDExternalNamespace2) + + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: externalNamespace, + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + } + + expectedAppExternalNamespace2 := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: externalNamespace2, + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + } + + var expectedAppNewNamespace *argov1alpha1.Application + var expectedAppNewMetadata *argov1alpha1.Application + + Given(t). + // Create a ListGenerator-based ApplicationSet + When(). + SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2). + CreateNamespace(externalNamespace2).Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator-external", + Namespace: externalNamespace2, + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "{{.url}}", + Namespace: "guestbook", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), + }}, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedAppExternalNamespace2})). + When(). + SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). + CreateNamespace(externalNamespace).Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator-external", + Namespace: externalNamespace, + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "{{.url}}", + Namespace: "guestbook", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), + }}, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). + When(). + SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2). + Then(). + Expect(ApplicationsExist([]argov1alpha1.Application{expectedAppExternalNamespace2})). + When(). + SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). + Then(). + // Update the ApplicationSet template namespace, and verify it updates the Applications + When(). + And(func() { + expectedAppNewNamespace = expectedApp.DeepCopy() + expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). + When(). + SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2). + Then(). + Expect(ApplicationsExist([]argov1alpha1.Application{expectedAppExternalNamespace2})). + When(). + SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). + Then(). + // Update the metadata fields in the appset template, and make sure it propagates to the apps + When(). + And(func() { + expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() + expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} + expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{ + "label-key": "label-value", + } + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} + appset.Spec.Template.Labels = map[string]string{ + "label-key": "label-value", + } + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + + // verify the ApplicationSet status conditions were set correctly + Expect(ApplicationSetHasConditions("simple-list-generator-external", ExpectedConditions)). + When(). + SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2). + Then(). + Expect(ApplicationsExist([]argov1alpha1.Application{expectedAppExternalNamespace2})). + When(). + SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). + Then(). + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + When(). + SwitchToExternalNamespace(utils.ArgoCDExternalNamespace2). + Then(). + Expect(ApplicationsExist([]argov1alpha1.Application{expectedAppExternalNamespace2})). + When(). + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{expectedAppExternalNamespace2})) +} + +func TestSimpleListGenerator(t *testing.T) { + + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: fixture.TestNamespace(), + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + } + var expectedAppNewNamespace *argov1alpha1.Application + var expectedAppNewMetadata *argov1alpha1.Application + + Given(t). + // Create a ListGenerator-based ApplicationSet + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{cluster}}-guestbook"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "{{url}}", + Namespace: "guestbook", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), + }}, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). + + // Update the ApplicationSet template namespace, and verify it updates the Applications + When(). + And(func() { + expectedAppNewNamespace = expectedApp.DeepCopy() + expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). + + // Update the metadata fields in the appset template, and make sure it propagates to the apps + When(). + And(func() { + expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() + expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} + expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} + appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + + // verify the ApplicationSet status conditions were set correctly + Expect(ApplicationSetHasConditions("simple-list-generator", ExpectedConditions)). + + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})) + +} + +func TestSimpleListGeneratorGoTemplate(t *testing.T) { + + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: fixture.TestNamespace(), + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + } + var expectedAppNewNamespace *argov1alpha1.Application + var expectedAppNewMetadata *argov1alpha1.Application + + Given(t). + // Create a ListGenerator-based ApplicationSet + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator", + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "{{.url}}", + Namespace: "guestbook", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), + }}, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). + + // Update the ApplicationSet template namespace, and verify it updates the Applications + When(). + And(func() { + expectedAppNewNamespace = expectedApp.DeepCopy() + expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). + + // Update the metadata fields in the appset template, and make sure it propagates to the apps + When(). + And(func() { + expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() + expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} + expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} + appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + + // verify the ApplicationSet status conditions were set correctly + Expect(ApplicationSetHasConditions("simple-list-generator", ExpectedConditions)). + + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})) + +} + +func TestCreateApplicationDespiteParamsError(t *testing.T) { + expectedErrorMessage := `failed to execute go template {{.cluster}}-guestbook: template: :1:2: executing "" at <.cluster>: map has no entry for key "cluster"` + expectedConditionsParamsError := []v1alpha1.ApplicationSetCondition{ + { + Type: v1alpha1.ApplicationSetConditionErrorOccurred, + Status: v1alpha1.ApplicationSetConditionStatusTrue, + Message: expectedErrorMessage, + Reason: v1alpha1.ApplicationSetReasonRenderTemplateParamsError, + }, + { + Type: v1alpha1.ApplicationSetConditionParametersGenerated, + Status: v1alpha1.ApplicationSetConditionStatusFalse, + Message: expectedErrorMessage, + Reason: v1alpha1.ApplicationSetReasonErrorOccurred, + }, + { + Type: v1alpha1.ApplicationSetConditionResourcesUpToDate, + Status: v1alpha1.ApplicationSetConditionStatusFalse, + Message: expectedErrorMessage, + Reason: v1alpha1.ApplicationSetReasonRenderTemplateParamsError, + }, + } + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: fixture.TestNamespace(), + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + } + + Given(t). + // Create a ListGenerator-based ApplicationSet + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-list-generator", + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + GoTemplateOptions: []string{"missingkey=error"}, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "{{.url}}", + Namespace: "guestbook", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + { + Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), + }, + { + Raw: []byte(`{"invalidCluster": "invalid-cluster","url": "https://kubernetes.default.svc"}`), + }}, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). + + // verify the ApplicationSet status conditions were set correctly + Expect(ApplicationSetHasConditions("simple-list-generator", expectedConditionsParamsError)). + + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{expectedApp})) + +} + +func TestRenderHelmValuesObject(t *testing.T) { + + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: fixture.TestNamespace(), + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "helm-guestbook", + Helm: &argov1alpha1.ApplicationSourceHelm{ + ValuesObject: &runtime.RawExtension{ + // This will always be converted as yaml + Raw: []byte(`{"some":{"string":"Hello world"}}`), + }, + }, + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + } + + Given(t). + // Create a ListGenerator-based ApplicationSet + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "test-values-object", + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "helm-guestbook", + Helm: &argov1alpha1.ApplicationSourceHelm{ + ValuesObject: &runtime.RawExtension{ + Raw: []byte(`{"some":{"string":"{{.test}}"}}`), + }, + }, + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "{{.url}}", + Namespace: "guestbook", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "test": "Hello world"}`), + }}, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{expectedApp})) + +} + +func TestTemplatePatch(t *testing.T) { + + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: fixture.TestNamespace(), + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + Annotations: map[string]string{ + "annotation-some-key": "annotation-some-value", + }, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + SyncPolicy: &argov1alpha1.SyncPolicy{ + SyncOptions: argov1alpha1.SyncOptions{"CreateNamespace=true"}, + }, + }, + } + + templatePatch := `{ + "metadata": { + "annotations": { + {{- range $k, $v := .annotations }} + "{{ $k }}": "{{ $v }}" + {{- end }} + } + }, + {{- if .createNamespace }} + "spec": { + "syncPolicy": { + "syncOptions": [ + "CreateNamespace=true" + ] + } + } + {{- end }} + } + ` + + var expectedAppNewNamespace *argov1alpha1.Application + var expectedAppNewMetadata *argov1alpha1.Application + + Given(t). + // Create a ListGenerator-based ApplicationSet + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "patch-template", + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "{{.url}}", + Namespace: "guestbook", + }, + }, + }, + TemplatePatch: &templatePatch, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{ + "cluster": "my-cluster", + "url": "https://kubernetes.default.svc", + "createNamespace": true, + "annotations": { + "annotation-some-key": "annotation-some-value" + } + }`), + }}, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). + + // Update the ApplicationSet template namespace, and verify it updates the Applications + When(). + And(func() { + expectedAppNewNamespace = expectedApp.DeepCopy() + expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). + + // Update the metadata fields in the appset template, and make sure it propagates to the apps + When(). + And(func() { + expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() + expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{ + "label-key": "label-value", + } + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + + // verify the ApplicationSet status conditions were set correctly + Expect(ApplicationSetHasConditions("patch-template", ExpectedConditions)). + + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})) + +} + +func TestSyncPolicyCreateUpdate(t *testing.T) { + + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook-sync-policy-create-update", + Namespace: utils.ArgoCDNamespace, + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + } + var expectedAppNewNamespace *argov1alpha1.Application + var expectedAppNewMetadata *argov1alpha1.Application + + Given(t). + // Create a ListGenerator-based ApplicationSet + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "sync-policy-create-update", + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook-sync-policy-create-update"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "{{.url}}", + Namespace: "guestbook", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`), + }}, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). -func TestSimpleListGenerator(t *testing.T) { + // Update the ApplicationSet template namespace, and verify it updates the Applications + When(). + And(func() { + expectedAppNewNamespace = expectedApp.DeepCopy() + expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). + + // Update the metadata fields in the appset template + // Update as well the policy + // As policy is create-update, updates must reflected + When(). + And(func() { + expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() + expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} + expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{ + "label-key": "label-value", + } + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} + appset.Spec.Template.Labels = map[string]string{ + "label-key": "label-value", + } + applicationsSyncPolicy := argov1alpha1.ApplicationsSyncPolicyCreateUpdate + appset.Spec.SyncPolicy = &argov1alpha1.ApplicationSetSyncPolicy{ + ApplicationsSync: &applicationsSyncPolicy, + } + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + + // Update the list and remove element + // As policy is create-update, app deletion must not be reflected + When(). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Generators = []v1alpha1.ApplicationSetGenerator{} + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + + // verify the ApplicationSet status conditions were set correctly + Expect(ApplicationSetHasConditions("sync-policy-create-update", ExpectedConditions)). + + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})) + +} + +func TestSyncPolicyCreateDelete(t *testing.T) { expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ @@ -51,7 +939,7 @@ func TestSimpleListGenerator(t *testing.T) { APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ - Name: "my-cluster-guestbook", + Name: "my-cluster-guestbook-sync-policy-create-delete", Namespace: utils.ArgoCDNamespace, Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, @@ -69,16 +957,16 @@ func TestSimpleListGenerator(t *testing.T) { }, } var expectedAppNewNamespace *argov1alpha1.Application - var expectedAppNewMetadata *argov1alpha1.Application Given(t). // Create a ListGenerator-based ApplicationSet When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ - Name: "simple-list-generator", + Name: "sync-policy-create-delete", }, Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{cluster}}-guestbook"}, + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook-sync-policy-create-delete"}, Spec: argov1alpha1.ApplicationSpec{ Project: "default", Source: &argov1alpha1.ApplicationSource{ @@ -87,7 +975,7 @@ func TestSimpleListGenerator(t *testing.T) { Path: "guestbook", }, Destination: argov1alpha1.ApplicationDestination{ - Server: "{{url}}", + Server: "{{.url}}", Namespace: "guestbook", }, }, @@ -114,28 +1002,36 @@ func TestSimpleListGenerator(t *testing.T) { appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). - // Update the metadata fields in the appset template, and make sure it propagates to the apps + // Update the metadata fields in the appset template + // Update as well the policy + // As policy is create-delete, updates must not be reflected When(). - And(func() { - expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() - expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} - expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} - }). Update(func(appset *v1alpha1.ApplicationSet) { appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} - }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + applicationsSyncPolicy := argov1alpha1.ApplicationsSyncPolicyCreateDelete + appset.Spec.SyncPolicy = &argov1alpha1.ApplicationSetSyncPolicy{ + ApplicationsSync: &applicationsSyncPolicy, + } + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). + + // Update the list and remove element + // As policy is create-delete, app deletion must be reflected + When(). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Generators = []v1alpha1.ApplicationSetGenerator{} + }).Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewNamespace})). // verify the ApplicationSet status conditions were set correctly - Expect(ApplicationSetHasConditions("simple-list-generator", ExpectedConditions)). + Expect(ApplicationSetHasConditions("sync-policy-create-delete", ExpectedConditions)). - // Delete the ApplicationSet, and verify it deletes the Applications + // Delete the ApplicationSet When(). - Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})) + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewNamespace})) } -func TestSimpleListGeneratorGoTemplate(t *testing.T) { +func TestSyncPolicyCreateOnly(t *testing.T) { expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ @@ -143,7 +1039,7 @@ func TestSimpleListGeneratorGoTemplate(t *testing.T) { APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ - Name: "my-cluster-guestbook", + Name: "my-cluster-guestbook-sync-policy-create-only", Namespace: utils.ArgoCDNamespace, Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, @@ -161,17 +1057,16 @@ func TestSimpleListGeneratorGoTemplate(t *testing.T) { }, } var expectedAppNewNamespace *argov1alpha1.Application - var expectedAppNewMetadata *argov1alpha1.Application Given(t). // Create a ListGenerator-based ApplicationSet When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ - Name: "simple-list-generator", + Name: "sync-policy-create-only", }, Spec: v1alpha1.ApplicationSetSpec{ GoTemplate: true, Template: v1alpha1.ApplicationSetTemplate{ - ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook-sync-policy-create-only"}, Spec: argov1alpha1.ApplicationSpec{ Project: "default", Source: &argov1alpha1.ApplicationSource{ @@ -207,24 +1102,32 @@ func TestSimpleListGeneratorGoTemplate(t *testing.T) { appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). - // Update the metadata fields in the appset template, and make sure it propagates to the apps + // Update the metadata fields in the appset template + // Update as well the policy + // As policy is create-only, updates must not be reflected When(). - And(func() { - expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() - expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} - expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"} - }). Update(func(appset *v1alpha1.ApplicationSet) { appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} - }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + applicationsSyncPolicy := argov1alpha1.ApplicationsSyncPolicyCreateOnly + appset.Spec.SyncPolicy = &argov1alpha1.ApplicationSetSyncPolicy{ + ApplicationsSync: &applicationsSyncPolicy, + } + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). + + // Update the list and remove element + // As policy is create-only, app deletion must not be reflected + When(). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Generators = []v1alpha1.ApplicationSetGenerator{} + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). // verify the ApplicationSet status conditions were set correctly - Expect(ApplicationSetHasConditions("simple-list-generator", ExpectedConditions)). + Expect(ApplicationSetHasConditions("sync-policy-create-only", ExpectedConditions)). // Delete the ApplicationSet, and verify it deletes the Applications When(). - Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})) + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewNamespace})) } @@ -232,12 +1135,12 @@ func TestSimpleGitDirectoryGenerator(t *testing.T) { generateExpectedApp := func(name string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: name, - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -341,12 +1244,12 @@ func TestSimpleGitDirectoryGeneratorGoTemplate(t *testing.T) { generateExpectedApp := func(name string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: name, - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -452,12 +1355,12 @@ func TestSimpleGitFilesGenerator(t *testing.T) { generateExpectedApp := func(name string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: name, - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -561,12 +1464,12 @@ func TestSimpleGitFilesGeneratorGoTemplate(t *testing.T) { generateExpectedApp := func(name string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: name, - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -670,7 +1573,7 @@ func TestSimpleGitFilesPreserveResourcesOnDeletion(t *testing.T) { Given(t). When(). - CreateNamespace(). + CreateNamespace(utils.ApplicationsResourcesNamespace). // Create a GitGenerator-based ApplicationSet Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ Name: "simple-git-generator", @@ -687,7 +1590,7 @@ func TestSimpleGitFilesPreserveResourcesOnDeletion(t *testing.T) { }, Destination: argov1alpha1.ApplicationDestination{ Server: "https://kubernetes.default.svc", - Namespace: utils.ApplicationSetNamespace, + Namespace: utils.ApplicationsResourcesNamespace, }, // Automatically create resources @@ -730,7 +1633,7 @@ func TestSimpleGitFilesPreserveResourcesOnDeletionGoTemplate(t *testing.T) { Given(t). When(). - CreateNamespace(). + CreateNamespace(utils.ApplicationsResourcesNamespace). // Create a GitGenerator-based ApplicationSet Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ Name: "simple-git-generator", @@ -748,7 +1651,7 @@ func TestSimpleGitFilesPreserveResourcesOnDeletionGoTemplate(t *testing.T) { }, Destination: argov1alpha1.ApplicationDestination{ Server: "https://kubernetes.default.svc", - Namespace: utils.ApplicationSetNamespace, + Namespace: utils.ApplicationsResourcesNamespace, }, // Automatically create resources @@ -971,25 +1874,42 @@ func githubSCMMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) t.Fail() } default: - w.WriteHeader(404) + w.WriteHeader(http.StatusNotFound) } } } -func TestSimpleSCMProviderGenerator(t *testing.T) { +func testServerWithPort(t *testing.T, port int, handler http.Handler) *httptest.Server { // Use mocked API response to avoid rate-limiting. - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) + if err != nil { + t.Error(fmt.Errorf("Unable to start server %w", err)) + } + + ts := httptest.NewUnstartedServer(handler) + + ts.Listener.Close() + ts.Listener = l + + return ts +} + +func TestSimpleSCMProviderGenerator(t *testing.T) { + + ts := testServerWithPort(t, 8341, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { githubSCMMockHandler(t)(w, r) })) + ts.Start() + defer ts.Close() expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: "argo-cd-guestbook", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -1050,19 +1970,20 @@ func TestSimpleSCMProviderGenerator(t *testing.T) { } func TestSimpleSCMProviderGeneratorGoTemplate(t *testing.T) { - // Use mocked API response to avoid rate-limiting. - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ts := testServerWithPort(t, 8342, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { githubSCMMockHandler(t)(w, r) })) + ts.Start() + defer ts.Close() expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: "argo-cd-guestbook", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -1123,15 +2044,90 @@ func TestSimpleSCMProviderGeneratorGoTemplate(t *testing.T) { }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})) } +func TestSCMProviderGeneratorSCMProviderNotAllowed(t *testing.T) { + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "argo-cd-guestbook", + Namespace: fixture.TestNamespace(), + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "git@github.com:argoproj/argo-cd.git", + TargetRevision: "master", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + } + + // Because you can't &"". + repoMatch := "argo-cd" + + Given(t). + // Create an SCMProviderGenerator-based ApplicationSet + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "scm-provider-generator-scm-provider-not-allowed", + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{ .repository }}-guestbook"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "{{ .url }}", + TargetRevision: "{{ .branch }}", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + SCMProvider: &v1alpha1.SCMProviderGenerator{ + Github: &v1alpha1.SCMProviderGeneratorGithub{ + Organization: "argoproj", + API: "http://myservice.mynamespace.svc.cluster.local", + }, + Filters: []v1alpha1.SCMProviderGeneratorFilter{ + { + RepositoryMatch: &repoMatch, + }, + }, + }, + }, + }, + }, + }).Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{expectedApp})). + And(func() { + // app should be listed + output, err := fixture.RunCli("appset", "get", "scm-provider-generator-scm-provider-not-allowed") + assert.NoError(t, err) + assert.Contains(t, output, "scm provider not allowed") + }) +} + func TestCustomApplicationFinalizers(t *testing.T) { expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: "my-cluster-guestbook", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io/background"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -1192,12 +2188,12 @@ func TestCustomApplicationFinalizers(t *testing.T) { func TestCustomApplicationFinalizersGoTemplate(t *testing.T) { expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: "my-cluster-guestbook", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io/background"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -1269,6 +2265,10 @@ func githubPullMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request "name": "preview" } ], + "base": { + "ref": "master", + "sha": "7a4a5c987fdfb2b0629e9dbf5f31636c69ba4775" + }, "head": { "ref": "pull-request", "sha": "824a5c987fdfb2b0629e9dbf5f31636c69ba4772" @@ -1279,25 +2279,28 @@ func githubPullMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request t.Fail() } default: - w.WriteHeader(404) + w.WriteHeader(http.StatusNotFound) } } } func TestSimplePullRequestGenerator(t *testing.T) { - // Use mocked API response to avoid rate-limiting. - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + ts := testServerWithPort(t, 8343, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { githubPullMockHandler(t)(w, r) })) + ts.Start() + defer ts.Close() + expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: "guestbook-1", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -1360,19 +2363,21 @@ func TestSimplePullRequestGenerator(t *testing.T) { } func TestSimplePullRequestGeneratorGoTemplate(t *testing.T) { - // Use mocked API response to avoid rate-limiting. - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ts := testServerWithPort(t, 8344, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { githubPullMockHandler(t)(w, r) })) + ts.Start() + defer ts.Close() + expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: "guestbook-1", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, Labels: map[string]string{"app": "preview"}, }, @@ -1438,17 +2443,100 @@ func TestSimplePullRequestGeneratorGoTemplate(t *testing.T) { }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})) } +func TestPullRequestGeneratorNotAllowedSCMProvider(t *testing.T) { + + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "guestbook-1", + Namespace: fixture.TestNamespace(), + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + Labels: map[string]string{ + "app": "preview", + }, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git", + TargetRevision: "824a5c987fdfb2b0629e9dbf5f31636c69ba4772", + Path: "kustomize-guestbook", + Kustomize: &argov1alpha1.ApplicationSourceKustomize{ + NamePrefix: "guestbook-1", + }, + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook-pull-request", + }, + }, + } + + Given(t). + // Create an PullRequestGenerator-based ApplicationSet + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "pull-request-generator-not-allowed-scm", + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{ + Name: "guestbook-{{ .number }}", + Labels: map[string]string{"app": "{{index .labels 0}}"}}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "git@github.com:applicationset-test-org/argocd-example-apps.git", + TargetRevision: "{{ .head_sha }}", + Path: "kustomize-guestbook", + Kustomize: &argov1alpha1.ApplicationSourceKustomize{ + NamePrefix: "guestbook-{{ .number }}", + }, + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook-{{ .branch }}", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + PullRequest: &v1alpha1.PullRequestGenerator{ + Github: &v1alpha1.PullRequestGeneratorGithub{ + API: "http://myservice.mynamespace.svc.cluster.local", + Owner: "applicationset-test-org", + Repo: "argocd-example-apps", + Labels: []string{ + "preview", + }, + }, + }, + }, + }, + }, + }).Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{expectedApp})). + And(func() { + // app should be listed + output, err := fixture.RunCli("appset", "get", "pull-request-generator-not-allowed-scm") + assert.NoError(t, err) + assert.Contains(t, output, "scm provider not allowed") + }) +} + func TestGitGeneratorPrivateRepo(t *testing.T) { FailOnErr(fixture.RunCli("repo", "add", fixture.RepoURL(fixture.RepoURLTypeHTTPS), "--username", fixture.GitUsername, "--password", fixture.GitPassword, "--insecure-skip-server-verification")) generateExpectedApp := func(name string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: name, - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -1518,12 +2606,12 @@ func TestGitGeneratorPrivateRepoGoTemplate(t *testing.T) { generateExpectedApp := func(name string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: name, - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ diff --git a/test/e2e/cli_test.go b/test/e2e/cli_test.go index 028d3d516764e..8e87ea16f4469 100644 --- a/test/e2e/cli_test.go +++ b/test/e2e/cli_test.go @@ -31,7 +31,7 @@ func TestCliAppCommand(t *testing.T) { output, err := RunCli("app", "list") assert.NoError(t, err) expected := Tmpl( - `{{.Name}} https://kubernetes.default.svc {{.Namespace}} default Synced Healthy `, + `{{.Name}} https://kubernetes.default.svc {{.Namespace}} default Synced Healthy Manual `, map[string]interface{}{"Name": Name(), "Namespace": DeploymentNamespace()}) assert.Contains(t, NormalizeOutput(output), expected) }) diff --git a/test/e2e/cluster_generator_test.go b/test/e2e/cluster_generator_test.go index c09fc9ed2fa34..1d5699e23503d 100644 --- a/test/e2e/cluster_generator_test.go +++ b/test/e2e/cluster_generator_test.go @@ -4,15 +4,21 @@ import ( "testing" "time" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) -func TestSimpleClusterGenerator(t *testing.T) { +func TestSimpleClusterGeneratorExternalNamespace(t *testing.T) { + + var externalNamespace = string(utils.ArgoCDExternalNamespace) expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ @@ -21,7 +27,107 @@ func TestSimpleClusterGenerator(t *testing.T) { }, ObjectMeta: metav1.ObjectMeta{ Name: "cluster1-guestbook", - Namespace: utils.ArgoCDNamespace, + Namespace: externalNamespace, + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Name: "cluster1", + Namespace: "guestbook", + }, + }, + } + + var expectedAppNewNamespace *argov1alpha1.Application + var expectedAppNewMetadata *argov1alpha1.Application + + Given(t). + // Create a ClusterGenerator-based ApplicationSet + When(). + CreateClusterSecret("my-secret", "cluster1", "https://kubernetes.default.svc"). + SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). + CreateNamespace(externalNamespace). + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-cluster-generator", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-guestbook"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Name: "{{name}}", + // Server: "{{server}}", + Namespace: "guestbook", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + Clusters: &v1alpha1.ClusterGenerator{ + Selector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "argocd.argoproj.io/secret-type": "cluster", + }, + }, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). + + // Update the ApplicationSet template namespace, and verify it updates the Applications + When(). + And(func() { + expectedAppNewNamespace = expectedApp.DeepCopy() + expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). + + // Update the metadata fields in the appset template, and make sure it propagates to the apps + When(). + And(func() { + expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() + expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} + expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{ + "label-key": "label-value", + } + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} + appset.Spec.Template.Labels = map[string]string{ + "label-key": "label-value", + } + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewNamespace})) +} + +func TestSimpleClusterGenerator(t *testing.T) { + + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster1-guestbook", + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -109,12 +215,12 @@ func TestSimpleClusterGenerator(t *testing.T) { func TestClusterGeneratorWithLocalCluster(t *testing.T) { expectedAppTemplate := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: "in-cluster-guestbook", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -226,12 +332,12 @@ func TestSimpleClusterGeneratorAddingCluster(t *testing.T) { expectedAppTemplate := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: "{{name}}-guestbook", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -308,12 +414,12 @@ func TestSimpleClusterGeneratorDeletingCluster(t *testing.T) { expectedAppTemplate := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: "{{name}}-guestbook", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ diff --git a/test/e2e/cluster_objects_test.go b/test/e2e/cluster_objects_test.go index 4388932296673..4299a35c55c00 100644 --- a/test/e2e/cluster_objects_test.go +++ b/test/e2e/cluster_objects_test.go @@ -5,12 +5,14 @@ import ( "github.com/argoproj/gitops-engine/pkg/health" . "github.com/argoproj/gitops-engine/pkg/sync/common" + "github.com/stretchr/testify/assert" . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + . "github.com/argoproj/argo-cd/v2/test/e2e/fixture" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" + "github.com/argoproj/argo-cd/v2/util/argo" ) -// ensure that cluster scoped objects, like a cluster role, as a hok, can be successfully deployed func TestClusterRoleBinding(t *testing.T) { Given(t). Path("cluster-role"). @@ -20,5 +22,35 @@ func TestClusterRoleBinding(t *testing.T) { Then(). Expect(OperationPhaseIs(OperationSucceeded)). Expect(HealthIs(health.HealthStatusHealthy)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + diffOutput, err := RunCli("app", "diff", app.Name, "--revision=HEAD") + assert.NoError(t, err) + assert.Empty(t, diffOutput) + }). + When(). + SetTrackingMethod(string(argo.TrackingMethodAnnotation)). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)). + And(func(app *Application) { + diffOutput, err := RunCli("app", "diff", app.Name, "--revision=HEAD") + assert.NoError(t, err) + assert.Empty(t, diffOutput) + }) +} + +// ensure that cluster scoped objects, like a cluster role, as a hook, can be successfully deployed +func TestClusterRoleBindingHook(t *testing.T) { + Given(t). + Path("cluster-role-hook"). + When(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(HealthIs(health.HealthStatusHealthy)). Expect(SyncStatusIs(SyncStatusCodeSynced)) } diff --git a/test/e2e/cluster_test.go b/test/e2e/cluster_test.go index 671acb735e193..2074a6aa1b7b1 100644 --- a/test/e2e/cluster_test.go +++ b/test/e2e/cluster_test.go @@ -5,6 +5,7 @@ import ( "net/url" "strings" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -13,6 +14,7 @@ import ( "github.com/argoproj/argo-cd/v2/test/e2e/fixture" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture" accountFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/account" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" clusterFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/cluster" . "github.com/argoproj/argo-cd/v2/util/errors" ) @@ -21,16 +23,38 @@ func TestClusterList(t *testing.T) { SkipIfAlreadyRun(t) defer RecordTestRun(t) + last := "" + expected := fmt.Sprintf(`SERVER NAME VERSION STATUS MESSAGE PROJECT +https://kubernetes.default.svc in-cluster %v Successful `, GetVersions().ServerVersion) + clusterFixture. Given(t). - Project(ProjectName). + Project(ProjectName) + + // We need an application targeting the cluster, otherwise the test will + // fail if run isolated. + app.GivenWithSameState(t). + Path(guestbookPath). When(). - List(). - Then(). - AndCLIOutput(func(output string, err error) { - assert.Equal(t, fmt.Sprintf(`SERVER NAME VERSION STATUS MESSAGE PROJECT -https://kubernetes.default.svc in-cluster %v Successful `, GetVersions().ServerVersion), output) - }) + CreateApp() + + tries := 5 + for i := 0; i <= tries; i += 1 { + clusterFixture.GivenWithSameState(t). + When(). + List(). + Then(). + AndCLIOutput(func(output string, err error) { + last = output + }) + if expected == last { + break + } else if i < tries { + // We retry with a simple backoff + time.Sleep(time.Duration(i+1) * time.Second) + } + } + assert.Equal(t, expected, last) } func TestClusterAdd(t *testing.T) { diff --git a/test/e2e/clusterdecisiongenerator_e2e_test.go b/test/e2e/clusterdecisiongenerator_e2e_test.go index 44993dd409fde..5f0d6ff6ae3c7 100644 --- a/test/e2e/clusterdecisiongenerator_e2e_test.go +++ b/test/e2e/clusterdecisiongenerator_e2e_test.go @@ -3,17 +3,23 @@ package e2e import ( "testing" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) var tenSec = int64(10) -func TestSimpleClusterDecisionResourceGenerator(t *testing.T) { +func TestSimpleClusterDecisionResourceGeneratorExternalNamespace(t *testing.T) { + + var externalNamespace = string(utils.ArgoCDExternalNamespace) expectedApp := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ @@ -22,7 +28,115 @@ func TestSimpleClusterDecisionResourceGenerator(t *testing.T) { }, ObjectMeta: metav1.ObjectMeta{ Name: "cluster1-guestbook", - Namespace: utils.ArgoCDNamespace, + Namespace: externalNamespace, + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Name: "cluster1", + Namespace: "guestbook", + }, + }, + } + + var expectedAppNewNamespace *argov1alpha1.Application + var expectedAppNewMetadata *argov1alpha1.Application + + clusterList := []interface{}{ + map[string]interface{}{ + "clusterName": "cluster1", + "reason": "argotest", + }, + } + + Given(t). + // Create a ClusterGenerator-based ApplicationSet + When(). + CreateClusterSecret("my-secret", "cluster1", "https://kubernetes.default.svc"). + CreatePlacementRoleAndRoleBinding(). + CreatePlacementDecisionConfigMap("my-configmap"). + CreatePlacementDecision("my-placementdecision"). + StatusUpdatePlacementDecision("my-placementdecision", clusterList). + CreateNamespace(externalNamespace). + SwitchToExternalNamespace(utils.ArgoCDExternalNamespace). + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "simple-cluster-generator", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{name}}-guestbook"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Name: "{{clusterName}}", + // Server: "{{server}}", + Namespace: "guestbook", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + ClusterDecisionResource: &v1alpha1.DuckTypeGenerator{ + ConfigMapRef: "my-configmap", + Name: "my-placementdecision", + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). + + // Update the ApplicationSet template namespace, and verify it updates the Applications + When(). + And(func() { + expectedAppNewNamespace = expectedApp.DeepCopy() + expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). + + // Update the metadata fields in the appset template, and make sure it propagates to the apps + When(). + And(func() { + expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() + expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"} + expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{ + "label-key": "label-value", + } + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"} + appset.Spec.Template.Labels = map[string]string{ + "label-key": "label-value", + } + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewNamespace})) +} + +func TestSimpleClusterDecisionResourceGenerator(t *testing.T) { + + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster1-guestbook", + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -119,12 +233,12 @@ func TestSimpleClusterDecisionResourceGeneratorAddingCluster(t *testing.T) { expectedAppTemplate := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: "{{name}}-guestbook", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -214,12 +328,12 @@ func TestSimpleClusterDecisionResourceGeneratorDeletingClusterSecret(t *testing. expectedAppTemplate := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: "{{name}}-guestbook", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -311,12 +425,12 @@ func TestSimpleClusterDecisionResourceGeneratorDeletingClusterFromResource(t *te expectedAppTemplate := argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: "{{name}}-guestbook", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ diff --git a/test/e2e/custom_tool_test.go b/test/e2e/custom_tool_test.go index cd587f42693b6..7370fb5478ad3 100644 --- a/test/e2e/custom_tool_test.go +++ b/test/e2e/custom_tool_test.go @@ -2,6 +2,7 @@ package e2e import ( "os" + "path/filepath" "sort" "strings" "testing" @@ -10,6 +11,7 @@ import ( "github.com/argoproj/gitops-engine/pkg/health" . "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture" @@ -21,56 +23,41 @@ import ( func TestCustomToolWithGitCreds(t *testing.T) { ctx := Given(t) ctx. - // path does not matter, we ignore it - ConfigManagementPlugin( - ConfigManagementPlugin{ - Name: Name(), - Generate: Command{ - Command: []string{"sh", "-c"}, - Args: []string{`echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"GitAskpass\": \"$GIT_ASKPASS\"}}}"`}, - }, - }, - ). + And(func() { + go startCMPServer(t, "./testdata/cmp-gitcreds") + time.Sleep(1 * time.Second) + t.Setenv("ARGOCD_BINARY_NAME", "argocd") + }). CustomCACertAdded(). // add the private repo with credentials HTTPSRepoURLAdded(true). RepoURLType(RepoURLTypeHTTPS). - Path("https-kustomize-base"). + Path("cmp-gitcreds"). When(). CreateApp(). Sync(). Then(). Expect(OperationPhaseIs(OperationSucceeded)). Expect(SyncStatusIs(SyncStatusCodeSynced)). - Expect(HealthIs(health.HealthStatusHealthy)). - And(func(app *Application) { - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitAskpass}") - assert.NoError(t, err) - assert.Equal(t, "argocd", output) - }) + Expect(HealthIs(health.HealthStatusHealthy)) } // make sure we can echo back the Git creds func TestCustomToolWithGitCredsTemplate(t *testing.T) { ctx := Given(t) ctx. - // path does not matter, we ignore it - ConfigManagementPlugin( - ConfigManagementPlugin{ - Name: Name(), - Generate: Command{ - Command: []string{"sh", "-c"}, - Args: []string{`echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"GitAskpass\": \"$GIT_ASKPASS\", \"GitUsername\": \"$GIT_USERNAME\", \"GitPassword\": \"$GIT_PASSWORD\"}}}"`}, - }, - }, - ). + And(func() { + go startCMPServer(t, "./testdata/cmp-gitcredstemplate") + time.Sleep(1 * time.Second) + t.Setenv("ARGOCD_BINARY_NAME", "argocd") + }). CustomCACertAdded(). // add the git creds template HTTPSCredentialsUserPassAdded(). // add the private repo without credentials HTTPSRepoURLAdded(false). RepoURLType(RepoURLTypeHTTPS). - Path("https-kustomize-base"). + Path("cmp-gitcredstemplate"). When(). CreateApp(). Sync(). @@ -78,11 +65,6 @@ func TestCustomToolWithGitCredsTemplate(t *testing.T) { Expect(OperationPhaseIs(OperationSucceeded)). Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(HealthIs(health.HealthStatusHealthy)). - And(func(app *Application) { - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitAskpass}") - assert.NoError(t, err) - assert.Equal(t, "argocd", output) - }). And(func(app *Application) { output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitUsername}") assert.NoError(t, err) @@ -99,24 +81,21 @@ func TestCustomToolWithGitCredsTemplate(t *testing.T) { func TestCustomToolWithEnv(t *testing.T) { ctx := Given(t) ctx. - // path does not matter, we ignore it - ConfigManagementPlugin( - ConfigManagementPlugin{ - Name: Name(), - Generate: Command{ - Command: []string{"sh", "-c"}, - Args: []string{`echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$ARGOCD_ENV_FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"`}, - }, - }, - ). + And(func() { + go startCMPServer(t, "./testdata/cmp-fileName") + time.Sleep(1 * time.Second) + t.Setenv("ARGOCD_BINARY_NAME", "argocd") + }). // does not matter what the path is - Path("guestbook"). + Path("cmp-fileName"). When(). CreateFromFile(func(app *Application) { - app.Spec.GetSource().Plugin.Env = Env{{ - Name: "FOO", - Value: "bar", - }} + app.Spec.Source.Plugin = &ApplicationSourcePlugin{ + Env: Env{{ + Name: "FOO", + Value: "bar", + }}, + } }). Sync(). Then(). @@ -156,25 +135,23 @@ func TestCustomToolWithEnv(t *testing.T) { }) } -//make sure we can sync and diff with --local +// make sure we can sync and diff with --local func TestCustomToolSyncAndDiffLocal(t *testing.T) { + testdataPath, err := filepath.Abs("testdata") + require.NoError(t, err) ctx := Given(t) + appPath := filepath.Join(testdataPath, "guestbook") ctx. - // path does not matter, we ignore it - ConfigManagementPlugin( - ConfigManagementPlugin{ - Name: Name(), - Generate: Command{ - Command: []string{"sh", "-c"}, - Args: []string{`echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$ARGOCD_ENV_FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"`}, - }, - }, - ). + And(func() { + go startCMPServer(t, "./testdata/cmp-kustomize") + time.Sleep(1 * time.Second) + t.Setenv("ARGOCD_BINARY_NAME", "argocd") + }). // does not matter what the path is Path("guestbook"). When(). - CreateApp("--config-management-plugin", ctx.AppName()). - Sync("--local", "testdata/guestbook"). + CreateApp("--config-management-plugin", "cmp-kustomize-v1.0"). + Sync("--local", appPath, "--local-repo-root", testdataPath). Then(). Expect(OperationPhaseIs(OperationSucceeded)). Expect(SyncStatusIs(SyncStatusCodeSynced)). @@ -183,36 +160,36 @@ func TestCustomToolSyncAndDiffLocal(t *testing.T) { time.Sleep(1 * time.Second) }). And(func(app *Application) { - FailOnErr(RunCli("app", "sync", ctx.AppName(), "--local", "testdata/guestbook")) + FailOnErr(RunCli("app", "sync", ctx.AppName(), "--local", appPath, "--local-repo-root", testdataPath)) }). And(func(app *Application) { - FailOnErr(RunCli("app", "diff", ctx.AppName(), "--local", "testdata/guestbook")) + FailOnErr(RunCli("app", "diff", ctx.AppName(), "--local", appPath, "--local-repo-root", testdataPath)) }) } -func startCMPServer(configFile string) { +func startCMPServer(t *testing.T, configFile string) { pluginSockFilePath := TmpDir + PluginSockFilePath - os.Setenv("ARGOCD_BINARY_NAME", "argocd-cmp-server") + t.Setenv("ARGOCD_BINARY_NAME", "argocd-cmp-server") // ARGOCD_PLUGINSOCKFILEPATH should be set as the same value as repo server env var - os.Setenv("ARGOCD_PLUGINSOCKFILEPATH", pluginSockFilePath) + t.Setenv("ARGOCD_PLUGINSOCKFILEPATH", pluginSockFilePath) if _, err := os.Stat(pluginSockFilePath); os.IsNotExist(err) { // path/to/whatever does not exist err := os.Mkdir(pluginSockFilePath, 0700) - CheckError(err) + require.NoError(t, err) } FailOnErr(RunWithStdin("", "", "../../dist/argocd", "--config-dir-path", configFile)) } -//Discover by fileName +// Discover by fileName func TestCMPDiscoverWithFileName(t *testing.T) { pluginName := "cmp-fileName" Given(t). And(func() { - go startCMPServer("./testdata/cmp-fileName") + go startCMPServer(t, "./testdata/cmp-fileName") time.Sleep(1 * time.Second) - os.Setenv("ARGOCD_BINARY_NAME", "argocd") + t.Setenv("ARGOCD_BINARY_NAME", "argocd") }). - Path(pluginName). + Path(pluginName + "/subdir"). When(). CreateApp(). Sync(). @@ -222,13 +199,13 @@ func TestCMPDiscoverWithFileName(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)) } -//Discover by Find glob +// Discover by Find glob func TestCMPDiscoverWithFindGlob(t *testing.T) { Given(t). And(func() { - go startCMPServer("./testdata/cmp-find-glob") + go startCMPServer(t, "./testdata/cmp-find-glob") time.Sleep(1 * time.Second) - os.Setenv("ARGOCD_BINARY_NAME", "argocd") + t.Setenv("ARGOCD_BINARY_NAME", "argocd") }). Path("guestbook"). When(). @@ -240,13 +217,13 @@ func TestCMPDiscoverWithFindGlob(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)) } -//Discover by Plugin Name +// Discover by Plugin Name func TestCMPDiscoverWithPluginName(t *testing.T) { Given(t). And(func() { - go startCMPServer("./testdata/cmp-find-glob") + go startCMPServer(t, "./testdata/cmp-find-glob") time.Sleep(1 * time.Second) - os.Setenv("ARGOCD_BINARY_NAME", "argocd") + t.Setenv("ARGOCD_BINARY_NAME", "argocd") }). Path("guestbook"). When(). @@ -261,15 +238,15 @@ func TestCMPDiscoverWithPluginName(t *testing.T) { Expect(HealthIs(health.HealthStatusHealthy)) } -//Discover by Find command +// Discover by Find command func TestCMPDiscoverWithFindCommandWithEnv(t *testing.T) { pluginName := "cmp-find-command" ctx := Given(t) ctx. And(func() { - go startCMPServer("./testdata/cmp-find-command") + go startCMPServer(t, "./testdata/cmp-find-command") time.Sleep(1 * time.Second) - os.Setenv("ARGOCD_BINARY_NAME", "argocd") + t.Setenv("ARGOCD_BINARY_NAME", "argocd") }). Path(pluginName). When(). @@ -310,9 +287,9 @@ func TestCMPDiscoverWithFindCommandWithEnv(t *testing.T) { func TestPruneResourceFromCMP(t *testing.T) { Given(t). And(func() { - go startCMPServer("./testdata/cmp-find-glob") + go startCMPServer(t, "./testdata/cmp-find-glob") time.Sleep(1 * time.Second) - os.Setenv("ARGOCD_BINARY_NAME", "argocd") + t.Setenv("ARGOCD_BINARY_NAME", "argocd") }). Path("guestbook"). When(). @@ -329,3 +306,74 @@ func TestPruneResourceFromCMP(t *testing.T) { assert.Error(t, err) }) } + +func TestPreserveFileModeForCMP(t *testing.T) { + Given(t). + And(func() { + go startCMPServer(t, "./testdata/cmp-preserve-file-mode") + time.Sleep(1 * time.Second) + t.Setenv("ARGOCD_BINARY_NAME", "argocd") + }). + Path("cmp-preserve-file-mode"). + When(). + CreateFromFile(func(app *Application) { + app.Spec.Source.Plugin = &ApplicationSourcePlugin{Name: "cmp-preserve-file-mode-v1.0"} + }). + Refresh(RefreshTypeNormal). + Then(). + And(func(app *Application) { + require.Len(t, app.Status.Resources, 1) + assert.Equal(t, "ConfigMap", app.Status.Resources[0].Kind) + }) +} + +func TestCMPWithSymlinkPartialFiles(t *testing.T) { + Given(t, WithTestData("testdata2")). + And(func() { + go startCMPServer(t, "./testdata2/cmp-symlink") + time.Sleep(1 * time.Second) + t.Setenv("ARGOCD_BINARY_NAME", "argocd") + }). + Path("guestbook-partial-symlink-files"). + When(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)) +} + +func TestCMPWithSymlinkFiles(t *testing.T) { + Given(t, WithTestData("testdata2")). + And(func() { + go startCMPServer(t, "./testdata2/cmp-symlink") + time.Sleep(1 * time.Second) + t.Setenv("ARGOCD_BINARY_NAME", "argocd") + }). + Path("guestbook-symlink-files"). + When(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)) +} + +func TestCMPWithSymlinkFolder(t *testing.T) { + Given(t, WithTestData("testdata2")). + And(func() { + go startCMPServer(t, "./testdata2/cmp-symlink") + time.Sleep(1 * time.Second) + t.Setenv("ARGOCD_BINARY_NAME", "argocd") + }). + Path("guestbook-symlink-folder"). + When(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)) +} diff --git a/test/e2e/delarative_test.go b/test/e2e/declarative_test.go similarity index 100% rename from test/e2e/delarative_test.go rename to test/e2e/declarative_test.go diff --git a/test/e2e/deployment_test.go b/test/e2e/deployment_test.go index 085ecee244ba2..20e79c2aff56c 100644 --- a/test/e2e/deployment_test.go +++ b/test/e2e/deployment_test.go @@ -1,12 +1,22 @@ package e2e import ( + "context" + "encoding/json" "fmt" + "os" "testing" + "time" "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/clientcmd" + "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/util/argo" + "github.com/argoproj/argo-cd/v2/util/clusterauth" "github.com/argoproj/gitops-engine/pkg/health" . "github.com/argoproj/gitops-engine/pkg/sync/common" @@ -108,3 +118,304 @@ func TestDeploymentWithoutTrackingMode(t *testing.T) { `, ctx.AppName())) }) } + +// This test verifies that Argo CD can: +// A) Deploy to a cluster where the URL of the cluster contains a query parameter: e.g. https://(kubernetes-url):443/?context=some-val +// and +// B) Multiple users can deploy to the same K8s cluster, using above mechanism (but with different Argo CD Cluster Secrets, and different ServiceAccounts) +func TestDeployToKubernetesAPIURLWithQueryParameter(t *testing.T) { + + // We test with both a cluster-scoped, and a non-cluster scoped, Argo CD Cluster Secret. + clusterScopedParam := []bool{false, true} + for _, clusterScoped := range clusterScopedParam { + + EnsureCleanState(t) + + // Simulate two users, each with their own Argo CD cluster secret that can only deploy to their Namespace + users := []string{E2ETestPrefix + "user1", E2ETestPrefix + "user2"} + + for _, username := range users { + createNamespaceScopedUser(t, username, clusterScoped) + + GivenWithSameState(t). + Name("e2e-test-app-"+username). + Path("deployment"). + When(). + CreateWithNoNameSpace("--dest-namespace", username). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)) + } + + } + +} + +// This test verifies that Argo CD can: +// When multiple Argo CD cluster secrets used to deploy to the same cluster (using query parameters), that the ServiceAccount RBAC +// fully enforces user boundary. +// Our simulated user's ServiceAccounts should not be able to deploy into a namespace that is outside that SA's RBAC. +func TestArgoCDSupportsMultipleServiceAccountsWithDifferingRBACOnSameCluster(t *testing.T) { + + // We test with both a cluster-scoped, and a non-cluster scoped, Argo CD Cluster Secret. + clusterScopedParam := []bool{ /*false,*/ true} + + for _, clusterScoped := range clusterScopedParam { + + EnsureCleanState(t) + + // Simulate two users, each with their own Argo CD cluster secret that can only deploy to their Namespace + users := []string{E2ETestPrefix + "user1", E2ETestPrefix + "user2"} + + for _, username := range users { + createNamespaceScopedUser(t, username, clusterScoped) + } + + for idx, username := range users { + + // we should use user-a's serviceaccount to deploy to user-b's namespace, and vice versa + // - If everything as working as expected, this should fail. + otherUser := users[(idx+1)%len(users)] + + // e.g. Attempt to deploy to user1's namespace, with user2's cluster Secret. This should fail, as user2's cluster Secret does not have the requisite permissions. + consequences := GivenWithSameState(t). + Name("e2e-test-app-"+username). + DestName(E2ETestPrefix+"cluster-"+otherUser). + Path("deployment"). + When(). + CreateWithNoNameSpace("--dest-namespace", username).IgnoreErrors(). + Sync().Then() + + // The error message differs based on whether the Argo CD Cluster Secret is namespace-scoped or cluster-scoped, but the idea is the same: + // - Even when deploying to the same cluster using 2 separate ServiceAccounts, the RBAC of those ServiceAccounts should continue to fully enforce RBAC boundaries. + + if !clusterScoped { + consequences.Expect(Condition(ApplicationConditionComparisonError, "Namespace \""+username+"\" for Deployment \"nginx-deployment\" is not managed")) + } else { + consequences.Expect(OperationMessageContains("User \"system:serviceaccount:" + otherUser + ":" + otherUser + "-serviceaccount\" cannot create resource \"deployments\" in API group \"apps\" in the namespace \"" + username + "\"")) + } + } + + } +} + +// generateReadOnlyClusterRoleandBindingForServiceAccount creates a ClusterRole/Binding that allows a ServiceAccount in a given namespace to read all resources on a cluster. +// - This allows the ServiceAccount to be used within a cluster-scoped Argo CD Cluster Secret +func generateReadOnlyClusterRoleandBindingForServiceAccount(roleSuffix string, serviceAccountNS string) (rbacv1.ClusterRole, rbacv1.ClusterRoleBinding) { + + clusterRole := rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: E2ETestPrefix + "read-all-" + roleSuffix, + }, + Rules: []rbacv1.PolicyRule{{ + Verbs: []string{"get", "list", "watch"}, + Resources: []string{"*"}, + APIGroups: []string{"*"}, + }}, + } + + clusterRoleBinding := rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: E2ETestPrefix + "read-all-" + roleSuffix, + }, + Subjects: []rbacv1.Subject{{ + Kind: rbacv1.ServiceAccountKind, + Namespace: serviceAccountNS, + Name: roleSuffix + "-serviceaccount", + }}, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: clusterRole.Name, + }, + } + + return clusterRole, clusterRoleBinding +} + +// buildArgoCDClusterSecret build (but does not create) an Argo CD Cluster Secret object with the given values +func buildArgoCDClusterSecret(secretName, secretNamespace, clusterName, clusterServer, clusterConfigJSON, clusterResources, clusterNamespaces string) corev1.Secret { + res := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: secretNamespace, + Labels: map[string]string{ + common.LabelKeySecretType: common.LabelValueSecretTypeCluster, + }, + }, + Data: map[string][]byte{ + "name": ([]byte)(clusterName), + "server": ([]byte)(clusterServer), + "config": ([]byte)(string(clusterConfigJSON)), + }, + } + + if clusterResources != "" { + res.Data["clusterResources"] = ([]byte)(clusterResources) + } + + if clusterNamespaces != "" { + res.Data["namespaces"] = ([]byte)(clusterNamespaces) + } + + return res +} + +// createNamespaceScopedUser +// - username = name of Namespace the simulated user is able to deploy to +// - clusterScopedSecrets = whether the Service Account is namespace-scoped or cluster-scoped. +func createNamespaceScopedUser(t *testing.T, username string, clusterScopedSecrets bool) { + + // Create a new Namespace for our simulated user + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: username, + }, + } + _, err := KubeClientset.CoreV1().Namespaces().Create(context.Background(), &ns, metav1.CreateOptions{}) + assert.Nil(t, err) + + // Create a ServiceAccount in that Namespace, which will be used for the Argo CD Cluster SEcret + serviceAccountName := username + "-serviceaccount" + err = clusterauth.CreateServiceAccount(KubeClientset, serviceAccountName, ns.Name) + assert.Nil(t, err) + + // Create a Role that allows the ServiceAccount to read/write all within the Namespace + role := rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: E2ETestPrefix + "allow-all", + Namespace: ns.Name, + }, + Rules: []rbacv1.PolicyRule{{ + Verbs: []string{"*"}, + Resources: []string{"*"}, + APIGroups: []string{"*"}, + }}, + } + _, err = KubeClientset.RbacV1().Roles(role.Namespace).Create(context.Background(), &role, metav1.CreateOptions{}) + assert.Nil(t, err) + + // Bind the Role with the ServiceAccount in the Namespace + roleBinding := rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: E2ETestPrefix + "allow-all-binding", + Namespace: ns.Name, + }, + Subjects: []rbacv1.Subject{{ + Kind: rbacv1.ServiceAccountKind, + Name: serviceAccountName, + Namespace: ns.Name, + }}, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Role", + Name: role.Name, + }, + } + _, err = KubeClientset.RbacV1().RoleBindings(roleBinding.Namespace).Create(context.Background(), &roleBinding, metav1.CreateOptions{}) + assert.Nil(t, err) + + // Retrieve the bearer token from the ServiceAccount + token, err := clusterauth.GetServiceAccountBearerToken(KubeClientset, ns.Name, serviceAccountName, time.Second*60) + assert.Nil(t, err) + assert.NotEmpty(t, token) + + // In order to test a cluster-scoped Argo CD Cluster Secret, we may optionally grant the ServiceAccount read-all permissions at cluster scope. + if clusterScopedSecrets { + clusterRole, clusterRoleBinding := generateReadOnlyClusterRoleandBindingForServiceAccount(username, username) + + _, err := KubeClientset.RbacV1().ClusterRoles().Create(context.Background(), &clusterRole, metav1.CreateOptions{}) + assert.Nil(t, err) + + _, err = KubeClientset.RbacV1().ClusterRoleBindings().Create(context.Background(), &clusterRoleBinding, metav1.CreateOptions{}) + assert.Nil(t, err) + + } + + // Build the Argo CD Cluster Secret by using the service account token, and extracting needed values from kube config + clusterSecretConfigJSON := ClusterConfig{ + BearerToken: token, + TLSClientConfig: TLSClientConfig{ + Insecure: true, + }, + } + + jsonStringBytes, err := json.Marshal(clusterSecretConfigJSON) + assert.Nil(t, err) + + _, apiURL, err := extractKubeConfigValues() + assert.Nil(t, err) + + clusterResourcesField := "" + namespacesField := "" + + if !clusterScopedSecrets { + clusterResourcesField = "false" + namespacesField = ns.Name + } + + // We create an Argo CD cluster Secret declaratively, using the K8s client, rather than via CLI, as the CLI doesn't currently + // support Kubernetes API server URLs with query parameters. + + secret := buildArgoCDClusterSecret("test-"+username, ArgoCDNamespace, E2ETestPrefix+"cluster-"+username, apiURL+"?user="+username, + string(jsonStringBytes), clusterResourcesField, namespacesField) + + // Finally, create the Cluster secret in the Argo CD E2E namespace + _, err = KubeClientset.CoreV1().Secrets(secret.Namespace).Create(context.Background(), &secret, metav1.CreateOptions{}) + assert.Nil(t, err) +} + +// extractKubeConfigValues returns contents of the local environment's kubeconfig, using standard path resolution mechanism. +// Returns: +// - contents of kubeconfig +// - server name (within the kubeconfig) +// - error +func extractKubeConfigValues() (string, string, error) { + + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + + config, err := loadingRules.Load() + if err != nil { + return "", "", err + } + + context, ok := config.Contexts[config.CurrentContext] + if !ok || context == nil { + return "", "", fmt.Errorf("no context") + } + + cluster, ok := config.Clusters[context.Cluster] + if !ok || cluster == nil { + return "", "", fmt.Errorf("no cluster") + } + + var kubeConfigDefault string + + paths := loadingRules.Precedence + { + + // For all the kubeconfig paths, look for one that exists + for _, path := range paths { + _, err = os.Stat(path) + if err == nil { + // Success + kubeConfigDefault = path + break + } // Otherwise, continue. + + } + + if kubeConfigDefault == "" { + return "", "", fmt.Errorf("unable to retrieve kube config path") + } + } + + kubeConfigContents, err := os.ReadFile(kubeConfigDefault) + if err != nil { + return "", "", err + } + + return string(kubeConfigContents), cluster.Server, nil +} diff --git a/test/e2e/fixture/account/actions.go b/test/e2e/fixture/account/actions.go index 75e8762e76c91..f5708c5606a41 100644 --- a/test/e2e/fixture/account/actions.go +++ b/test/e2e/fixture/account/actions.go @@ -41,7 +41,7 @@ func (a *Actions) DoNotIgnoreErrors() *Actions { func (a *Actions) prepareSetPasswordArgs(account string) []string { a.context.t.Helper() return []string{ - "account", "update-password", "--account", account, "--current-password", fixture.AdminPassword, "--new-password", fixture.DefaultTestUserPassword, "--plaintext", + "account", "update-password", "--account", account, "--current-password", fixture.AdminPassword, "--new-password", fixture.DefaultTestUserPassword, } } diff --git a/test/e2e/fixture/app/actions.go b/test/e2e/fixture/app/actions.go index c8df055f1b8ac..a2b1d5e01371b 100644 --- a/test/e2e/fixture/app/actions.go +++ b/test/e2e/fixture/app/actions.go @@ -1,12 +1,14 @@ package app import ( + "encoding/json" "fmt" "os" log "github.com/sirupsen/logrus" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + client "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/test/e2e/fixture" "github.com/argoproj/argo-cd/v2/util/errors" @@ -64,6 +66,24 @@ func (a *Actions) AddSignedFile(fileName, fileContents string) *Actions { return a } +func (a *Actions) AddSignedTag(name string) *Actions { + a.context.t.Helper() + fixture.AddSignedTag(name) + return a +} + +func (a *Actions) AddTag(name string) *Actions { + a.context.t.Helper() + fixture.AddTag(name) + return a +} + +func (a *Actions) RemoveSubmodule() *Actions { + a.context.t.Helper() + fixture.RemoveSubmodule() + return a +} + func (a *Actions) CreateFromPartialFile(data string, flags ...string) *Actions { a.context.t.Helper() tmpFile, err := os.CreateTemp("", "") @@ -259,7 +279,7 @@ func (a *Actions) Declarative(filename string) *Actions { func (a *Actions) DeclarativeWithCustomRepo(filename string, repoURL string) *Actions { a.context.t.Helper() values := map[string]interface{}{ - "ArgoCDNamespace": fixture.ArgoCDNamespace, + "ArgoCDNamespace": fixture.TestNamespace(), "DeploymentNamespace": fixture.DeploymentNamespace(), "Name": a.context.AppName(), "Path": a.context.path, @@ -277,6 +297,28 @@ func (a *Actions) PatchApp(patch string) *Actions { return a } +func (a *Actions) PatchAppHttp(patch string) *Actions { + a.context.t.Helper() + var application Application + var patchType = "merge" + var appName = a.context.AppQualifiedName() + var appNamespace = a.context.AppNamespace() + patchRequest := &client.ApplicationPatchRequest{ + Name: &appName, + PatchType: &patchType, + Patch: &patch, + AppNamespace: &appNamespace, + } + jsonBytes, err := json.MarshalIndent(patchRequest, "", " ") + errors.CheckError(err) + err = fixture.DoHttpJsonRequest("PATCH", + fmt.Sprintf("/api/v1/applications/%v", appName), + &application, + jsonBytes...) + errors.CheckError(err) + return a +} + func (a *Actions) AppSet(flags ...string) *Actions { a.context.t.Helper() args := []string{"app", "set", a.context.AppQualifiedName()} @@ -324,6 +366,10 @@ func (a *Actions) Sync(args ...string) *Actions { args = append(args, "--force") } + if a.context.applyOutOfSyncOnly { + args = append(args, "--apply-out-of-sync-only") + } + if a.context.replace { args = append(args, "--replace") } @@ -353,6 +399,12 @@ func (a *Actions) Refresh(refreshType RefreshType) *Actions { return a } +func (a *Actions) Get() *Actions { + a.context.t.Helper() + a.runCli("app", "get", a.context.AppQualifiedName()) + return a +} + func (a *Actions) Delete(cascade bool) *Actions { a.context.t.Helper() a.runCli("app", "delete", a.context.AppQualifiedName(), fmt.Sprintf("--cascade=%v", cascade), "--yes") @@ -365,6 +417,23 @@ func (a *Actions) DeleteBySelector(selector string) *Actions { return a } +func (a *Actions) DeleteBySelectorWithWait(selector string) *Actions { + a.context.t.Helper() + a.runCli("app", "delete", fmt.Sprintf("--selector=%s", selector), "--yes", "--wait") + return a +} + +func (a *Actions) Wait(args ...string) *Actions { + a.context.t.Helper() + args = append([]string{"app", "wait"}, args...) + if a.context.name != "" { + args = append(args, a.context.AppQualifiedName()) + } + args = append(args, "--timeout", fmt.Sprintf("%v", a.context.timeout)) + a.runCli(args...) + return a +} + func (a *Actions) SetParamInSettingConfigMap(key, value string) *Actions { fixture.SetParamInSettingConfigMap(key, value) return a diff --git a/test/e2e/fixture/app/consequences.go b/test/e2e/fixture/app/consequences.go index f5d9240024951..9ee99fec6ca6d 100644 --- a/test/e2e/fixture/app/consequences.go +++ b/test/e2e/fixture/app/consequences.go @@ -86,11 +86,6 @@ func (c *Consequences) resource(kind, name, namespace string) ResourceStatus { } } -func (c *Consequences) Timeout(timeout int) *Consequences { - c.timeout = timeout - return c -} - func (c *Consequences) AndCLIOutput(block func(output string, err error)) *Consequences { c.context.t.Helper() block(c.actions.lastOutput, c.actions.lastError) diff --git a/test/e2e/fixture/app/context.go b/test/e2e/fixture/app/context.go index 0aa94a33ac906..41c8dbd17bcad 100644 --- a/test/e2e/fixture/app/context.go +++ b/test/e2e/fixture/app/context.go @@ -38,6 +38,7 @@ type Context struct { project string revision string force bool + applyOutOfSyncOnly bool directoryRecurse bool replace bool helmPassCredentials bool @@ -50,8 +51,8 @@ type ContextArgs struct { AppNamespace string } -func Given(t *testing.T) *Context { - fixture.EnsureCleanState(t) +func Given(t *testing.T, opts ...fixture.TestOption) *Context { + fixture.EnsureCleanState(t, opts...) return GivenWithSameState(t) } @@ -64,7 +65,7 @@ func GivenWithNamespace(t *testing.T, namespace string) *Context { func GivenWithSameState(t *testing.T) *Context { // ARGOCE_E2E_DEFAULT_TIMEOUT can be used to override the default timeout // for any context. - timeout := env.ParseNumFromEnv("ARGOCD_E2E_DEFAULT_TIMEOUT", 10, 0, 180) + timeout := env.ParseNumFromEnv("ARGOCD_E2E_DEFAULT_TIMEOUT", 20, 0, 180) return &Context{ t: t, destServer: v1alpha1.KubernetesInternalAPIServerAddr, @@ -300,13 +301,6 @@ func (c *Context) ResourceFilter(filter settings.ResourcesFilter) *Context { return c } -// this both configures the plugin, but forces use of it -func (c *Context) ConfigManagementPlugin(plugin v1alpha1.ConfigManagementPlugin) *Context { - fixture.SetConfigManagementPlugins(plugin) - c.configManagementPlugin = plugin.Name - return c -} - func (c *Context) And(block func()) *Context { block() return c @@ -348,6 +342,11 @@ func (c *Context) Force() *Context { return c } +func (c *Context) ApplyOutOfSyncOnly() *Context { + c.applyOutOfSyncOnly = true + return c +} + func (c *Context) HelmPassCredentials() *Context { c.helmPassCredentials = true return c diff --git a/test/e2e/fixture/app/expectation.go b/test/e2e/fixture/app/expectation.go index 14f978c84a402..4d4918e981751 100644 --- a/test/e2e/fixture/app/expectation.go +++ b/test/e2e/fixture/app/expectation.go @@ -3,6 +3,7 @@ package app import ( "context" "fmt" + "reflect" "regexp" "strings" @@ -87,6 +88,26 @@ func NoConditions() Expectation { } } +func NoStatus() Expectation { + return func(c *Consequences) (state, string) { + message := "no status" + if reflect.ValueOf(c.app().Status).IsZero() { + return succeeded, message + } + return pending, message + } +} + +func StatusExists() Expectation { + return func(c *Consequences) (state, string) { + message := "status exists" + if !reflect.ValueOf(c.app().Status).IsZero() { + return succeeded, message + } + return pending, message + } +} + func Namespace(name string, block func(app *Application, ns *v1.Namespace)) Expectation { return func(c *Consequences) (state, string) { ns, err := namespace(name) @@ -195,6 +216,19 @@ func DoesNotExist() Expectation { } } +func DoesNotExistNow() Expectation { + return func(c *Consequences) (state, string) { + _, err := c.get() + if err != nil { + if apierr.IsNotFound(err) { + return succeeded, "app does not exist" + } + return failed, err.Error() + } + return failed, "app should not exist" + } +} + func Pod(predicate func(p v1.Pod) bool) Expectation { return func(c *Consequences) (state, string) { pods, err := pods() @@ -271,20 +305,31 @@ func event(namespace string, reason string, message string) Expectation { } func Event(reason string, message string) Expectation { - return event(fixture.ArgoCDNamespace, reason, message) + return event(fixture.TestNamespace(), reason, message) } func NamespacedEvent(namespace string, reason string, message string) Expectation { return event(namespace, reason, message) } -// asserts that the last command was successful -func Success(message string) Expectation { +// Success asserts that the last command was successful and that the output contains the given message. +func Success(message string, matchers ...func(string, string) bool) Expectation { + if len(matchers) == 0 { + matchers = append(matchers, strings.Contains) + } + match := func(actual, expected string) bool { + for i := range matchers { + if !matchers[i](actual, expected) { + return false + } + } + return true + } return func(c *Consequences) (state, string) { if c.actions.lastError != nil { return failed, "error" } - if !strings.Contains(c.actions.lastOutput, message) { + if !match(c.actions.lastOutput, message) { return failed, fmt.Sprintf("output did not contain '%s'", message) } return succeeded, fmt.Sprintf("no error and output contained '%s'", message) @@ -324,3 +369,10 @@ func ErrorRegex(messagePattern, err string) Expectation { return regexp.MustCompile(expected).MatchString(actual) }) } + +// SuccessRegex asserts that the last command was successful and output matches given regex expression +func SuccessRegex(messagePattern string) Expectation { + return Success(messagePattern, func(actual, expected string) bool { + return regexp.MustCompile(expected).MatchString(actual) + }) +} diff --git a/test/e2e/fixture/applicationsets/actions.go b/test/e2e/fixture/applicationsets/actions.go index 13198890e0efe..0b167c2b1a734 100644 --- a/test/e2e/fixture/applicationsets/actions.go +++ b/test/e2e/fixture/applicationsets/actions.go @@ -7,6 +7,8 @@ import ( "strings" "time" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + log "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/rbac/v1" @@ -14,9 +16,9 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/dynamic" "github.com/argoproj/argo-cd/v2/common" - argocommon "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" "github.com/argoproj/argo-cd/v2/util/clusterauth" @@ -62,6 +64,18 @@ func (a *Actions) Then() *Consequences { return &Consequences{a.context, a} } +func (a *Actions) SwitchToExternalNamespace(namespace utils.ExternalNamespace) *Actions { + a.context.switchToNamespace = namespace + log.Infof("switched to external namespace: %s", namespace) + return a +} + +func (a *Actions) SwitchToArgoCDNamespace() *Actions { + a.context.switchToNamespace = "" + log.Infof("switched to argocd namespace: %s", utils.ArgoCDNamespace) + return a +} + // CreateClusterSecret creates a faux cluster secret, with the given cluster server and cluster name (this cluster // will not actually be used by the Argo CD controller, but that's not needed for our E2E tests) func (a *Actions) CreateClusterSecret(secretName string, clusterName string, clusterServer string) *Actions { @@ -73,7 +87,7 @@ func (a *Actions) CreateClusterSecret(secretName string, clusterName string, clu // Look for a service account matching '*application-controller*' err := wait.Poll(500*time.Millisecond, 30*time.Second, func() (bool, error) { - serviceAccountList, err := fixtureClient.KubeClientset.CoreV1().ServiceAccounts(utils.ArgoCDNamespace).List(context.Background(), metav1.ListOptions{}) + serviceAccountList, err := fixtureClient.KubeClientset.CoreV1().ServiceAccounts(fixture.TestNamespace()).List(context.Background(), metav1.ListOptions{}) if err != nil { fmt.Println("Unable to retrieve ServiceAccount list", err) return false, nil @@ -100,16 +114,16 @@ func (a *Actions) CreateClusterSecret(secretName string, clusterName string, clu if err == nil { var bearerToken string - bearerToken, err = clusterauth.GetServiceAccountBearerToken(fixtureClient.KubeClientset, utils.ArgoCDNamespace, serviceAccountName, common.BearerTokenTimeout) + bearerToken, err = clusterauth.GetServiceAccountBearerToken(fixtureClient.KubeClientset, fixture.TestNamespace(), serviceAccountName, common.BearerTokenTimeout) // bearerToken secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Labels: map[string]string{ - argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster, - utils.TestingLabel: "true", + common.LabelKeySecretType: common.LabelValueSecretTypeCluster, + utils.TestingLabel: "true", }, }, Data: map[string][]byte{ @@ -141,7 +155,7 @@ func (a *Actions) CreateClusterSecret(secretName string, clusterName string, clu // DeleteClusterSecret deletes a faux cluster secret func (a *Actions) DeleteClusterSecret(secretName string) *Actions { - err := utils.GetE2EFixtureK8sClient().KubeClientset.CoreV1().Secrets(utils.ArgoCDNamespace).Delete(context.Background(), secretName, metav1.DeleteOptions{}) + err := utils.GetE2EFixtureK8sClient().KubeClientset.CoreV1().Secrets(fixture.TestNamespace()).Delete(context.Background(), secretName, metav1.DeleteOptions{}) a.describeAction = fmt.Sprintf("deleting cluster Secret '%s'", secretName) a.lastOutput, a.lastError = "", err @@ -153,7 +167,7 @@ func (a *Actions) DeleteClusterSecret(secretName string) *Actions { // DeleteConfigMap deletes a faux cluster secret func (a *Actions) DeleteConfigMap(configMapName string) *Actions { - err := utils.GetE2EFixtureK8sClient().KubeClientset.CoreV1().ConfigMaps(utils.ArgoCDNamespace).Delete(context.Background(), configMapName, metav1.DeleteOptions{}) + err := utils.GetE2EFixtureK8sClient().KubeClientset.CoreV1().ConfigMaps(fixture.TestNamespace()).Delete(context.Background(), configMapName, metav1.DeleteOptions{}) a.describeAction = fmt.Sprintf("deleting configMap '%s'", configMapName) a.lastOutput, a.lastError = "", err @@ -165,7 +179,7 @@ func (a *Actions) DeleteConfigMap(configMapName string) *Actions { // DeletePlacementDecision deletes a faux cluster secret func (a *Actions) DeletePlacementDecision(placementDecisionName string) *Actions { - err := utils.GetE2EFixtureK8sClient().DynamicClientset.Resource(pdGVR).Namespace(utils.ArgoCDNamespace).Delete(context.Background(), placementDecisionName, metav1.DeleteOptions{}) + err := utils.GetE2EFixtureK8sClient().DynamicClientset.Resource(pdGVR).Namespace(fixture.TestNamespace()).Delete(context.Background(), placementDecisionName, metav1.DeleteOptions{}) a.describeAction = fmt.Sprintf("deleting placement decision '%s'", placementDecisionName) a.lastOutput, a.lastError = "", err @@ -176,15 +190,15 @@ func (a *Actions) DeletePlacementDecision(placementDecisionName string) *Actions // Create a temporary namespace, from utils.ApplicationSet, for use by the test. // This namespace will be deleted on subsequent tests. -func (a *Actions) CreateNamespace() *Actions { +func (a *Actions) CreateNamespace(namespace string) *Actions { a.context.t.Helper() fixtureClient := utils.GetE2EFixtureK8sClient() _, err := fixtureClient.KubeClientset.CoreV1().Namespaces().Create(context.Background(), - &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: utils.ApplicationSetNamespace}}, metav1.CreateOptions{}) + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}, metav1.CreateOptions{}) - a.describeAction = fmt.Sprintf("creating namespace '%s'", utils.ApplicationSetNamespace) + a.describeAction = fmt.Sprintf("creating namespace '%s'", namespace) a.lastOutput, a.lastError = "", err a.verifyAction() @@ -195,17 +209,32 @@ func (a *Actions) CreateNamespace() *Actions { func (a *Actions) Create(appSet v1alpha1.ApplicationSet) *Actions { a.context.t.Helper() + fixtureClient := utils.GetE2EFixtureK8sClient() + appSet.APIVersion = "argoproj.io/v1alpha1" appSet.Kind = "ApplicationSet" - fixtureClient := utils.GetE2EFixtureK8sClient() - newResource, err := fixtureClient.AppSetClientset.Create(context.Background(), utils.MustToUnstructured(&appSet), metav1.CreateOptions{}) + var appSetClientSet dynamic.ResourceInterface + + if a.context.switchToNamespace != "" { + externalAppSetClientset, found := fixtureClient.ExternalAppSetClientsets[utils.ExternalNamespace(a.context.switchToNamespace)] + if !found { + a.lastOutput, a.lastError = "", fmt.Errorf("No external clientset found for %s", a.context.switchToNamespace) + return a + } + appSetClientSet = externalAppSetClientset + } else { + appSetClientSet = fixtureClient.AppSetClientset + } + + newResource, err := appSetClientSet.Create(context.Background(), utils.MustToUnstructured(&appSet), metav1.CreateOptions{}) if err == nil { a.context.name = newResource.GetName() + a.context.namespace = newResource.GetNamespace() } - a.describeAction = fmt.Sprintf("creating ApplicationSet '%s'", appSet.Name) + a.describeAction = fmt.Sprintf("creating ApplicationSet '%s/%s'", appSet.Namespace, appSet.Name) a.lastOutput, a.lastError = "", err a.verifyAction() @@ -218,8 +247,8 @@ func (a *Actions) CreatePlacementRoleAndRoleBinding() *Actions { var err error - _, err = fixtureClient.KubeClientset.RbacV1().Roles(utils.ArgoCDNamespace).Create(context.Background(), &v1.Role{ - ObjectMeta: metav1.ObjectMeta{Name: "placement-role", Namespace: utils.ArgoCDNamespace}, + _, err = fixtureClient.KubeClientset.RbacV1().Roles(fixture.TestNamespace()).Create(context.Background(), &v1.Role{ + ObjectMeta: metav1.ObjectMeta{Name: "placement-role", Namespace: fixture.TestNamespace()}, Rules: []v1.PolicyRule{ { Verbs: []string{"get", "list", "watch"}, @@ -233,13 +262,13 @@ func (a *Actions) CreatePlacementRoleAndRoleBinding() *Actions { } if err == nil { - _, err = fixtureClient.KubeClientset.RbacV1().RoleBindings(utils.ArgoCDNamespace).Create(context.Background(), + _, err = fixtureClient.KubeClientset.RbacV1().RoleBindings(fixture.TestNamespace()).Create(context.Background(), &v1.RoleBinding{ - ObjectMeta: metav1.ObjectMeta{Name: "placement-role-binding", Namespace: utils.ArgoCDNamespace}, + ObjectMeta: metav1.ObjectMeta{Name: "placement-role-binding", Namespace: fixture.TestNamespace()}, Subjects: []v1.Subject{ { Name: "argocd-applicationset-controller", - Namespace: utils.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), Kind: "ServiceAccount", }, }, @@ -267,14 +296,14 @@ func (a *Actions) CreatePlacementDecisionConfigMap(configMapName string) *Action fixtureClient := utils.GetE2EFixtureK8sClient() - _, err := fixtureClient.KubeClientset.CoreV1().ConfigMaps(utils.ArgoCDNamespace).Get(context.Background(), configMapName, metav1.GetOptions{}) + _, err := fixtureClient.KubeClientset.CoreV1().ConfigMaps(fixture.TestNamespace()).Get(context.Background(), configMapName, metav1.GetOptions{}) // Don't do anything if it exists if err == nil { return a } - _, err = fixtureClient.KubeClientset.CoreV1().ConfigMaps(utils.ArgoCDNamespace).Create(context.Background(), + _, err = fixtureClient.KubeClientset.CoreV1().ConfigMaps(fixture.TestNamespace()).Create(context.Background(), &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: configMapName, @@ -299,7 +328,7 @@ func (a *Actions) CreatePlacementDecision(placementDecisionName string) *Actions fixtureClient := utils.GetE2EFixtureK8sClient().DynamicClientset - _, err := fixtureClient.Resource(pdGVR).Namespace(utils.ArgoCDNamespace).Get( + _, err := fixtureClient.Resource(pdGVR).Namespace(fixture.TestNamespace()).Get( context.Background(), placementDecisionName, metav1.GetOptions{}) @@ -312,7 +341,7 @@ func (a *Actions) CreatePlacementDecision(placementDecisionName string) *Actions Object: map[string]interface{}{ "metadata": map[string]interface{}{ "name": placementDecisionName, - "namespace": utils.ArgoCDNamespace, + "namespace": fixture.TestNamespace(), }, "kind": "PlacementDecision", "apiVersion": "cluster.open-cluster-management.io/v1alpha1", @@ -320,7 +349,7 @@ func (a *Actions) CreatePlacementDecision(placementDecisionName string) *Actions }, } - _, err = fixtureClient.Resource(pdGVR).Namespace(utils.ArgoCDNamespace).Create( + _, err = fixtureClient.Resource(pdGVR).Namespace(fixture.TestNamespace()).Create( context.Background(), placementDecision, metav1.CreateOptions{}) @@ -336,7 +365,7 @@ func (a *Actions) StatusUpdatePlacementDecision(placementDecisionName string, cl a.context.t.Helper() fixtureClient := utils.GetE2EFixtureK8sClient().DynamicClientset - placementDecision, err := fixtureClient.Resource(pdGVR).Namespace(utils.ArgoCDNamespace).Get( + placementDecision, err := fixtureClient.Resource(pdGVR).Namespace(fixture.TestNamespace()).Get( context.Background(), placementDecisionName, metav1.GetOptions{}) @@ -346,7 +375,7 @@ func (a *Actions) StatusUpdatePlacementDecision(placementDecisionName string, cl } if err == nil { - _, err = fixtureClient.Resource(pdGVR).Namespace(utils.ArgoCDNamespace).UpdateStatus( + _, err = fixtureClient.Resource(pdGVR).Namespace(fixture.TestNamespace()).UpdateStatus( context.Background(), placementDecision, metav1.UpdateOptions{}) @@ -364,9 +393,22 @@ func (a *Actions) Delete() *Actions { fixtureClient := utils.GetE2EFixtureK8sClient() + var appSetClientSet dynamic.ResourceInterface + + if a.context.switchToNamespace != "" { + externalAppSetClientset, found := fixtureClient.ExternalAppSetClientsets[utils.ExternalNamespace(a.context.switchToNamespace)] + if !found { + a.lastOutput, a.lastError = "", fmt.Errorf("No external clientset found for %s", a.context.switchToNamespace) + return a + } + appSetClientSet = externalAppSetClientset + } else { + appSetClientSet = fixtureClient.AppSetClientset + } + deleteProp := metav1.DeletePropagationForeground - err := fixtureClient.AppSetClientset.Delete(context.Background(), a.context.name, metav1.DeleteOptions{PropagationPolicy: &deleteProp}) - a.describeAction = fmt.Sprintf("Deleting ApplicationSet '%s' %v", a.context.name, err) + err := appSetClientSet.Delete(context.Background(), a.context.name, metav1.DeleteOptions{PropagationPolicy: &deleteProp}) + a.describeAction = fmt.Sprintf("Deleting ApplicationSet '%s/%s' %v", a.context.namespace, a.context.name, err) a.lastOutput, a.lastError = "", err a.verifyAction() @@ -378,7 +420,20 @@ func (a *Actions) get() (*v1alpha1.ApplicationSet, error) { appSet := v1alpha1.ApplicationSet{} fixtureClient := utils.GetE2EFixtureK8sClient() - newResource, err := fixtureClient.AppSetClientset.Get(context.Background(), a.context.name, metav1.GetOptions{}) + + var appSetClientSet dynamic.ResourceInterface + + if a.context.switchToNamespace != "" { + externalAppSetClientset, found := fixtureClient.ExternalAppSetClientsets[utils.ExternalNamespace(a.context.switchToNamespace)] + if !found { + return nil, fmt.Errorf("No external clientset found for %s", a.context.switchToNamespace) + } + appSetClientSet = externalAppSetClientset + } else { + appSetClientSet = fixtureClient.AppSetClientset + } + + newResource, err := appSetClientSet.Get(context.Background(), a.context.name, metav1.GetOptions{}) if err != nil { return nil, err } @@ -413,10 +468,24 @@ func (a *Actions) Update(toUpdate func(*v1alpha1.ApplicationSet)) *Actions { if err == nil { // Keep trying to update until it succeeds, or the test times out toUpdate(appSet) - a.describeAction = fmt.Sprintf("updating ApplicationSet '%s'", appSet.Name) + a.describeAction = fmt.Sprintf("updating ApplicationSet '%s/%s'", appSet.Namespace, appSet.Name) fixtureClient := utils.GetE2EFixtureK8sClient() - _, err = fixtureClient.AppSetClientset.Update(context.Background(), utils.MustToUnstructured(&appSet), metav1.UpdateOptions{}) + + var appSetClientSet dynamic.ResourceInterface + + if a.context.switchToNamespace != "" { + externalAppSetClientset, found := fixtureClient.ExternalAppSetClientsets[utils.ExternalNamespace(a.context.switchToNamespace)] + if !found { + a.lastOutput, a.lastError = "", fmt.Errorf("No external clientset found for %s", a.context.switchToNamespace) + return a + } + appSetClientSet = externalAppSetClientset + } else { + appSetClientSet = fixtureClient.AppSetClientset + } + + _, err = appSetClientSet.Update(context.Background(), utils.MustToUnstructured(&appSet), metav1.UpdateOptions{}) if err != nil { mostRecentError = err @@ -444,5 +513,18 @@ func (a *Actions) verifyAction() { if !a.ignoreErrors { a.Then().Expect(Success("")) } +} + +func (a *Actions) AppSet(appName string, flags ...string) *Actions { + a.context.t.Helper() + args := []string{"app", "set", appName} + args = append(args, flags...) + a.runCli(args...) + return a +} +func (a *Actions) runCli(args ...string) { + a.context.t.Helper() + a.lastOutput, a.lastError = fixture.RunCli(args...) + a.verifyAction() } diff --git a/test/e2e/fixture/applicationsets/consequences.go b/test/e2e/fixture/applicationsets/consequences.go index e5d45cdf29f2e..db614f3cf3075 100644 --- a/test/e2e/fixture/applicationsets/consequences.go +++ b/test/e2e/fixture/applicationsets/consequences.go @@ -5,12 +5,14 @@ import ( "encoding/json" "time" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + "github.com/argoproj/pkg/errors" log "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/dynamic" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" ) @@ -60,7 +62,7 @@ func (c *Consequences) When() *Actions { return c.actions } -func (c *Consequences) app(name string) *argov1alpha1.Application { +func (c *Consequences) app(name string) *v1alpha1.Application { apps := c.apps() for index, app := range apps { @@ -72,14 +74,21 @@ func (c *Consequences) app(name string) *argov1alpha1.Application { return nil } -func (c *Consequences) apps() []argov1alpha1.Application { +func (c *Consequences) apps() []v1alpha1.Application { + + var namespace string + if c.context.switchToNamespace != "" { + namespace = string(c.context.switchToNamespace) + } else { + namespace = fixture.TestNamespace() + } fixtureClient := utils.GetE2EFixtureK8sClient() - list, err := fixtureClient.AppClientset.ArgoprojV1alpha1().Applications(utils.ArgoCDNamespace).List(context.Background(), metav1.ListOptions{}) + list, err := fixtureClient.AppClientset.ArgoprojV1alpha1().Applications(namespace).List(context.Background(), metav1.ListOptions{}) errors.CheckError(err) if list == nil { - list = &argov1alpha1.ApplicationList{} + list = &v1alpha1.ApplicationList{} } return list.Items @@ -88,7 +97,16 @@ func (c *Consequences) apps() []argov1alpha1.Application { func (c *Consequences) applicationSet(applicationSetName string) *v1alpha1.ApplicationSet { fixtureClient := utils.GetE2EFixtureK8sClient() - list, err := fixtureClient.AppSetClientset.Get(context.Background(), c.actions.context.name, metav1.GetOptions{}) + + var appSetClientSet dynamic.ResourceInterface + + if c.context.switchToNamespace != "" { + appSetClientSet = fixtureClient.ExternalAppSetClientsets[c.context.switchToNamespace] + } else { + appSetClientSet = fixtureClient.AppSetClientset + } + + list, err := appSetClientSet.Get(context.Background(), c.actions.context.name, metav1.GetOptions{}) errors.CheckError(err) var appSet v1alpha1.ApplicationSet diff --git a/test/e2e/fixture/applicationsets/context.go b/test/e2e/fixture/applicationsets/context.go index c6269c4b36b61..a7e91f4d0c8ff 100644 --- a/test/e2e/fixture/applicationsets/context.go +++ b/test/e2e/fixture/applicationsets/context.go @@ -4,7 +4,7 @@ import ( "testing" "time" - . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" ) // Context implements the "given" part of given/when/then @@ -12,11 +12,13 @@ type Context struct { t *testing.T // name is the ApplicationSet's name, created by a Create action - name string + name string + namespace string + switchToNamespace utils.ExternalNamespace } func Given(t *testing.T) *Context { - EnsureCleanState(t) + utils.EnsureCleanState(t) return &Context{t: t} } diff --git a/test/e2e/fixture/applicationsets/expectation.go b/test/e2e/fixture/applicationsets/expectation.go index e8be81b906d7e..990ad5f33dbfb 100644 --- a/test/e2e/fixture/applicationsets/expectation.go +++ b/test/e2e/fixture/applicationsets/expectation.go @@ -11,7 +11,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" ) @@ -58,13 +57,13 @@ func Error(message, err string) Expectation { // ApplicationsExist checks whether each of the 'expectedApps' exist in the namespace, and are // equivalent to provided values. -func ApplicationsExist(expectedApps []argov1alpha1.Application) Expectation { +func ApplicationsExist(expectedApps []v1alpha1.Application) Expectation { return func(c *Consequences) (state, string) { for _, expectedApp := range expectedApps { foundApp := c.app(expectedApp.Name) if foundApp == nil { - return pending, fmt.Sprintf("missing app '%s'", expectedApp.Name) + return pending, fmt.Sprintf("missing app '%s'", expectedApp.QualifiedName()) } if !appsAreEqual(expectedApp, *foundApp) { @@ -74,7 +73,7 @@ func ApplicationsExist(expectedApps []argov1alpha1.Application) Expectation { return failed, err.Error() } - return pending, fmt.Sprintf("apps are not equal: '%s', diff: %s\n", expectedApp.Name, diff) + return pending, fmt.Sprintf("apps are not equal: '%s', diff: %s\n", expectedApp.QualifiedName(), diff) } @@ -107,13 +106,13 @@ func ApplicationSetHasConditions(applicationSetName string, expectedConditions [ } // ApplicationsDoNotExist checks that each of the 'expectedApps' no longer exist in the namespace -func ApplicationsDoNotExist(expectedApps []argov1alpha1.Application) Expectation { +func ApplicationsDoNotExist(expectedApps []v1alpha1.Application) Expectation { return func(c *Consequences) (state, string) { for _, expectedApp := range expectedApps { foundApp := c.app(expectedApp.Name) if foundApp != nil { - return pending, fmt.Sprintf("app '%s' should no longer exist", expectedApp.Name) + return pending, fmt.Sprintf("app '%s' should no longer exist", expectedApp.QualifiedName()) } } @@ -124,7 +123,7 @@ func ApplicationsDoNotExist(expectedApps []argov1alpha1.Application) Expectation // Pod checks whether a specified condition is true for any of the pods in the namespace func Pod(predicate func(p corev1.Pod) bool) Expectation { return func(c *Consequences) (state, string) { - pods, err := pods(utils.ApplicationSetNamespace) + pods, err := pods(utils.ApplicationsResourcesNamespace) if err != nil { return failed, err.Error() } @@ -145,7 +144,7 @@ func pods(namespace string) (*corev1.PodList, error) { } // getDiff returns a string containing a comparison result of two applications (for test output/debug purposes) -func getDiff(orig, new argov1alpha1.Application) (string, error) { +func getDiff(orig, new v1alpha1.Application) (string, error) { bytes, _, err := diff.CreateTwoWayMergePatch(orig, new, orig) if err != nil { @@ -177,13 +176,13 @@ func getConditionDiff(orig, new []v1alpha1.ApplicationSetCondition) (string, err } // filterFields returns a copy of Application, but with unnecessary (for testing) fields removed -func filterFields(input argov1alpha1.Application) argov1alpha1.Application { +func filterFields(input v1alpha1.Application) v1alpha1.Application { spec := input.Spec metaCopy := input.ObjectMeta.DeepCopy() - output := argov1alpha1.Application{ + output := v1alpha1.Application{ ObjectMeta: metav1.ObjectMeta{ Labels: metaCopy.Labels, Annotations: metaCopy.Annotations, @@ -191,13 +190,13 @@ func filterFields(input argov1alpha1.Application) argov1alpha1.Application { Namespace: metaCopy.Namespace, Finalizers: metaCopy.Finalizers, }, - Spec: argov1alpha1.ApplicationSpec{ - Source: &argov1alpha1.ApplicationSource{ + Spec: v1alpha1.ApplicationSpec{ + Source: &v1alpha1.ApplicationSource{ Path: spec.GetSource().Path, RepoURL: spec.GetSource().RepoURL, TargetRevision: spec.GetSource().TargetRevision, }, - Destination: argov1alpha1.ApplicationDestination{ + Destination: v1alpha1.ApplicationDestination{ Server: spec.Destination.Server, Name: spec.Destination.Name, Namespace: spec.Destination.Namespace, @@ -227,7 +226,7 @@ func filterConditionFields(input *[]v1alpha1.ApplicationSetCondition) *[]v1alpha } // appsAreEqual returns true if the apps are equal, comparing only fields of interest -func appsAreEqual(one argov1alpha1.Application, two argov1alpha1.Application) bool { +func appsAreEqual(one v1alpha1.Application, two v1alpha1.Application) bool { return reflect.DeepEqual(filterFields(one), filterFields(two)) } diff --git a/test/e2e/fixture/applicationsets/utils/fixture.go b/test/e2e/fixture/applicationsets/utils/fixture.go index dc70ac2a02b4e..d4e23e5f5415d 100644 --- a/test/e2e/fixture/applicationsets/utils/fixture.go +++ b/test/e2e/fixture/applicationsets/utils/fixture.go @@ -13,8 +13,6 @@ import ( log "github.com/sirupsen/logrus" - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - "k8s.io/apimachinery/pkg/api/equality" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -24,18 +22,29 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + "github.com/argoproj/argo-cd/v2/util/errors" ) +type ExternalNamespace string + const ( // ArgoCDNamespace is the namespace into which Argo CD and ApplicationSet controller are deployed, // and in which Application resources should be created. ArgoCDNamespace = "argocd-e2e" - // ApplicationSetNamespace is the namespace into which temporary resources (such as Deployments/Pods/etc) + // ArgoCDExternalNamespace is an external namespace to test additional namespaces + ArgoCDExternalNamespace ExternalNamespace = "argocd-e2e-external" + + // ArgoCDExternalNamespace2 is an external namespace to test additional namespaces + ArgoCDExternalNamespace2 ExternalNamespace = "argocd-e2e-external-2" + + // ApplicationsResourcesNamespace is the namespace into which temporary resources (such as Deployments/Pods/etc) // can be deployed, such as using it as the target namespace in an Application resource. // Note: this is NOT the namespace the ApplicationSet controller is deployed to; see ArgoCDNamespace. - ApplicationSetNamespace = "applicationset-e2e" + ApplicationsResourcesNamespace = "applicationset-e2e" TmpDir = "/tmp/applicationset-e2e" TestingLabel = "e2e.argoproj.io" @@ -51,16 +60,30 @@ var ( // E2EFixtureK8sClient contains Kubernetes clients initialized from local k8s configuration type E2EFixtureK8sClient struct { - KubeClientset kubernetes.Interface - DynamicClientset dynamic.Interface - AppClientset appclientset.Interface - AppSetClientset dynamic.ResourceInterface + KubeClientset kubernetes.Interface + DynamicClientset dynamic.Interface + AppClientset appclientset.Interface + AppSetClientset dynamic.ResourceInterface + ExternalAppSetClientsets map[ExternalNamespace]dynamic.ResourceInterface +} + +func GetEnvWithDefault(envName, defaultValue string) string { + r := os.Getenv(envName) + if r == "" { + return defaultValue + } + return r +} + +// TestNamespace returns the namespace where Argo CD E2E test instance will be +// running in. +func TestNamespace() string { + return GetEnvWithDefault("ARGOCD_E2E_NAMESPACE", ArgoCDNamespace) } // GetE2EFixtureK8sClient initializes the Kubernetes clients (if needed), and returns the most recently initalized value. // Note: this requires a local Kubernetes configuration (for example, while running the E2E tests). func GetE2EFixtureK8sClient() *E2EFixtureK8sClient { - // Initialize the Kubernetes clients only on first use clientInitialized.Do(func() { @@ -73,13 +96,17 @@ func GetE2EFixtureK8sClient() *E2EFixtureK8sClient { KubeClientset: kubernetes.NewForConfigOrDie(config), } - internalClientVars.AppSetClientset = internalClientVars.DynamicClientset.Resource(v1alpha1.SchemeGroupVersion.WithResource("applicationsets")).Namespace(ArgoCDNamespace) + internalClientVars.AppSetClientset = internalClientVars.DynamicClientset.Resource(v1alpha1.SchemeGroupVersion.WithResource("applicationsets")).Namespace(TestNamespace()) + internalClientVars.ExternalAppSetClientsets = map[ExternalNamespace]dynamic.ResourceInterface{ + ArgoCDExternalNamespace: internalClientVars.DynamicClientset.Resource(v1alpha1.SchemeGroupVersion.WithResource("applicationsets")).Namespace(string(ArgoCDExternalNamespace)), + ArgoCDExternalNamespace2: internalClientVars.DynamicClientset.Resource(v1alpha1.SchemeGroupVersion.WithResource("applicationsets")).Namespace(string(ArgoCDExternalNamespace2)), + } }) return internalClientVars } -// EnsureCleanSlate ensures that the Kubernetes resources on the cluster are are in a 'clean' state, before a test is run. +// EnsureCleanSlate ensures that the Kubernetes resources on the cluster are in a 'clean' state, before a test is run. func EnsureCleanState(t *testing.T) { start := time.Now() @@ -89,19 +116,31 @@ func EnsureCleanState(t *testing.T) { policy := v1.DeletePropagationForeground // Delete the applicationset-e2e namespace, if it exists - err := fixtureClient.KubeClientset.CoreV1().Namespaces().Delete(context.Background(), ApplicationSetNamespace, v1.DeleteOptions{PropagationPolicy: &policy}) + err := fixtureClient.KubeClientset.CoreV1().Namespaces().Delete(context.Background(), ApplicationsResourcesNamespace, v1.DeleteOptions{PropagationPolicy: &policy}) if err != nil && !strings.Contains(err.Error(), "not found") { // 'not found' error is expected CheckError(err) } + // Delete the argocd-e2e-external namespace, if it exists + err2 := fixtureClient.KubeClientset.CoreV1().Namespaces().Delete(context.Background(), string(ArgoCDExternalNamespace), v1.DeleteOptions{PropagationPolicy: &policy}) + if err2 != nil && !strings.Contains(err2.Error(), "not found") { // 'not found' error is expected + CheckError(err2) + } + + // Delete the argocd-e2e-external namespace, if it exists + err3 := fixtureClient.KubeClientset.CoreV1().Namespaces().Delete(context.Background(), string(ArgoCDExternalNamespace2), v1.DeleteOptions{PropagationPolicy: &policy}) + if err3 != nil && !strings.Contains(err3.Error(), "not found") { // 'not found' error is expected + CheckError(err3) + } + // delete resources // kubectl delete applicationsets --all CheckError(fixtureClient.AppSetClientset.DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{})) // kubectl delete apps --all - CheckError(fixtureClient.AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{})) + CheckError(fixtureClient.AppClientset.ArgoprojV1alpha1().Applications(TestNamespace()).DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{})) // kubectl delete secrets -l e2e.argoproj.io=true - CheckError(fixtureClient.KubeClientset.CoreV1().Secrets(ArgoCDNamespace).DeleteCollection(context.Background(), + CheckError(fixtureClient.KubeClientset.CoreV1().Secrets(TestNamespace()).DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{LabelSelector: TestingLabel + "=true"})) // First we wait up to 30 seconds for all the ApplicationSets to delete, but we don't fail if they don't. @@ -121,14 +160,14 @@ func EnsureCleanState(t *testing.T) { // Remove finalizers from Argo CD Application resources in the namespace err = waitForSuccess(func() error { - appList, err := fixtureClient.AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).List(context.Background(), v1.ListOptions{}) + appList, err := fixtureClient.AppClientset.ArgoprojV1alpha1().Applications(TestNamespace()).List(context.Background(), v1.ListOptions{}) if err != nil { return err } for _, app := range appList.Items { t.Log("Removing finalizer for: ", app.Name) app.Finalizers = []string{} - _, err := fixtureClient.AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Update(context.TODO(), &app, v1.UpdateOptions{}) + _, err := fixtureClient.AppClientset.ArgoprojV1alpha1().Applications(TestNamespace()).Update(context.TODO(), &app, v1.UpdateOptions{}) if err != nil { return err } @@ -145,12 +184,24 @@ func EnsureCleanState(t *testing.T) { // create tmp dir FailOnErr(Run("", "mkdir", "-p", TmpDir)) + // We can switch user and as result in previous state we will have non-admin user, this case should be reset + fixture.LoginAs("admin") + log.WithFields(log.Fields{"duration": time.Since(start), "name": t.Name(), "id": id, "username": "admin", "password": "password"}).Info("clean state") } func waitForExpectedClusterState() error { fixtureClient := GetE2EFixtureK8sClient() + + SetProjectSpec(fixtureClient, "default", v1alpha1.AppProjectSpec{ + OrphanedResources: nil, + SourceRepos: []string{"*"}, + Destinations: []v1alpha1.ApplicationDestination{{Namespace: "*", Server: "*"}}, + ClusterResourceWhitelist: []v1.GroupKind{{Group: "*", Kind: "*"}}, + SourceNamespaces: []string{string(ArgoCDExternalNamespace), string(ArgoCDExternalNamespace2)}, + }) + // Wait up to 60 seconds for all the ApplicationSets to delete if err := waitForSuccess(func() error { list, err := fixtureClient.AppSetClientset.List(context.Background(), v1.ListOptions{}) @@ -169,7 +220,7 @@ func waitForExpectedClusterState() error { // Wait up to 60 seconds for all the Applications to delete if err := waitForSuccess(func() error { - appList, err := fixtureClient.AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).List(context.Background(), v1.ListOptions{}) + appList, err := fixtureClient.AppClientset.ArgoprojV1alpha1().Applications(TestNamespace()).List(context.Background(), v1.ListOptions{}) if err != nil { return err } @@ -184,31 +235,45 @@ func waitForExpectedClusterState() error { } // Wait up to 120 seconds for namespace to not exist - if err := waitForSuccess(func() error { - _, err := fixtureClient.KubeClientset.CoreV1().Namespaces().Get(context.Background(), ApplicationSetNamespace, v1.GetOptions{}) + for _, namespace := range []string{string(ApplicationsResourcesNamespace), string(ArgoCDExternalNamespace), string(ArgoCDExternalNamespace2)} { + // Wait up to 120 seconds for namespace to not exist + if err := waitForSuccess(func() error { + return cleanUpNamespace(fixtureClient, namespace) + }, time.Now().Add(120*time.Second)); err != nil { + return err + } + } - msg := "" + return nil +} - if err == nil { - msg = fmt.Sprintf("namespace '%s' still exists, after delete", ApplicationSetNamespace) - } +func SetProjectSpec(fixtureClient *E2EFixtureK8sClient, project string, spec v1alpha1.AppProjectSpec) { + proj, err := fixtureClient.AppClientset.ArgoprojV1alpha1().AppProjects(TestNamespace()).Get(context.Background(), project, v1.GetOptions{}) + errors.CheckError(err) + proj.Spec = spec + _, err = fixtureClient.AppClientset.ArgoprojV1alpha1().AppProjects(TestNamespace()).Update(context.Background(), proj, v1.UpdateOptions{}) + errors.CheckError(err) +} - if msg == "" && err != nil && strings.Contains(err.Error(), "not found") { - // Success is an error containing 'applicationset-e2e' not found. - return nil - } +func cleanUpNamespace(fixtureClient *E2EFixtureK8sClient, namespace string) error { + _, err := fixtureClient.KubeClientset.CoreV1().Namespaces().Get(context.Background(), namespace, v1.GetOptions{}) - if msg == "" { - msg = err.Error() - } + msg := "" - return fmt.Errorf(msg) + if err == nil { + msg = fmt.Sprintf("namespace '%s' still exists, after delete", namespace) + } - }, time.Now().Add(120*time.Second)); err != nil { - return err + if msg == "" && err != nil && strings.Contains(err.Error(), "not found") { + // Success is an error containing 'applicationset-e2e' not found. + return nil } - return nil + if msg == "" { + msg = err.Error() + } + + return fmt.Errorf(msg) } // waitForSuccess waits for the condition to return a non-error value. diff --git a/test/e2e/fixture/cluster/actions.go b/test/e2e/fixture/cluster/actions.go index 3f047e8f9b03e..0613c9a22cf15 100644 --- a/test/e2e/fixture/cluster/actions.go +++ b/test/e2e/fixture/cluster/actions.go @@ -45,10 +45,10 @@ func (a *Actions) Create(args ...string) *Actions { Cluster: &v1alpha1.Cluster{ Server: a.context.server, Name: a.context.name, - Config: v1alpha1.ClusterConfig{}, + Config: v1alpha1.ClusterConfig{BearerToken: a.context.bearerToken}, ConnectionState: v1alpha1.ConnectionState{}, ServerVersion: "", - Namespaces: nil, + Namespaces: a.context.namespaces, RefreshRequestedAt: nil, Info: v1alpha1.ClusterInfo{}, Shard: nil, diff --git a/test/e2e/fixture/cluster/context.go b/test/e2e/fixture/cluster/context.go index 236be6a3a3913..bd0102f891d71 100644 --- a/test/e2e/fixture/cluster/context.go +++ b/test/e2e/fixture/cluster/context.go @@ -12,12 +12,13 @@ import ( type Context struct { t *testing.T // seconds - timeout int - name string - project string - server string - upsert bool - namespaces []string + timeout int + name string + project string + server string + upsert bool + namespaces []string + bearerToken string } func Given(t *testing.T) *Context { @@ -67,6 +68,11 @@ func (c *Context) Project(project string) *Context { return c } +func (c *Context) BearerToken(bearerToken string) *Context { + c.bearerToken = bearerToken + return c +} + func (c *Context) Upsert(upsert bool) *Context { c.upsert = upsert return c diff --git a/test/e2e/fixture/fixture.go b/test/e2e/fixture/fixture.go index 0d758c8abbe74..f8dd60cb74974 100644 --- a/test/e2e/fixture/fixture.go +++ b/test/e2e/fixture/fixture.go @@ -15,7 +15,6 @@ import ( "github.com/argoproj/pkg/errors" jsonpatch "github.com/evanphx/json-patch" - "github.com/ghodss/yaml" log "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -23,12 +22,14 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/pkg/apiclient" sessionpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/session" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" + "github.com/argoproj/argo-cd/v2/util/env" . "github.com/argoproj/argo-cd/v2/util/errors" grpcutil "github.com/argoproj/argo-cd/v2/util/grpc" "github.com/argoproj/argo-cd/v2/util/io" @@ -57,28 +58,40 @@ const ( // cmp plugin sock file path PluginSockFilePath = "/app/config/plugin" + + E2ETestPrefix = "e2e-test-" ) const ( - EnvAdminUsername = "ARGOCD_E2E_ADMIN_USERNAME" - EnvAdminPassword = "ARGOCD_E2E_ADMIN_PASSWORD" + EnvAdminUsername = "ARGOCD_E2E_ADMIN_USERNAME" + EnvAdminPassword = "ARGOCD_E2E_ADMIN_PASSWORD" + EnvArgoCDServerName = "ARGOCD_E2E_SERVER_NAME" + EnvArgoCDRedisHAProxyName = "ARGOCD_E2E_REDIS_HAPROXY_NAME" + EnvArgoCDRedisName = "ARGOCD_E2E_REDIS_NAME" + EnvArgoCDRepoServerName = "ARGOCD_E2E_REPO_SERVER_NAME" + EnvArgoCDAppControllerName = "ARGOCD_E2E_APPLICATION_CONTROLLER_NAME" ) var ( - id string - deploymentNamespace string - name string - KubeClientset kubernetes.Interface - KubeConfig *rest.Config - DynamicClientset dynamic.Interface - AppClientset appclientset.Interface - ArgoCDClientset apiclient.Client - adminUsername string - AdminPassword string - apiServerAddress string - token string - plainText bool - testsRun map[string]bool + id string + deploymentNamespace string + name string + KubeClientset kubernetes.Interface + KubeConfig *rest.Config + DynamicClientset dynamic.Interface + AppClientset appclientset.Interface + ArgoCDClientset apiclient.Client + adminUsername string + AdminPassword string + apiServerAddress string + token string + plainText bool + testsRun map[string]bool + argoCDServerName string + argoCDRedisHAProxyName string + argoCDRedisName string + argoCDRepoServerName string + argoCDAppControllerName string ) type RepoURLType string @@ -92,6 +105,7 @@ type ACL struct { const ( RepoURLTypeFile = "file" RepoURLTypeHTTPS = "https" + RepoURLTypeHTTPSOrg = "https-org" RepoURLTypeHTTPSClientCert = "https-cc" RepoURLTypeHTTPSSubmodule = "https-sub" RepoURLTypeHTTPSSubmoduleParent = "https-par" @@ -103,6 +117,8 @@ const ( RepoURLTypeHelmOCI = "helm-oci" GitUsername = "admin" GitPassword = "password" + GithubAppID = "2978632978" + GithubAppInstallationID = "7893789433789" GpgGoodKeyID = "D56C4FCA57A46444" HelmOCIRegistryURL = "localhost:5000/myrepo" ) @@ -139,8 +155,7 @@ func GetEnvWithDefault(envName, defaultValue string) string { // IsRemote returns true when the tests are being run against a workload that // is running in a remote cluster. func IsRemote() bool { - r := os.Getenv("ARGOCD_E2E_REMOTE") - return r == "true" + return env.ParseBoolFromEnv("ARGOCD_E2E_REMOTE", false) } // IsLocal returns when the tests are being run against a local workload @@ -164,11 +179,26 @@ func init() { adminUsername = GetEnvWithDefault(EnvAdminUsername, defaultAdminUsername) AdminPassword = GetEnvWithDefault(EnvAdminPassword, defaultAdminPassword) + argoCDServerName = GetEnvWithDefault(EnvArgoCDServerName, common.DefaultServerName) + argoCDRedisHAProxyName = GetEnvWithDefault(EnvArgoCDRedisHAProxyName, common.DefaultRedisHaProxyName) + argoCDRedisName = GetEnvWithDefault(EnvArgoCDRedisName, common.DefaultRedisName) + argoCDRepoServerName = GetEnvWithDefault(EnvArgoCDRepoServerName, common.DefaultRepoServerName) + argoCDAppControllerName = GetEnvWithDefault(EnvArgoCDAppControllerName, common.DefaultApplicationControllerName) + dialTime := 30 * time.Second tlsTestResult, err := grpcutil.TestTLS(apiServerAddress, dialTime) CheckError(err) - ArgoCDClientset, err = apiclient.NewClient(&apiclient.ClientOptions{Insecure: true, ServerAddr: apiServerAddress, PlainText: !tlsTestResult.TLS}) + ArgoCDClientset, err = apiclient.NewClient(&apiclient.ClientOptions{ + Insecure: true, + ServerAddr: apiServerAddress, + PlainText: !tlsTestResult.TLS, + ServerName: argoCDServerName, + RedisHaProxyName: argoCDRedisHAProxyName, + RedisName: argoCDRedisName, + RepoServerName: argoCDRepoServerName, + AppControllerName: argoCDAppControllerName, + }) CheckError(err) plainText = !tlsTestResult.TLS @@ -214,10 +244,15 @@ func loginAs(username, password string) { token = sessionResponse.Token ArgoCDClientset, err = apiclient.NewClient(&apiclient.ClientOptions{ - Insecure: true, - ServerAddr: apiServerAddress, - AuthToken: token, - PlainText: plainText, + Insecure: true, + ServerAddr: apiServerAddress, + AuthToken: token, + PlainText: plainText, + ServerName: argoCDServerName, + RedisHaProxyName: argoCDRedisHAProxyName, + RedisName: argoCDRedisName, + RepoServerName: argoCDRepoServerName, + AppControllerName: argoCDAppControllerName, }) CheckError(err) } @@ -251,6 +286,7 @@ const ( EnvRepoURLTypeSSHSubmodule = "ARGOCD_E2E_REPO_SSH_SUBMODULE" EnvRepoURLTypeSSHSubmoduleParent = "ARGOCD_E2E_REPO_SSH_SUBMODULE_PARENT" EnvRepoURLTypeHTTPS = "ARGOCD_E2E_REPO_HTTPS" + EnvRepoURLTypeHTTPSOrg = "ARGOCD_E2E_REPO_HTTPS_ORG" EnvRepoURLTypeHTTPSClientCert = "ARGOCD_E2E_REPO_HTTPS_CLIENT_CERT" EnvRepoURLTypeHTTPSSubmodule = "ARGOCD_E2E_REPO_HTTPS_SUBMODULE" EnvRepoURLTypeHTTPSSubmoduleParent = "ARGOCD_E2E_REPO_HTTPS_SUBMODULE_PARENT" @@ -272,6 +308,9 @@ func RepoURL(urlType RepoURLType) string { // Git server via HTTPS case RepoURLTypeHTTPS: return GetEnvWithDefault(EnvRepoURLTypeHTTPS, "https://localhost:9443/argo-e2e/testdata.git") + // Git "organisation" via HTTPS + case RepoURLTypeHTTPSOrg: + return GetEnvWithDefault(EnvRepoURLTypeHTTPSOrg, "https://localhost:9443/argo-e2e") // Git server via HTTPS - Client Cert protected case RepoURLTypeHTTPSClientCert: return GetEnvWithDefault(EnvRepoURLTypeHTTPSClientCert, "https://localhost:9444/argo-e2e/testdata.git") @@ -443,17 +482,6 @@ func SetPermissions(permissions []ACL, username string, roleName string) { }) } -func SetConfigManagementPlugins(plugin ...v1alpha1.ConfigManagementPlugin) { - updateSettingConfigMap(func(cm *corev1.ConfigMap) error { - yamlBytes, err := yaml.Marshal(plugin) - if err != nil { - return err - } - cm.Data["configManagementPlugins"] = string(yamlBytes) - return nil - }) -} - func SetResourceFilter(filters settings.ResourcesFilter) { updateSettingConfigMap(func(cm *corev1.ConfigMap) error { exclusions, err := yaml.Marshal(filters.ResourceExclusions) @@ -514,7 +542,30 @@ func SetParamInNotificationsConfigMap(key, value string) { }) } -func EnsureCleanState(t *testing.T) { +type TestOption func(option *testOption) + +type testOption struct { + testdata string +} + +func newTestOption(opts ...TestOption) *testOption { + to := &testOption{ + testdata: "testdata", + } + for _, opt := range opts { + opt(to) + } + return to +} + +func WithTestData(testdata string) TestOption { + return func(option *testOption) { + option.testdata = testdata + } +} + +func EnsureCleanState(t *testing.T, opts ...TestOption) { + opt := newTestOption(opts...) // In large scenarios, we can skip tests that already run SkipIfAlreadyRun(t) // Register this test after it has been run & was successfull @@ -632,9 +683,9 @@ func EnsureCleanState(t *testing.T) { } // set-up tmp repo, must have unique name - FailOnErr(Run("", "cp", "-Rf", "testdata", repoDirectory())) + FailOnErr(Run("", "cp", "-Rf", opt.testdata, repoDirectory())) FailOnErr(Run(repoDirectory(), "chmod", "777", ".")) - FailOnErr(Run(repoDirectory(), "git", "init")) + FailOnErr(Run(repoDirectory(), "git", "init", "-b", "master")) FailOnErr(Run(repoDirectory(), "git", "add", ".")) FailOnErr(Run(repoDirectory(), "git", "commit", "-q", "-m", "initial commit")) @@ -647,9 +698,49 @@ func EnsureCleanState(t *testing.T) { FailOnErr(Run("", "kubectl", "create", "ns", DeploymentNamespace())) FailOnErr(Run("", "kubectl", "label", "ns", DeploymentNamespace(), TestingLabel+"=true")) + // delete old namespaces used by E2E tests + namespaces, err := KubeClientset.CoreV1().Namespaces().List(context.Background(), v1.ListOptions{}) + CheckError(err) + for _, namespace := range namespaces.Items { + if strings.HasPrefix(namespace.Name, E2ETestPrefix) { + FailOnErr(Run("", "kubectl", "delete", "ns", namespace.Name)) + } + } + + // delete old ClusterRoles that begin with "e2e-test-" prefix (E2ETestPrefix), which were created by tests + clusterRoles, err := KubeClientset.RbacV1().ClusterRoles().List(context.Background(), v1.ListOptions{}) + CheckError(err) + for _, clusterRole := range clusterRoles.Items { + if strings.HasPrefix(clusterRole.Name, E2ETestPrefix) { + FailOnErr(Run("", "kubectl", "delete", "clusterrole", clusterRole.Name)) + } + } + + // delete old ClusterRoleBindings that begin with "e2e-test-prefix", which were created by E2E tests + clusterRoleBindings, err := KubeClientset.RbacV1().ClusterRoleBindings().List(context.Background(), v1.ListOptions{}) + CheckError(err) + for _, clusterRoleBinding := range clusterRoleBindings.Items { + if strings.HasPrefix(clusterRoleBinding.Name, E2ETestPrefix) { + FailOnErr(Run("", "kubectl", "delete", "clusterrolebinding", clusterRoleBinding.Name)) + } + } + log.WithFields(log.Fields{"duration": time.Since(start), "name": t.Name(), "id": id, "username": "admin", "password": "password"}).Info("clean state") } +func RunCliWithRetry(maxRetries int, args ...string) (string, error) { + var out string + var err error + for i := 0; i < maxRetries; i++ { + out, err = RunCli(args...) + if err == nil { + break + } + time.Sleep(time.Second) + } + return out, err +} + func RunCli(args ...string) (string, error) { return RunCliWithStdin("", args...) } @@ -747,6 +838,26 @@ func AddSignedFile(path, contents string) { } } +func AddSignedTag(name string) { + prevGnuPGHome := os.Getenv("GNUPGHOME") + os.Setenv("GNUPGHOME", TmpDir+"/gpg") + defer os.Setenv("GNUPGHOME", prevGnuPGHome) + FailOnErr(Run(repoDirectory(), "git", "-c", fmt.Sprintf("user.signingkey=%s", GpgGoodKeyID), "tag", "-sm", "add signed tag", name)) + if IsRemote() { + FailOnErr(Run(repoDirectory(), "git", "push", "--tags", "-f", "origin", "master")) + } +} + +func AddTag(name string) { + prevGnuPGHome := os.Getenv("GNUPGHOME") + os.Setenv("GNUPGHOME", TmpDir+"/gpg") + defer os.Setenv("GNUPGHOME", prevGnuPGHome) + FailOnErr(Run(repoDirectory(), "git", "tag", name)) + if IsRemote() { + FailOnErr(Run(repoDirectory(), "git", "push", "--tags", "-f", "origin", "master")) + } +} + // create the resource by creating using "kubectl apply", with bonus templating func Declarative(filename string, values interface{}) (string, error) { @@ -762,13 +873,10 @@ func Declarative(filename string, values interface{}) (string, error) { } func CreateSubmoduleRepos(repoType string) { - oldEnv := os.Getenv("GIT_ALLOW_PROTOCOL") - CheckError(os.Setenv("GIT_ALLOW_PROTOCOL", "file")) - // set-up submodule repo FailOnErr(Run("", "cp", "-Rf", "testdata/git-submodule/", submoduleDirectory())) FailOnErr(Run(submoduleDirectory(), "chmod", "777", ".")) - FailOnErr(Run(submoduleDirectory(), "git", "init")) + FailOnErr(Run(submoduleDirectory(), "git", "init", "-b", "master")) FailOnErr(Run(submoduleDirectory(), "git", "add", ".")) FailOnErr(Run(submoduleDirectory(), "git", "commit", "-q", "-m", "initial commit")) @@ -780,9 +888,20 @@ func CreateSubmoduleRepos(repoType string) { // set-up submodule parent repo FailOnErr(Run("", "mkdir", submoduleParentDirectory())) FailOnErr(Run(submoduleParentDirectory(), "chmod", "777", ".")) - FailOnErr(Run(submoduleParentDirectory(), "git", "init")) + FailOnErr(Run(submoduleParentDirectory(), "git", "init", "-b", "master")) FailOnErr(Run(submoduleParentDirectory(), "git", "add", ".")) - FailOnErr(Run(submoduleParentDirectory(), "git", "submodule", "add", "-b", "master", "../submodule.git", "submodule/test")) + if IsRemote() { + FailOnErr(Run(submoduleParentDirectory(), "git", "submodule", "add", "-b", "master", os.Getenv("ARGOCD_E2E_GIT_SERVICE_SUBMODULE"), "submodule/test")) + } else { + oldAllowProtocol, isAllowProtocolSet := os.LookupEnv("GIT_ALLOW_PROTOCOL") + CheckError(os.Setenv("GIT_ALLOW_PROTOCOL", "file")) + FailOnErr(Run(submoduleParentDirectory(), "git", "submodule", "add", "-b", "master", "../submodule.git", "submodule/test")) + if isAllowProtocolSet { + CheckError(os.Setenv("GIT_ALLOW_PROTOCOL", oldAllowProtocol)) + } else { + CheckError(os.Unsetenv("GIT_ALLOW_PROTOCOL")) + } + } if repoType == "ssh" { FailOnErr(Run(submoduleParentDirectory(), "git", "config", "--file=.gitmodules", "submodule.submodule/test.url", RepoURL(RepoURLTypeSSHSubmodule))) } else if repoType == "https" { @@ -795,8 +914,18 @@ func CreateSubmoduleRepos(repoType string) { FailOnErr(Run(submoduleParentDirectory(), "git", "remote", "add", "origin", os.Getenv("ARGOCD_E2E_GIT_SERVICE_SUBMODULE_PARENT"))) FailOnErr(Run(submoduleParentDirectory(), "git", "push", "origin", "master", "-f")) } +} - CheckError(os.Setenv("GIT_ALLOW_PROTOCOL", oldEnv)) +func RemoveSubmodule() { + log.Info("removing submodule") + + FailOnErr(Run(submoduleParentDirectory(), "git", "rm", "submodule/test")) + FailOnErr(Run(submoduleParentDirectory(), "touch", "submodule/.gitkeep")) + FailOnErr(Run(submoduleParentDirectory(), "git", "add", "submodule/.gitkeep")) + FailOnErr(Run(submoduleParentDirectory(), "git", "commit", "-m", "remove submodule")) + if IsRemote() { + FailOnErr(Run(submoduleParentDirectory(), "git", "push", "-f", "origin", "master")) + } } // RestartRepoServer performs a restart of the repo server deployment and waits @@ -809,8 +938,10 @@ func RestartRepoServer() { if prefix != "" { workload = prefix + "-repo-server" } - FailOnErr(Run("", "kubectl", "rollout", "restart", "deployment", workload)) - FailOnErr(Run("", "kubectl", "rollout", "status", "deployment", workload)) + FailOnErr(Run("", "kubectl", "rollout", "-n", TestNamespace(), "restart", "deployment", workload)) + FailOnErr(Run("", "kubectl", "rollout", "-n", TestNamespace(), "status", "deployment", workload)) + // wait longer to avoid error on s390x + time.Sleep(10 * time.Second) } } @@ -824,8 +955,8 @@ func RestartAPIServer() { if prefix != "" { workload = prefix + "-server" } - FailOnErr(Run("", "kubectl", "rollout", "restart", "deployment", workload)) - FailOnErr(Run("", "kubectl", "rollout", "status", "deployment", workload)) + FailOnErr(Run("", "kubectl", "rollout", "-n", TestNamespace(), "restart", "deployment", workload)) + FailOnErr(Run("", "kubectl", "rollout", "-n", TestNamespace(), "status", "deployment", workload)) } } diff --git a/test/e2e/fixture/http.go b/test/e2e/fixture/http.go index 58c56046d3858..00c123ab5d893 100644 --- a/test/e2e/fixture/http.go +++ b/test/e2e/fixture/http.go @@ -2,6 +2,7 @@ package fixture import ( "bytes" + "crypto/tls" "encoding/json" "io" "net/http" @@ -27,7 +28,15 @@ func DoHttpRequest(method string, path string, data ...byte) (*http.Response, er return nil, err } req.AddCookie(&http.Cookie{Name: common.AuthCookieName, Value: token}) - return http.DefaultClient.Do(req) + req.Header.Set("Content-Type", "application/json") + + httpClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: IsRemote()}, + }, + } + + return httpClient.Do(req) } // DoHttpJsonRequest executes a http request against the Argo CD API server and unmarshals the response body as JSON diff --git a/test/e2e/fixture/project/actions.go b/test/e2e/fixture/project/actions.go index 81943c4fc2b6c..61a2ad90615fb 100644 --- a/test/e2e/fixture/project/actions.go +++ b/test/e2e/fixture/project/actions.go @@ -52,10 +52,10 @@ func (a *Actions) AddSource(repo string) *Actions { } func (a *Actions) UpdateProject(updater func(project *v1alpha1.AppProject)) *Actions { - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.TODO(), a.context.name, v1.GetOptions{}) + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.TODO(), a.context.name, v1.GetOptions{}) require.NoError(a.context.t, err) updater(proj) - _, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Update(context.TODO(), proj, v1.UpdateOptions{}) + _, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Update(context.TODO(), proj, v1.UpdateOptions{}) require.NoError(a.context.t, err) return a } diff --git a/test/e2e/git_submodule_test.go b/test/e2e/git_submodule_test.go index 27476db84a1ed..525c13d4b35ef 100644 --- a/test/e2e/git_submodule_test.go +++ b/test/e2e/git_submodule_test.go @@ -40,3 +40,25 @@ func TestGitSubmoduleHTTPSSupport(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(Pod(func(p v1.Pod) bool { return p.Name == "pod-in-submodule" })) } + +func TestGitSubmoduleRemovalSupport(t *testing.T) { + Given(t). + RepoURLType(fixture.RepoURLTypeSSHSubmoduleParent). + Path("submodule"). + Recurse(). + CustomSSHKnownHostsAdded(). + SubmoduleSSHRepoURLAdded(true). + When(). + CreateFromFile(func(app *Application) {}). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(Pod(func(p v1.Pod) bool { return p.Name == "pod-in-submodule" })). + When(). + RemoveSubmodule(). + Refresh(RefreshTypeNormal). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(NotPod(func(p v1.Pod) bool { return p.Name == "pod-in-submodule" })) +} diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 318fad17d97eb..5fd774ea0c46d 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -6,6 +6,7 @@ import ( "net" "net/http" "os" + "strings" "testing" "github.com/argoproj/gitops-engine/pkg/health" @@ -19,6 +20,7 @@ import ( "github.com/argoproj/argo-cd/v2/test/e2e/fixture" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" + projectFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/project" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/repos" . "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/settings" @@ -200,7 +202,7 @@ func TestHelmValuesLiteralFileLocal(t *testing.T) { if err != nil { panic(err) } - assert.Equal(t, string(data), app.Spec.GetSource().Helm.Values) + assert.Equal(t, strings.TrimSuffix(string(data), "\n"), app.Spec.GetSource().Helm.ValuesString()) }). When(). AppUnSet("--values-literal"). @@ -242,7 +244,7 @@ func TestHelmValuesLiteralFileRemote(t *testing.T) { AppSet("--values-literal-file", "http://"+address). Then(). And(func(app *Application) { - assert.Equal(t, "a: b", app.Spec.GetSource().Helm.Values) + assert.Equal(t, "a: b", app.Spec.GetSource().Helm.ValuesString()) }). When(). AppUnSet("--values-literal"). @@ -399,6 +401,45 @@ func TestHelmWithMultipleDependencies(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)) } +func TestHelmDependenciesPermissionDenied(t *testing.T) { + SkipOnEnv(t, "HELM") + + projName := "argo-helm-project-denied" + projectFixture. + Given(t). + Name(projName). + Destination("*,*"). + When(). + Create(). + AddSource(RepoURL(RepoURLTypeFile)) + + expectedErr := fmt.Sprintf("helm repos localhost:5000/myrepo are not permitted in project '%s'", projName) + GivenWithSameState(t). + Project(projName). + Path("helm-oci-with-dependencies"). + CustomCACertAdded(). + HelmHTTPSCredentialsUserPassAdded(). + HelmPassCredentials(). + When(). + IgnoreErrors(). + CreateApp(). + Then(). + Expect(Error("", expectedErr)) + + expectedErr = fmt.Sprintf("helm repos https://localhost:9443/argo-e2e/testdata.git/helm-repo/local, https://localhost:9443/argo-e2e/testdata.git/helm-repo/local2 are not permitted in project '%s'", projName) + GivenWithSameState(t). + Project(projName). + Path("helm-with-multiple-dependencies-permission-denied"). + CustomCACertAdded(). + HelmHTTPSCredentialsUserPassAdded(). + HelmPassCredentials(). + When(). + IgnoreErrors(). + CreateApp(). + Then(). + Expect(Error("", expectedErr)) +} + func TestHelmWithDependenciesLegacyRepo(t *testing.T) { SkipOnEnv(t, "HELM") testHelmWithDependencies(t, "helm-with-dependencies", true) @@ -413,13 +454,13 @@ func testHelmWithDependencies(t *testing.T, chartPath string, legacyRepo bool) { if legacyRepo { ctx.And(func() { FailOnErr(fixture.Run("", "kubectl", "create", "secret", "generic", "helm-repo", - "-n", fixture.ArgoCDNamespace, + "-n", fixture.TestNamespace(), fmt.Sprintf("--from-file=certSecret=%s", repos.CertPath), fmt.Sprintf("--from-file=keySecret=%s", repos.CertKeyPath), fmt.Sprintf("--from-literal=username=%s", GitUsername), fmt.Sprintf("--from-literal=password=%s", GitPassword), )) - FailOnErr(fixture.KubeClientset.CoreV1().Secrets(fixture.ArgoCDNamespace).Patch(context.Background(), + FailOnErr(fixture.KubeClientset.CoreV1().Secrets(fixture.TestNamespace()).Patch(context.Background(), "helm-repo", types.MergePatchType, []byte(`{"metadata": { "labels": {"e2e.argoproj.io": "true"} }}`), metav1.PatchOptions{})) fixture.SetHelmRepos(settings.HelmRepoCredentials{ diff --git a/test/e2e/hook_test.go b/test/e2e/hook_test.go index f6bb1be872ac6..2db8ff87795ad 100644 --- a/test/e2e/hook_test.go +++ b/test/e2e/hook_test.go @@ -1,12 +1,14 @@ package e2e import ( + "context" "fmt" "testing" "time" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/argoproj/gitops-engine/pkg/health" . "github.com/argoproj/gitops-engine/pkg/sync/common" @@ -48,6 +50,24 @@ func testHookSuccessful(t *testing.T, hookType HookType) { Expect(ResourceResultIs(ResourceResult{Version: "v1", Kind: "Pod", Namespace: DeploymentNamespace(), Name: "hook", Message: "pod/hook created", HookType: hookType, HookPhase: OperationSucceeded, SyncPhase: SyncPhase(hookType)})) } +func TestPostDeleteHook(t *testing.T) { + Given(t). + Path("post-delete-hook"). + When(). + CreateApp(). + Refresh(RefreshTypeNormal). + Delete(true). + Then(). + Expect(DoesNotExist()). + AndAction(func() { + hooks, err := KubeClientset.CoreV1().Pods(DeploymentNamespace()).List(context.Background(), metav1.ListOptions{}) + CheckError(err) + assert.Len(t, hooks.Items, 1) + assert.Equal(t, "hook", hooks.Items[0].Name) + }) + +} + // make sure that that hooks do not appear in "argocd app diff" func TestHookDiff(t *testing.T) { Given(t). diff --git a/test/e2e/jsonnet_test.go b/test/e2e/jsonnet_test.go index 7cc50d2bccab5..cad88f34a0048 100644 --- a/test/e2e/jsonnet_test.go +++ b/test/e2e/jsonnet_test.go @@ -102,7 +102,7 @@ func TestJsonnetExtVarEnv(t *testing.T) { }) } -//Jsonnet file located in nested sub directory uses import +// Jsonnet file located in nested sub directory uses import func TestJsonnetNestedDirWithImports(t *testing.T) { Given(t). Path("jsonnet-nested-dir-with-imports/apps"). diff --git a/test/e2e/kustomize_test.go b/test/e2e/kustomize_test.go index e6840e7b95eba..862e55c9e9502 100644 --- a/test/e2e/kustomize_test.go +++ b/test/e2e/kustomize_test.go @@ -1,6 +1,7 @@ package e2e import ( + "strconv" "testing" "github.com/argoproj/gitops-engine/pkg/health" @@ -146,7 +147,7 @@ func TestKustomizeBuildOptionsLoadRestrictor(t *testing.T) { Path(guestbookPath). And(func() { errors.FailOnErr(fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", - "-n", fixture.ArgoCDNamespace, + "-n", fixture.TestNamespace(), "-p", `{ "data": { "kustomize.buildOptions": "--load-restrictor LoadRestrictionsNone" } }`)) }). When(). @@ -160,7 +161,7 @@ func TestKustomizeBuildOptionsLoadRestrictor(t *testing.T) { Given(). And(func() { errors.FailOnErr(fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", - "-n", fixture.ArgoCDNamespace, + "-n", fixture.TestNamespace(), "-p", `{ "data": { "kustomize.buildOptions": "" } }`)) }) } @@ -179,6 +180,45 @@ func TestKustomizeImages(t *testing.T) { }) } +// make sure we we can invoke the CLI to replace replicas and actual deployment is set to correct value +func TestKustomizeReplicas2AppSource(t *testing.T) { + deploymentName := "guestbook-ui" + deploymentReplicas := 2 + checkReplicasFor := func(kind string) func(app *Application) { + return func(app *Application) { + name := deploymentName + replicas, err := fixture.Run( + "", "kubectl", "-n="+fixture.DeploymentNamespace(), + "get", kind, name, + "-ojsonpath={.spec.replicas}") + assert.NoError(t, err) + assert.Equal(t, strconv.Itoa(deploymentReplicas), replicas, "wrong value of replicas %s %s", kind, name) + } + } + + Given(t). + Path("guestbook"). + When(). + CreateApp(). + AppSet("--kustomize-replica", deploymentName+"=2"). + Then(). + And(func(app *Application) { + assert.Equal(t, deploymentName, app.Spec.Source.Kustomize.Replicas[0].Name) + }). + And(func(app *Application) { + assert.Equal(t, deploymentReplicas, int(app.Spec.Source.Kustomize.Replicas[0].Count.IntVal)) + }). // check Kustomize CLI + Expect(Success("")). + When(). + Sync(). + Then(). + Expect(Success("")). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)). + And(checkReplicasFor("Deployment")) +} + // make sure we we can invoke the CLI to set namesuffix func TestKustomizeNameSuffix(t *testing.T) { Given(t). @@ -223,3 +263,27 @@ func TestKustomizeUnsetOverride(t *testing.T) { assert.Nil(t, app.Spec.GetSource().Kustomize) }) } + +// make sure we we can invoke the CLI to set and unset Deployment +func TestKustomizeUnsetOverrideDeployment(t *testing.T) { + deploymentName := "guestbook-ui" + deploymentReplicas := int32(2) + Given(t). + Path("guestbook"). + When(). // Replicas + CreateApp(). + AppSet("--kustomize-replica", deploymentName+"=2"). + Then(). + And(func(app *Application) { + assert.Equal(t, deploymentName, app.Spec.Source.Kustomize.Replicas[0].Name) + }). + And(func(app *Application) { + assert.Equal(t, deploymentReplicas, app.Spec.Source.Kustomize.Replicas[0].Count.IntVal) + }). + When(). + AppUnSet("--kustomize-replica", deploymentName). + Then(). + And(func(app *Application) { + assert.Nil(t, app.Spec.Source.Kustomize) + }) +} diff --git a/test/e2e/matrix_e2e_test.go b/test/e2e/matrix_e2e_test.go index f6b923f24b7a4..9fc023ecaaf69 100644 --- a/test/e2e/matrix_e2e_test.go +++ b/test/e2e/matrix_e2e_test.go @@ -11,18 +11,20 @@ import ( argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) func TestListMatrixGenerator(t *testing.T) { generateExpectedApp := func(cluster, name string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%s-%s", cluster, name), - Namespace: utils.ArgoCDNamespace, + Namespace: utils.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -141,12 +143,12 @@ func TestClusterMatrixGenerator(t *testing.T) { generateExpectedApp := func(cluster, name string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%s-%s", cluster, name), - Namespace: utils.ArgoCDNamespace, + Namespace: utils.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -263,3 +265,304 @@ func TestClusterMatrixGenerator(t *testing.T) { When(). Delete().Then().Expect(ApplicationsDoNotExist(expectedAppsNewNamespace)) } + +func TestMatrixTerminalMatrixGeneratorSelector(t *testing.T) { + generateExpectedApp := func(cluster, name string) argov1alpha1.Application { + return argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s", cluster, name), + Namespace: utils.TestNamespace(), + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: name, + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: name, + }, + }, + } + } + + expectedApps1 := []argov1alpha1.Application{ + generateExpectedApp("cluster1", "kustomize-guestbook"), + generateExpectedApp("cluster1", "helm-guestbook"), + generateExpectedApp("cluster1", "ksonnet-guestbook"), + } + expectedApps2 := []argov1alpha1.Application{ + generateExpectedApp("cluster2", "kustomize-guestbook"), + generateExpectedApp("cluster2", "helm-guestbook"), + generateExpectedApp("cluster2", "ksonnet-guestbook"), + } + + Given(t). + // Create ApplicationSet with LabelSelector on an ApplicationSetTerminalGenerator + When(). + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "matrix-generator-nested-matrix", + }, + Spec: v1alpha1.ApplicationSetSpec{ + ApplyNestedSelectors: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{values.name}}-{{path.basename}}"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "{{path}}", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "{{path.basename}}", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + Matrix: &v1alpha1.MatrixGenerator{ + Generators: []v1alpha1.ApplicationSetNestedGenerator{ + { + Matrix: toAPIExtensionsJSON(t, &v1alpha1.NestedMatrixGenerator{ + Generators: []v1alpha1.ApplicationSetTerminalGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + {Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "values": {"name": "cluster1"}}`)}, + {Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "values": {"name": "cluster2"}}`)}, + }, + }, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "values.name": "cluster1", + }, + }, + }, + { + Git: &v1alpha1.GitGenerator{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + Directories: []v1alpha1.GitDirectoryGeneratorItem{ + { + Path: "*guestbook*", + }, + }, + }, + }, + }, + }), + }, + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + {Raw: []byte(`{}`)}, + }, + }, + }, + }, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist(expectedApps1)).Expect(ApplicationsDoNotExist(expectedApps2)). + + // Update the ApplicationSetTerminalGenerator LabelSelector, and verify the Applications are deleted and created + When(). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Generators[0].Matrix.Generators[0].Matrix = toAPIExtensionsJSON(t, &v1alpha1.NestedMatrixGenerator{ + Generators: []v1alpha1.ApplicationSetTerminalGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + {Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "values": {"name": "cluster1"}}`)}, + {Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "values": {"name": "cluster2"}}`)}, + }, + }, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "values.name": "cluster2", + }, + }, + }, + { + Git: &v1alpha1.GitGenerator{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + Directories: []v1alpha1.GitDirectoryGeneratorItem{ + { + Path: "*guestbook*", + }, + }, + }, + }, + }, + }) + }).Then().Expect(ApplicationsExist(expectedApps2)).Expect(ApplicationsDoNotExist(expectedApps1)). + + // Set ApplyNestedSelector to false and verify all Applications are created + When(). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.ApplyNestedSelectors = false + }).Then().Expect(ApplicationsExist(expectedApps1)).Expect(ApplicationsExist(expectedApps2)). + + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist(expectedApps1)).Expect(ApplicationsDoNotExist(expectedApps2)) +} + +func TestMatrixTerminalMergeGeneratorSelector(t *testing.T) { + generateExpectedApp := func(name, nameSuffix string) argov1alpha1.Application { + return argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s", name, nameSuffix), + Namespace: utils.TestNamespace(), + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: name, + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: name, + }, + }, + } + } + + expectedApps1 := []argov1alpha1.Application{ + generateExpectedApp("kustomize-guestbook", "1"), + } + expectedApps2 := []argov1alpha1.Application{ + generateExpectedApp("helm-guestbook", "2"), + } + + Given(t). + // Create ApplicationSet with LabelSelector on an ApplicationSetTerminalGenerator + When(). + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "matrix-generator-nested-merge", + }, + Spec: v1alpha1.ApplicationSetSpec{ + ApplyNestedSelectors: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{path.basename}}-{{name-suffix}}"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "{{path}}", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "{{path.basename}}", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + Matrix: &v1alpha1.MatrixGenerator{ + Generators: []v1alpha1.ApplicationSetNestedGenerator{ + { + Merge: toAPIExtensionsJSON(t, &v1alpha1.NestedMergeGenerator{ + MergeKeys: []string{"path.basename"}, + Generators: []v1alpha1.ApplicationSetTerminalGenerator{ + { + Git: &v1alpha1.GitGenerator{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + Directories: []v1alpha1.GitDirectoryGeneratorItem{ + { + Path: "*guestbook*", + }, + }, + }, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "path.basename": "kustomize-guestbook", + }, + }, + }, + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + {Raw: []byte(`{"path.basename": "kustomize-guestbook", "name-suffix": "1"}`)}, + {Raw: []byte(`{"path.basename": "helm-guestbook", "name-suffix": "2"}`)}, + }, + }, + }, + }, + }), + }, + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + {Raw: []byte(`{}`)}, + }, + }, + }, + }, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist(expectedApps1)).Expect(ApplicationsDoNotExist(expectedApps2)). + + // Update the ApplicationSetTerminalGenerator LabelSelector, and verify the Applications are deleted and created + When(). + Update(func(appset *v1alpha1.ApplicationSet) { + + appset.Spec.Generators[0].Matrix.Generators[0].Merge = toAPIExtensionsJSON(t, &v1alpha1.NestedMergeGenerator{ + MergeKeys: []string{"path.basename"}, + Generators: []v1alpha1.ApplicationSetTerminalGenerator{ + { + Git: &v1alpha1.GitGenerator{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + Directories: []v1alpha1.GitDirectoryGeneratorItem{ + { + Path: "*guestbook*", + }, + }, + }, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "path.basename": "helm-guestbook", + }, + }, + }, + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + {Raw: []byte(`{"path.basename": "kustomize-guestbook", "name-suffix": "1"}`)}, + {Raw: []byte(`{"path.basename": "helm-guestbook", "name-suffix": "2"}`)}, + }, + }, + }, + }, + }) + }).Then().Expect(ApplicationsExist(expectedApps2)).Expect(ApplicationsDoNotExist(expectedApps1)). + + // Set ApplyNestedSelector to false and verify all Applications are created + When(). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.ApplyNestedSelectors = false + }).Then().Expect(ApplicationsExist(expectedApps1)).Expect(ApplicationsExist(expectedApps2)). + + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist(expectedApps1)).Expect(ApplicationsDoNotExist(expectedApps2)) +} diff --git a/test/e2e/merge_e2e_test.go b/test/e2e/merge_e2e_test.go index 6a3de30f19f84..9ad148b65b985 100644 --- a/test/e2e/merge_e2e_test.go +++ b/test/e2e/merge_e2e_test.go @@ -12,18 +12,20 @@ import ( argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application" ) func TestListMergeGenerator(t *testing.T) { generateExpectedApp := func(name, nameSuffix string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%s-%s", name, nameSuffix), - Namespace: utils.ArgoCDNamespace, + Namespace: utils.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -138,12 +140,12 @@ func TestClusterMergeGenerator(t *testing.T) { generateExpectedApp := func(cluster, name, nameSuffix string) argov1alpha1.Application { return argov1alpha1.Application{ TypeMeta: metav1.TypeMeta{ - Kind: "Application", + Kind: application.ApplicationKind, APIVersion: "argoproj.io/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%s-%s-%s", cluster, name, nameSuffix), - Namespace: utils.ArgoCDNamespace, + Namespace: utils.TestNamespace(), Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, }, Spec: argov1alpha1.ApplicationSpec{ @@ -279,6 +281,157 @@ func TestClusterMergeGenerator(t *testing.T) { Delete().Then().Expect(ApplicationsDoNotExist(expectedAppsNewNamespace)) } +func TestMergeTerminalMergeGeneratorSelector(t *testing.T) { + generateExpectedApp := func(name, nameSuffix string) argov1alpha1.Application { + return argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s", name, nameSuffix), + Namespace: utils.TestNamespace(), + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: name, + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: name, + }, + }, + } + } + + expectedApps1 := []argov1alpha1.Application{ + generateExpectedApp("kustomize-guestbook", "1"), + } + expectedApps2 := []argov1alpha1.Application{ + generateExpectedApp("helm-guestbook", "2"), + } + + Given(t). + // Create ApplicationSet with LabelSelector on an ApplicationSetTerminalGenerator + When(). + Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "merge-generator-nested-merge", + }, + Spec: v1alpha1.ApplicationSetSpec{ + ApplyNestedSelectors: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{path.basename}}-{{name-suffix}}"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "{{path}}", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "{{path.basename}}", + }, + }, + }, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + Merge: &v1alpha1.MergeGenerator{ + MergeKeys: []string{"path.basename"}, + Generators: []v1alpha1.ApplicationSetNestedGenerator{ + { + Merge: toAPIExtensionsJSON(t, &v1alpha1.NestedMergeGenerator{ + MergeKeys: []string{"path.basename"}, + Generators: []v1alpha1.ApplicationSetTerminalGenerator{ + { + Git: &v1alpha1.GitGenerator{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + Directories: []v1alpha1.GitDirectoryGeneratorItem{ + { + Path: "*guestbook*", + }, + }, + }, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "path.basename": "kustomize-guestbook", + }, + }, + }, + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + {Raw: []byte(`{"path.basename": "kustomize-guestbook", "name-suffix": "1"}`)}, + {Raw: []byte(`{"path.basename": "helm-guestbook", "name-suffix": "2"}`)}, + }, + }, + }, + }, + }), + }, + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + {Raw: []byte(`{}`)}, + }, + }, + }, + }, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist(expectedApps1)).Expect(ApplicationsDoNotExist(expectedApps2)). + + // Update the ApplicationSetTerminalGenerator LabelSelector, and verify the Applications are deleted and created + When(). + Update(func(appset *v1alpha1.ApplicationSet) { + + appset.Spec.Generators[0].Merge.Generators[0].Merge = toAPIExtensionsJSON(t, &v1alpha1.NestedMergeGenerator{ + MergeKeys: []string{"path.basename"}, + Generators: []v1alpha1.ApplicationSetTerminalGenerator{ + { + Git: &v1alpha1.GitGenerator{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + Directories: []v1alpha1.GitDirectoryGeneratorItem{ + { + Path: "*guestbook*", + }, + }, + }, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "path.basename": "helm-guestbook", + }, + }, + }, + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{ + {Raw: []byte(`{"path.basename": "kustomize-guestbook", "name-suffix": "1"}`)}, + {Raw: []byte(`{"path.basename": "helm-guestbook", "name-suffix": "2"}`)}, + }, + }, + }, + }, + }) + }).Then().Expect(ApplicationsExist(expectedApps2)).Expect(ApplicationsDoNotExist(expectedApps1)). + + // Set ApplyNestedSelector to false and verify all Applications are created + When(). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.ApplyNestedSelectors = false + }).Then().Expect(ApplicationsExist(expectedApps1)).Expect(ApplicationsExist(expectedApps2)). + + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist(expectedApps1)).Expect(ApplicationsDoNotExist(expectedApps2)) +} + func toAPIExtensionsJSON(t *testing.T, g interface{}) *apiextensionsv1.JSON { resVal, err := json.Marshal(g) diff --git a/test/e2e/multiarch-container/Dockerfile b/test/e2e/multiarch-container/Dockerfile index 41667d28f8176..8fd87a833defb 100644 --- a/test/e2e/multiarch-container/Dockerfile +++ b/test/e2e/multiarch-container/Dockerfile @@ -1,2 +1,2 @@ -FROM docker.io/library/busybox +FROM docker.io/library/busybox@sha256:650fd573e056b679a5110a70aabeb01e26b76e545ec4b9c70a9523f2dfaf18c6 CMD exec sh -c "trap : TERM INT; echo 'Hi' && tail -f /dev/null" diff --git a/test/e2e/notification_test.go b/test/e2e/notification_test.go index ce18f793a9afa..eebe4d8991ae5 100644 --- a/test/e2e/notification_test.go +++ b/test/e2e/notification_test.go @@ -12,10 +12,10 @@ import ( func TestNotificationsListServices(t *testing.T) { ctx := notifFixture.Given(t) ctx.When(). - SetParamInNotificationConfigMap("service.webhook.test", "url: https://test.com"). + SetParamInNotificationConfigMap("service.webhook.test", "url: https://test.example.com"). Then().Services(func(services *notification.ServiceList, err error) { assert.Nil(t, err) - assert.Equal(t, []*notification.Service{¬ification.Service{Name: pointer.String("test")}}, services.Items) + assert.Equal(t, []*notification.Service{{Name: pointer.String("test")}}, services.Items) }) } @@ -25,7 +25,7 @@ func TestNotificationsListTemplates(t *testing.T) { SetParamInNotificationConfigMap("template.app-created", "email:\n subject: Application {{.app.metadata.name}} has been created.\nmessage: Application {{.app.metadata.name}} has been created.\nteams:\n title: Application {{.app.metadata.name}} has been created.\n"). Then().Templates(func(templates *notification.TemplateList, err error) { assert.Nil(t, err) - assert.Equal(t, []*notification.Template{¬ification.Template{Name: pointer.String("app-created")}}, templates.Items) + assert.Equal(t, []*notification.Template{{Name: pointer.String("app-created")}}, templates.Items) }) } @@ -35,6 +35,6 @@ func TestNotificationsListTriggers(t *testing.T) { SetParamInNotificationConfigMap("trigger.on-created", "- description: Application is created.\n oncePer: app.metadata.name\n send:\n - app-created\n when: \"true\"\n"). Then().Triggers(func(triggers *notification.TriggerList, err error) { assert.Nil(t, err) - assert.Equal(t, []*notification.Trigger{¬ification.Trigger{Name: pointer.String("on-created")}}, triggers.Items) + assert.Equal(t, []*notification.Trigger{{Name: pointer.String("on-created")}}, triggers.Items) }) } diff --git a/test/e2e/project_management_test.go b/test/e2e/project_management_test.go index 4cfe088ef5de4..fb8886a21dbd4 100644 --- a/test/e2e/project_management_test.go +++ b/test/e2e/project_management_test.go @@ -21,11 +21,11 @@ import ( ) func assertProjHasEvent(t *testing.T, a *v1alpha1.AppProject, message string, reason string) { - list, err := fixture.KubeClientset.CoreV1().Events(fixture.ArgoCDNamespace).List(context.Background(), metav1.ListOptions{ + list, err := fixture.KubeClientset.CoreV1().Events(fixture.TestNamespace()).List(context.Background(), metav1.ListOptions{ FieldSelector: fields.SelectorFromSet(map[string]string{ "involvedObject.name": a.Name, "involvedObject.uid": string(a.UID), - "involvedObject.namespace": fixture.ArgoCDNamespace, + "involvedObject.namespace": fixture.TestNamespace(), }).String(), }) assert.NoError(t, err) @@ -51,7 +51,7 @@ func TestProjectCreation(t *testing.T) { "--orphaned-resources") assert.Nil(t, err) - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) assert.Equal(t, 2, len(proj.Spec.Destinations)) @@ -87,7 +87,7 @@ func TestProjectCreation(t *testing.T) { _, err = fixture.RunCliWithStdin(stdinString, "proj", "create", "-f", "-", "--upsert") assert.NoError(t, err) - proj, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + proj, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.NoError(t, err) assert.Equal(t, newDescription, proj.Spec.Description) } @@ -96,14 +96,14 @@ func TestProjectDeletion(t *testing.T) { fixture.EnsureCleanState(t) projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Create( + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( context.Background(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) assert.NoError(t, err) _, err = fixture.RunCli("proj", "delete", projectName) assert.NoError(t, err) - _, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + _, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.True(t, errors.IsNotFound(err)) assertProjHasEvent(t, proj, "delete", argo.EventReasonResourceDeleted) } @@ -112,7 +112,7 @@ func TestSetProject(t *testing.T) { fixture.EnsureCleanState(t) projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) - _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Create( + _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( context.Background(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) assert.NoError(t, err) @@ -123,7 +123,7 @@ func TestSetProject(t *testing.T) { "--orphaned-resources-warn=false") assert.NoError(t, err) - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) assert.Equal(t, 2, len(proj.Spec.Destinations)) @@ -144,7 +144,7 @@ func TestAddProjectDestination(t *testing.T) { fixture.EnsureCleanState(t) projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) - _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Create( + _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( context.Background(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) if err != nil { t.Fatalf("Unable to create project %v", err) @@ -180,7 +180,7 @@ func TestAddProjectDestination(t *testing.T) { assert.Error(t, err) assert.True(t, strings.Contains(err.Error(), "namespace has an invalid format, '!*'")) - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) assert.Equal(t, 1, len(proj.Spec.Destinations)) @@ -194,7 +194,7 @@ func TestAddProjectDestinationWithName(t *testing.T) { fixture.EnsureCleanState(t) projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) - _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Create( + _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( context.Background(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) if err != nil { t.Fatalf("Unable to create project %v", err) @@ -210,7 +210,7 @@ func TestAddProjectDestinationWithName(t *testing.T) { t.Fatalf("Unable to add project destination %v", err) } - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) assert.Equal(t, 1, len(proj.Spec.Destinations)) @@ -225,7 +225,7 @@ func TestRemoveProjectDestination(t *testing.T) { fixture.EnsureCleanState(t) projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) - _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Create(context.Background(), &v1alpha1.AppProject{ + _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create(context.Background(), &v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{Name: projectName}, Spec: v1alpha1.AppProjectSpec{ Destinations: []v1alpha1.ApplicationDestination{{ @@ -255,7 +255,7 @@ func TestRemoveProjectDestination(t *testing.T) { assert.Error(t, err) assert.Contains(t, err.Error(), "does not exist") - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) if err != nil { t.Fatalf("Unable to get project %v", err) } @@ -268,7 +268,7 @@ func TestAddProjectSource(t *testing.T) { fixture.EnsureCleanState(t) projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) - _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Create( + _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( context.Background(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) if err != nil { t.Fatalf("Unable to create project %v", err) @@ -283,7 +283,7 @@ func TestAddProjectSource(t *testing.T) { _, err = fixture.RunCli("proj", "add-source", projectName, "https://github.com/argoproj/argo-cd.git") assert.Nil(t, err) - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) assert.Equal(t, 1, len(proj.Spec.SourceRepos)) @@ -295,7 +295,7 @@ func TestRemoveProjectSource(t *testing.T) { fixture.EnsureCleanState(t) projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) - _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Create(context.Background(), &v1alpha1.AppProject{ + _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create(context.Background(), &v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{Name: projectName}, Spec: v1alpha1.AppProjectSpec{ SourceRepos: []string{"https://github.com/argoproj/argo-cd.git"}, @@ -311,7 +311,7 @@ func TestRemoveProjectSource(t *testing.T) { _, err = fixture.RunCli("proj", "remove-source", projectName, "https://github.com/argoproj/argo-cd.git") assert.NoError(t, err) - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) assert.Equal(t, 0, len(proj.Spec.SourceRepos)) @@ -335,24 +335,24 @@ func TestUseJWTToken(t *testing.T) { }, Destination: v1alpha1.ApplicationDestination{ Server: v1alpha1.KubernetesInternalAPIServerAddr, - Namespace: fixture.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), }, Project: projectName, }, } - _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Create(context.Background(), &v1alpha1.AppProject{ + _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create(context.Background(), &v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{Name: projectName}, Spec: v1alpha1.AppProjectSpec{ Destinations: []v1alpha1.ApplicationDestination{{ Server: v1alpha1.KubernetesInternalAPIServerAddr, - Namespace: fixture.ArgoCDNamespace, + Namespace: fixture.TestNamespace(), }}, SourceRepos: []string{"*"}, }, }, metav1.CreateOptions{}) assert.Nil(t, err) - _, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.ArgoCDNamespace).Create(context.Background(), testApp, metav1.CreateOptions{}) + _, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.TestNamespace()).Create(context.Background(), testApp, metav1.CreateOptions{}) assert.NoError(t, err) _, err = fixture.RunCli("proj", "role", "create", projectName, roleName) @@ -370,7 +370,7 @@ func TestUseJWTToken(t *testing.T) { assert.NoError(t, err) } - newProj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + newProj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.NoError(t, err) assert.Len(t, newProj.Status.JWTTokensByRole[roleName].Items, 1) assert.ElementsMatch(t, newProj.Status.JWTTokensByRole[roleName].Items, newProj.Spec.Roles[0].JWTTokens) @@ -381,7 +381,7 @@ func TestUseJWTToken(t *testing.T) { _, err = fixture.RunCli("proj", "role", "delete-token", projectName, roleName, strconv.FormatInt(newProj.Status.JWTTokensByRole[roleName].Items[0].IssuedAt, 10)) assert.NoError(t, err) - newProj, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + newProj, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.NoError(t, err) assert.Nil(t, newProj.Status.JWTTokensByRole[roleName].Items) assert.Nil(t, newProj.Spec.Roles[0].JWTTokens) @@ -392,7 +392,7 @@ func TestAddOrphanedIgnore(t *testing.T) { fixture.EnsureCleanState(t) projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) - _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Create( + _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create( context.Background(), &v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: projectName}}, metav1.CreateOptions{}) if err != nil { t.Fatalf("Unable to create project %v", err) @@ -418,7 +418,7 @@ func TestAddOrphanedIgnore(t *testing.T) { assert.Error(t, err) assert.True(t, strings.Contains(err.Error(), "already defined")) - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.NoError(t, err) assert.Equal(t, projectName, proj.Name) assert.Equal(t, 1, len(proj.Spec.OrphanedResources.Ignore)) @@ -433,11 +433,11 @@ func TestRemoveOrphanedIgnore(t *testing.T) { fixture.EnsureCleanState(t) projectName := "proj-" + strconv.FormatInt(time.Now().Unix(), 10) - _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Create(context.Background(), &v1alpha1.AppProject{ + _, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Create(context.Background(), &v1alpha1.AppProject{ ObjectMeta: metav1.ObjectMeta{Name: projectName}, Spec: v1alpha1.AppProjectSpec{ OrphanedResources: &v1alpha1.OrphanedResourcesMonitorSettings{ - Warn: pointer.BoolPtr(true), + Warn: pointer.Bool(true), Ignore: []v1alpha1.OrphanedResourceKey{{Group: "group", Kind: "kind", Name: "name"}}, }, }, @@ -467,7 +467,7 @@ func TestRemoveOrphanedIgnore(t *testing.T) { assert.Error(t, err) assert.Contains(t, err.Error(), "does not exist") - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) if err != nil { t.Fatalf("Unable to get project %v", err) } @@ -477,7 +477,7 @@ func TestRemoveOrphanedIgnore(t *testing.T) { } func createAndConfigGlobalProject() error { - //Create global project + // Create global project projectGlobalName := "proj-g-" + fixture.Name() _, err := fixture.RunCli("proj", "create", projectGlobalName, "--description", "Test description", @@ -489,7 +489,7 @@ func createAndConfigGlobalProject() error { return err } - projGlobal, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectGlobalName, metav1.GetOptions{}) + projGlobal, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectGlobalName, metav1.GetOptions{}) if err != nil { return err } @@ -514,12 +514,12 @@ func createAndConfigGlobalProject() error { win := &v1alpha1.SyncWindow{Kind: "deny", Schedule: "* * * * *", Duration: "1h", Applications: []string{"*"}} projGlobal.Spec.SyncWindows = append(projGlobal.Spec.SyncWindows, win) - _, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Update(context.Background(), projGlobal, metav1.UpdateOptions{}) + _, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Update(context.Background(), projGlobal, metav1.UpdateOptions{}) if err != nil { return err } - //Configure global project settings + // Configure global project settings globalProjectsSettings := `data: accounts.config-service: apiKey globalProjects: | @@ -533,7 +533,7 @@ func createAndConfigGlobalProject() error { projectName: %s` _, err = fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", - "-n", fixture.ArgoCDNamespace, + "-n", fixture.TestNamespace(), "-p", fmt.Sprintf(globalProjectsSettings, projGlobal.Name)) if err != nil { return err @@ -547,7 +547,7 @@ func TestGetVirtualProjectNoMatch(t *testing.T) { err := createAndConfigGlobalProject() assert.NoError(t, err) - //Create project which does not match global project settings + // Create project which does not match global project settings projectName := "proj-" + fixture.Name() _, err = fixture.RunCli("proj", "create", projectName, "--description", "Test description", @@ -556,10 +556,10 @@ func TestGetVirtualProjectNoMatch(t *testing.T) { "--orphaned-resources") assert.NoError(t, err) - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.NoError(t, err) - //Create an app belongs to proj project + // Create an app belongs to proj project _, err = fixture.RunCli("app", "create", fixture.Name(), "--repo", fixture.RepoURL(fixture.RepoURLTypeFile), "--path", guestbookPath, "--project", proj.Name, "--dest-server", v1alpha1.KubernetesInternalAPIServerAddr, "--dest-namespace", fixture.DeploymentNamespace()) assert.NoError(t, err) @@ -568,11 +568,11 @@ func TestGetVirtualProjectNoMatch(t *testing.T) { // Else the sync would fail to retrieve the app resources. time.Sleep(time.Second * 2) - //App trying to sync a resource which is not blacked listed anywhere + // App trying to sync a resource which is not blacked listed anywhere _, err = fixture.RunCli("app", "sync", fixture.Name(), "--resource", "apps:Deployment:guestbook-ui", "--timeout", fmt.Sprintf("%v", 10)) assert.NoError(t, err) - //app trying to sync a resource which is black listed by global project + // app trying to sync a resource which is black listed by global project _, err = fixture.RunCli("app", "sync", fixture.Name(), "--resource", ":Service:guestbook-ui", "--timeout", fmt.Sprintf("%v", 10)) assert.NoError(t, err) @@ -583,7 +583,7 @@ func TestGetVirtualProjectMatch(t *testing.T) { err := createAndConfigGlobalProject() assert.NoError(t, err) - //Create project which matches global project settings + // Create project which matches global project settings projectName := "proj-" + fixture.Name() _, err = fixture.RunCli("proj", "create", projectName, "--description", "Test description", @@ -592,15 +592,15 @@ func TestGetVirtualProjectMatch(t *testing.T) { "--orphaned-resources") assert.NoError(t, err) - proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.Background(), projectName, metav1.GetOptions{}) + proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Get(context.Background(), projectName, metav1.GetOptions{}) assert.NoError(t, err) - //Add a label to this project so that this project match global project selector + // Add a label to this project so that this project match global project selector proj.Labels = map[string]string{"opt": "me"} - _, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Update(context.Background(), proj, metav1.UpdateOptions{}) + _, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Update(context.Background(), proj, metav1.UpdateOptions{}) assert.NoError(t, err) - //Create an app belongs to proj project + // Create an app belongs to proj project _, err = fixture.RunCli("app", "create", fixture.Name(), "--repo", fixture.RepoURL(fixture.RepoURLTypeFile), "--path", guestbookPath, "--project", proj.Name, "--dest-server", v1alpha1.KubernetesInternalAPIServerAddr, "--dest-namespace", fixture.DeploymentNamespace()) assert.NoError(t, err) @@ -609,12 +609,12 @@ func TestGetVirtualProjectMatch(t *testing.T) { // Else the sync would fail to retrieve the app resources. time.Sleep(time.Second * 2) - //App trying to sync a resource which is not blacked listed anywhere + // App trying to sync a resource which is not blacked listed anywhere _, err = fixture.RunCli("app", "sync", fixture.Name(), "--resource", "apps:Deployment:guestbook-ui", "--timeout", fmt.Sprintf("%v", 10)) assert.Error(t, err) assert.Contains(t, err.Error(), "blocked by sync window") - //app trying to sync a resource which is black listed by global project + // app trying to sync a resource which is black listed by global project _, err = fixture.RunCli("app", "sync", fixture.Name(), "--resource", ":Service:guestbook-ui", "--timeout", fmt.Sprintf("%v", 10)) assert.Contains(t, err.Error(), "blocked by sync window") diff --git a/test/e2e/repo_creds_test.go b/test/e2e/repo_creds_test.go index b4772698675dc..59df113605a7b 100644 --- a/test/e2e/repo_creds_test.go +++ b/test/e2e/repo_creds_test.go @@ -57,7 +57,7 @@ func TestCanAddAppFromInsecurePrivateRepoWithCredCfg(t *testing.T) { And(func() { secretName := fixture.CreateSecret(fixture.GitUsername, fixture.GitPassword) FailOnErr(fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", - "-n", fixture.ArgoCDNamespace, + "-n", fixture.TestNamespace(), "-p", fmt.Sprintf( `{"data": {"repository.credentials": "- passwordSecret:\n key: password\n name: %s\n url: %s\n insecure: true\n usernameSecret:\n key: username\n name: %s\n"}}`, secretName, @@ -83,7 +83,7 @@ func TestCanAddAppFromPrivateRepoWithCredCfg(t *testing.T) { And(func() { secretName := fixture.CreateSecret(fixture.GitUsername, fixture.GitPassword) FailOnErr(fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", - "-n", fixture.ArgoCDNamespace, + "-n", fixture.TestNamespace(), "-p", fmt.Sprintf( `{"data": {"repository.credentials": "- passwordSecret:\n key: password\n name: %s\n url: %s\n usernameSecret:\n key: username\n name: %s\n"}}`, secretName, @@ -108,7 +108,7 @@ func TestCanAddAppFromClientCertRepoWithCredCfg(t *testing.T) { And(func() { secretName := fixture.CreateSecret(fixture.GitUsername, fixture.GitPassword) FailOnErr(fixture.Run("", "kubectl", "patch", "cm", "argocd-cm", - "-n", fixture.ArgoCDNamespace, + "-n", fixture.TestNamespace(), "-p", fmt.Sprintf( `{"data": {"repository.credentials": "- passwordSecret:\n key: password\n name: %s\n url: %s\n usernameSecret:\n key: username\n name: %s\n"}}`, secretName, diff --git a/test/e2e/repo_management_test.go b/test/e2e/repo_management_test.go index 30b304c8b7b2e..70b14d5682299 100644 --- a/test/e2e/repo_management_test.go +++ b/test/e2e/repo_management_test.go @@ -7,9 +7,11 @@ import ( "github.com/stretchr/testify/assert" repositorypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/test/e2e/fixture" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/repos" + . "github.com/argoproj/argo-cd/v2/util/errors" argoio "github.com/argoproj/argo-cd/v2/util/io" "github.com/argoproj/argo-cd/v2/util/settings" ) @@ -52,6 +54,38 @@ func TestAddRemovePublicRepo(t *testing.T) { }) } +func TestGetRepoWithInheritedCreds(t *testing.T) { + app.Given(t).And(func() { + // create repo credentials + FailOnErr(fixture.RunCli("repocreds", "add", fixture.RepoURL(fixture.RepoURLTypeHTTPSOrg), "--github-app-id", fixture.GithubAppID, "--github-app-installation-id", fixture.GithubAppInstallationID, "--github-app-private-key-path", repos.CertKeyPath)) + + repoUrl := fixture.RepoURL(fixture.RepoURLTypeHTTPS) + + // Hack: First we need to create repo with valid credentials + FailOnErr(fixture.RunCli("repo", "add", repoUrl, "--username", fixture.GitUsername, "--password", fixture.GitPassword, "--insecure-skip-server-verification")) + + // Then, we remove username/password so that the repo inherits the credentials from our repocreds + conn, repoClient, err := fixture.ArgoCDClientset.NewRepoClient() + assert.NoError(t, err) + defer argoio.Close(conn) + + _, err = repoClient.UpdateRepository(context.Background(), &repositorypkg.RepoUpdateRequest{ + Repo: &v1alpha1.Repository{ + Repo: repoUrl, + }, + }) + assert.NoError(t, err) + + // CLI output should indicate that repo has inherited credentials + out, err := fixture.RunCli("repo", "get", repoUrl) + assert.NoError(t, err) + assert.Contains(t, out, "inherited") + + _, err = fixture.RunCli("repo", "rm", repoUrl) + assert.NoError(t, err) + }) +} + func TestUpsertExistingRepo(t *testing.T) { app.Given(t).And(func() { fixture.SetRepos(settings.RepositoryCredentials{URL: fixture.RepoURL(fixture.RepoURLTypeFile)}) diff --git a/test/e2e/selective_sync_test.go b/test/e2e/selective_sync_test.go index 1738264c509fc..491914be55184 100644 --- a/test/e2e/selective_sync_test.go +++ b/test/e2e/selective_sync_test.go @@ -80,7 +80,7 @@ func TestSelectiveSyncWithoutNamespace(t *testing.T) { Expect(ResourceSyncStatusWithNamespaceIs("Deployment", "guestbook-ui", fixture.DeploymentNamespace(), SyncStatusCodeSynced)) } -//In selectedResource to sync, namespace is provided +// In selectedResource to sync, namespace is provided func TestSelectiveSyncWithNamespace(t *testing.T) { selectedResourceNamespace := getNewNamespace(t) defer func() { diff --git a/test/e2e/sync_options_test.go b/test/e2e/sync_options_test.go index 657c8b117705b..3eb7140787097 100644 --- a/test/e2e/sync_options_test.go +++ b/test/e2e/sync_options_test.go @@ -80,6 +80,29 @@ func TestSyncWithStatusIgnored(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)) } +func TestSyncWithApplyOutOfSyncOnly(t *testing.T) { + var ns string + Given(t). + Path(guestbookPath). + ApplyOutOfSyncOnly(). + When(). + CreateFromFile(func(app *Application) { + ns = app.Spec.Destination.Namespace + }). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + When(). + Sync(). + Then(). + When(). + PatchFile("guestbook-ui-deployment.yaml", `[{ "op": "replace", "path": "/spec/replicas", "value": 1 }]`). + Sync(). + Then(). + // Only one resource should be in sync result + Expect(ResourceResultNumbering(1)). + Expect(ResourceResultIs(ResourceResult{Group: "apps", Version: "v1", Kind: "Deployment", Namespace: ns, Name: "guestbook-ui", Message: "deployment.apps/guestbook-ui configured", SyncPhase: SyncPhaseSync, HookPhase: OperationRunning, Status: ResultCodeSynced})) +} + func TestSyncWithSkipHook(t *testing.T) { fixture.SkipOnEnv(t, "OPENSHIFT") Given(t). diff --git a/test/e2e/sync_waves_test.go b/test/e2e/sync_waves_test.go index ac5db15eee57d..8d0ee14e487d1 100644 --- a/test/e2e/sync_waves_test.go +++ b/test/e2e/sync_waves_test.go @@ -9,6 +9,8 @@ import ( "github.com/argoproj/gitops-engine/pkg/health" . "github.com/argoproj/gitops-engine/pkg/sync/common" + + v1 "k8s.io/api/core/v1" ) func TestFixingDegradedApp(t *testing.T) { @@ -100,3 +102,46 @@ func TestDegradedDeploymentIsSucceededAndSynced(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(ResourceResultNumbering(1)) } + +// resources should be pruned in reverse of creation order(syncwaves order) +func TestSyncPruneOrderWithSyncWaves(t *testing.T) { + ctx := Given(t).Timeout(60) + + // remove finalizer to ensure proper cleanup if test fails at early stage + defer func() { + _, _ = RunCli("app", "patch-resource", ctx.AppQualifiedName(), + "--kind", "Pod", + "--resource-name", "pod-with-finalizers", + "--patch", `[{"op": "remove", "path": "/metadata/finalizers"}]`, + "--patch-type", "application/json-patch+json", "--all", + ) + }() + + ctx.Path("syncwaves-prune-order"). + When(). + CreateApp(). + // creation order: sa & role -> rolebinding -> pod + Sync(). + Wait(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)). + When(). + // delete files to remove resources + DeleteFile("pod.yaml"). + DeleteFile("rbac.yaml"). + Refresh(RefreshTypeHard). + IgnoreErrors(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + When(). + // prune order: pod -> rolebinding -> sa & role + Sync("--prune"). + Wait(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)). + Expect(NotPod(func(p v1.Pod) bool { return p.Name == "pod-with-finalizers" })). + Expect(ResourceResultNumbering(4)) +} diff --git a/test/e2e/testdata/cluster-role-hook/cluster-role.yaml b/test/e2e/testdata/cluster-role-hook/cluster-role.yaml new file mode 100644 index 0000000000000..456e1b12fbd33 --- /dev/null +++ b/test/e2e/testdata/cluster-role-hook/cluster-role.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + namespace: cert-manager + name: my-cluster-role-binding + annotations: + argocd.argoproj.io/hook: PreSync +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: default + namespace: default diff --git a/test/e2e/testdata/cluster-role/pod.yaml b/test/e2e/testdata/cluster-role-hook/pod.yaml similarity index 100% rename from test/e2e/testdata/cluster-role/pod.yaml rename to test/e2e/testdata/cluster-role-hook/pod.yaml diff --git a/test/e2e/testdata/cluster-role/cluster-role.yaml b/test/e2e/testdata/cluster-role/cluster-role.yaml index cb6cd7c1b3e42..cc5365bb3f4de 100644 --- a/test/e2e/testdata/cluster-role/cluster-role.yaml +++ b/test/e2e/testdata/cluster-role/cluster-role.yaml @@ -1,10 +1,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - namespace: cert-manager name: my-cluster-role-binding - annotations: - argocd.argoproj.io/hook: PreSync roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -12,4 +9,4 @@ roleRef: subjects: - kind: ServiceAccount name: default - namespace: default \ No newline at end of file + namespace: default diff --git a/test/e2e/testdata/cmp-fileName/plugin.yaml b/test/e2e/testdata/cmp-fileName/plugin.yaml index 766278c79e773..b3f8068de2100 100644 --- a/test/e2e/testdata/cmp-fileName/plugin.yaml +++ b/test/e2e/testdata/cmp-fileName/plugin.yaml @@ -5,6 +5,6 @@ metadata: spec: version: v1.0 generate: - command: [sh, -c, 'echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"'] + command: [sh, -c, 'echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$ARGOCD_ENV_FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"'] discover: fileName: "subdir/s*.yaml" diff --git a/test/e2e/testdata/cmp-gitcreds/plugin.yaml b/test/e2e/testdata/cmp-gitcreds/plugin.yaml new file mode 100644 index 0000000000000..024804f495cc9 --- /dev/null +++ b/test/e2e/testdata/cmp-gitcreds/plugin.yaml @@ -0,0 +1,10 @@ +apiVersion: argoproj.io/v1alpha1 +kind: ConfigManagementPlugin +metadata: + name: cmp-gitcreds +spec: + version: v1.0 + generate: + command: [sh, -c, 'echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"GitAskpass\": \"$GIT_ASKPASS\"}}}"'] + discover: + fileName: "subdir/s*.yaml" diff --git a/test/e2e/testdata/cmp-gitcreds/subdir/special.yaml b/test/e2e/testdata/cmp-gitcreds/subdir/special.yaml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/e2e/testdata/cmp-gitcredstemplate/plugin.yaml b/test/e2e/testdata/cmp-gitcredstemplate/plugin.yaml new file mode 100644 index 0000000000000..e57ee747bd078 --- /dev/null +++ b/test/e2e/testdata/cmp-gitcredstemplate/plugin.yaml @@ -0,0 +1,10 @@ +apiVersion: argoproj.io/v1alpha1 +kind: ConfigManagementPlugin +metadata: + name: cmp-gitcredstemplate +spec: + version: v1.0 + generate: + command: [sh, -c, 'echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"GitAskpass\": \"$GIT_ASKPASS\", \"GitUsername\": \"$GIT_USERNAME\", \"GitPassword\": \"$GIT_PASSWORD\"}}}"'] + discover: + fileName: "subdir/s*.yaml" diff --git a/test/e2e/testdata/cmp-gitcredstemplate/subdir/special.yaml b/test/e2e/testdata/cmp-gitcredstemplate/subdir/special.yaml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/e2e/testdata/cmp-kustomize/plugin.yaml b/test/e2e/testdata/cmp-kustomize/plugin.yaml new file mode 100644 index 0000000000000..3cdcc6d643758 --- /dev/null +++ b/test/e2e/testdata/cmp-kustomize/plugin.yaml @@ -0,0 +1,10 @@ +apiVersion: argoproj.io/v1alpha1 +kind: ConfigManagementPlugin +metadata: + name: cmp-kustomize +spec: + version: v1.0 + generate: + command: [kustomize, build, .] + discover: + fileName: "kustomization.yaml" diff --git a/test/e2e/testdata/cmp-preserve-file-mode/plugin.yaml b/test/e2e/testdata/cmp-preserve-file-mode/plugin.yaml new file mode 100644 index 0000000000000..c522649234e88 --- /dev/null +++ b/test/e2e/testdata/cmp-preserve-file-mode/plugin.yaml @@ -0,0 +1,11 @@ +apiVersion: argoproj.io/v1alpha1 +kind: ConfigManagementPlugin +metadata: + name: cmp-preserve-file-mode +spec: + version: v1.0 + generate: + command: [sh, -c , "./script.sh"] + discovery: + - glob: "**/*" + preserveFileMode: true diff --git a/test/e2e/testdata/cmp-preserve-file-mode/script.sh b/test/e2e/testdata/cmp-preserve-file-mode/script.sh new file mode 100755 index 0000000000000..1fbb8fde0c14d --- /dev/null +++ b/test/e2e/testdata/cmp-preserve-file-mode/script.sh @@ -0,0 +1,8 @@ +cat << EOF +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-cm +data: + foo: bar +EOF \ No newline at end of file diff --git a/test/e2e/testdata/crashing-guestbook/kustomization.yaml b/test/e2e/testdata/crashing-guestbook/kustomization.yaml index b9ed6ab1a34f0..1c10225ce38b1 100644 --- a/test/e2e/testdata/crashing-guestbook/kustomization.yaml +++ b/test/e2e/testdata/crashing-guestbook/kustomization.yaml @@ -1,8 +1,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -bases: +resources: - ../guestbook patches: - - guestbook-deployment.yaml + - path: guestbook-deployment.yaml diff --git a/test/e2e/testdata/crd-version-differences/crd-does-not-exist-instance.yaml b/test/e2e/testdata/crd-version-differences/crd-does-not-exist-instance.yaml new file mode 100644 index 0000000000000..ba753eedda6a4 --- /dev/null +++ b/test/e2e/testdata/crd-version-differences/crd-does-not-exist-instance.yaml @@ -0,0 +1,7 @@ +apiVersion: argoproj.io/v1alpha1 +kind: DoesNotExist +metadata: + name: dummy-crd-instance-doesnotexist +spec: + cpu: 2000m + memory: 32Mi diff --git a/test/e2e/testdata/crd-version-differences/crd-v1alpha1.yaml b/test/e2e/testdata/crd-version-differences/crd-v1alpha1.yaml new file mode 100644 index 0000000000000..3cf492a837979 --- /dev/null +++ b/test/e2e/testdata/crd-version-differences/crd-v1alpha1.yaml @@ -0,0 +1,35 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: fakes.argoproj.io +spec: + conversion: + strategy: None + group: argoproj.io + names: + kind: Fake + listKind: FakeList + plural: fakes + singular: fake + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + type: object + properties: + cpu: + type: string + memory: + type: string diff --git a/test/e2e/testdata/crd-version-differences/crd-v1alpha2-instance.yaml b/test/e2e/testdata/crd-version-differences/crd-v1alpha2-instance.yaml new file mode 100644 index 0000000000000..987ee4bd0e3e2 --- /dev/null +++ b/test/e2e/testdata/crd-version-differences/crd-v1alpha2-instance.yaml @@ -0,0 +1,7 @@ +apiVersion: argoproj.io/v1alpha2 +kind: Fake +metadata: + name: fake-crd-instance +spec: + cpu: 2000m + memory: 32Mi diff --git a/test/e2e/testdata/crd-version-differences/crd-wronggroup-instance.yaml b/test/e2e/testdata/crd-version-differences/crd-wronggroup-instance.yaml new file mode 100644 index 0000000000000..95e022e046897 --- /dev/null +++ b/test/e2e/testdata/crd-version-differences/crd-wronggroup-instance.yaml @@ -0,0 +1,7 @@ +apiVersion: wrong.group/v1alpha1 +kind: Fake +metadata: + name: fake-crd-instance-wronggroup +spec: + cpu: 2000m + memory: 32Mi diff --git a/test/e2e/testdata/deprecated-extensions/deployment.yaml b/test/e2e/testdata/deprecated-extensions/deployment.yaml index 5593c3157fc57..bc4ef128d8bf4 100644 --- a/test/e2e/testdata/deprecated-extensions/deployment.yaml +++ b/test/e2e/testdata/deprecated-extensions/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: extensions-deployment diff --git a/test/e2e/testdata/git-submodule/submodule-pod.yaml b/test/e2e/testdata/git-submodule/submodule-pod.yaml index fa3b92c2f5875..134107da31cba 100644 --- a/test/e2e/testdata/git-submodule/submodule-pod.yaml +++ b/test/e2e/testdata/git-submodule/submodule-pod.yaml @@ -7,6 +7,4 @@ spec: - name: main image: quay.io/argoprojlabs/argocd-e2e-container:0.1 imagePullPolicy: IfNotPresent - command: - - "true" - restartPolicy: Never + terminationGracePeriodSeconds: 0 diff --git a/test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-deployment.yaml b/test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-deployment.yaml new file mode 100644 index 0000000000000..bf3375672f70c --- /dev/null +++ b/test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-deployment.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: guestbook-ui + labels: + test: "true" +spec: + replicas: 0 + revisionHistoryLimit: 3 + selector: + matchLabels: + app: guestbook-ui + template: + metadata: + labels: + app: guestbook-ui + spec: + containers: + - image: quay.io/argoprojlabs/argocd-e2e-container:0.2 + imagePullPolicy: IfNotPresent + name: guestbook-ui + ports: + - containerPort: 80 diff --git a/test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-namespace.yaml b/test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-namespace.yaml new file mode 100644 index 0000000000000..ceda849716ccb --- /dev/null +++ b/test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-namespace.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: guestbook-ui-with-namespace-manifest + labels: + test: "true" + annotations: + foo: bar + something: else diff --git a/test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-svc.yaml b/test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-svc.yaml new file mode 100644 index 0000000000000..e8a4a27fbae40 --- /dev/null +++ b/test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-svc.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: guestbook-ui +spec: + ports: + - port: 80 + targetPort: 80 + selector: + app: guestbook-ui diff --git a/test/e2e/testdata/helm-with-multiple-dependencies-permission-denied/Chart.yaml b/test/e2e/testdata/helm-with-multiple-dependencies-permission-denied/Chart.yaml new file mode 100644 index 0000000000000..72f36221ea482 --- /dev/null +++ b/test/e2e/testdata/helm-with-multiple-dependencies-permission-denied/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: helm-with-multiple-dependencies-permission-denied +version: v1.0.0 +dependencies: + - name: helm + repository: "https://localhost:9443/argo-e2e/testdata.git/helm-repo/local" + version: v1.0.0 + - name: helm + repository: "https://localhost:9443/argo-e2e/testdata.git/helm-repo/local2" + version: v1.0.0 + alias: helm2 \ No newline at end of file diff --git a/test/e2e/testdata/helm-with-multiple-dependencies/Chart.yaml b/test/e2e/testdata/helm-with-multiple-dependencies/Chart.yaml index fc1982761746c..f7f144d20e123 100644 --- a/test/e2e/testdata/helm-with-multiple-dependencies/Chart.yaml +++ b/test/e2e/testdata/helm-with-multiple-dependencies/Chart.yaml @@ -1,7 +1,11 @@ apiVersion: v2 -name: helm-with-dependencies +name: helm-with-multiple-dependencies version: v1.0.0 dependencies: - name: helm repository: "https://localhost:9444/argo-e2e/testdata.git/helm-repo/local" version: v1.0.0 + - name: helm + repository: "https://localhost:9444/argo-e2e/testdata.git/helm-repo/local2" + version: v1.0.0 + alias: helm2 \ No newline at end of file diff --git a/test/e2e/testdata/hook-custom-health/kustomization.yaml b/test/e2e/testdata/hook-custom-health/kustomization.yaml index 3c88ce9216652..7af040cfb6edb 100644 --- a/test/e2e/testdata/hook-custom-health/kustomization.yaml +++ b/test/e2e/testdata/hook-custom-health/kustomization.yaml @@ -1,8 +1,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -bases: - - ../guestbook - resources: + - ../guestbook - config-map.yaml \ No newline at end of file diff --git a/test/e2e/testdata/https-kustomize-base/local/kustomization.yaml b/test/e2e/testdata/https-kustomize-base/local/kustomization.yaml index da7f7e8e01e99..d7c6dafc34df3 100644 --- a/test/e2e/testdata/https-kustomize-base/local/kustomization.yaml +++ b/test/e2e/testdata/https-kustomize-base/local/kustomization.yaml @@ -1,7 +1,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -bases: +resources: - https://localhost:9443/argo-e2e/testdata.git//guestbook namePrefix: child- \ No newline at end of file diff --git a/test/e2e/testdata/https-kustomize-base/remote/kustomization.yaml b/test/e2e/testdata/https-kustomize-base/remote/kustomization.yaml index 96e9885a84cba..f3d27c1f258c9 100644 --- a/test/e2e/testdata/https-kustomize-base/remote/kustomization.yaml +++ b/test/e2e/testdata/https-kustomize-base/remote/kustomization.yaml @@ -1,7 +1,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -bases: +resources: - https://argocd-e2e-server:9443/argo-e2e/testdata.git//guestbook namePrefix: child- diff --git a/test/e2e/testdata/multi-namespace/deployment-with-namespace.yaml b/test/e2e/testdata/multi-namespace/deployment-with-namespace.yaml index 474e117ac487c..ebd3e77f679e6 100644 --- a/test/e2e/testdata/multi-namespace/deployment-with-namespace.yaml +++ b/test/e2e/testdata/multi-namespace/deployment-with-namespace.yaml @@ -1,4 +1,4 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: helm-guestbook diff --git a/test/e2e/testdata/multi-namespace/deployment.yaml b/test/e2e/testdata/multi-namespace/deployment.yaml index 08d30228fad03..bd6f69aadd7e8 100644 --- a/test/e2e/testdata/multi-namespace/deployment.yaml +++ b/test/e2e/testdata/multi-namespace/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: helm-guestbook diff --git a/test/e2e/testdata/networking/guestbook-ui-svc-ingress.yaml b/test/e2e/testdata/networking/guestbook-ui-svc-ingress.yaml index d499de1e9c308..a4427135b193d 100644 --- a/test/e2e/testdata/networking/guestbook-ui-svc-ingress.yaml +++ b/test/e2e/testdata/networking/guestbook-ui-svc-ingress.yaml @@ -9,7 +9,7 @@ metadata: ingress.kubernetes.io/app-root: "/" spec: rules: - - host: myhost.com + - host: example.com http: paths: - path: / @@ -27,7 +27,7 @@ metadata: ingress.kubernetes.io/app-root: "/" spec: rules: - - host: myhost.com + - host: example.com http: paths: - path: / diff --git a/test/e2e/testdata/post-delete-hook/hook.yaml b/test/e2e/testdata/post-delete-hook/hook.yaml new file mode 100644 index 0000000000000..5631db681f1d0 --- /dev/null +++ b/test/e2e/testdata/post-delete-hook/hook.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + argocd.argoproj.io/hook: PostDelete + name: hook +spec: + containers: + - command: + - "true" + image: quay.io/argoprojlabs/argocd-e2e-container:0.1 + imagePullPolicy: IfNotPresent + name: main + restartPolicy: Never \ No newline at end of file diff --git a/test/e2e/testdata/resource-actions/cron-job.yaml b/test/e2e/testdata/resource-actions/cron-job.yaml new file mode 100644 index 0000000000000..3ab1fb9b1cd8a --- /dev/null +++ b/test/e2e/testdata/resource-actions/cron-job.yaml @@ -0,0 +1,19 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: hello +spec: + schedule: "* * * * *" + jobTemplate: + spec: + template: + spec: + containers: + - name: hello + image: busybox:1.28 + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - date; echo Hello from the Kubernetes cluster + restartPolicy: OnFailure \ No newline at end of file diff --git a/test/e2e/testdata/ssh-kustomize-base/local/kustomization.yaml b/test/e2e/testdata/ssh-kustomize-base/local/kustomization.yaml index 56275b107b876..6c63e526ef842 100644 --- a/test/e2e/testdata/ssh-kustomize-base/local/kustomization.yaml +++ b/test/e2e/testdata/ssh-kustomize-base/local/kustomization.yaml @@ -1,5 +1,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -bases: +resources: - ssh://root@localhost:2222/tmp/argo-e2e/testdata.git//config-map diff --git a/test/e2e/testdata/ssh-kustomize-base/remote/kustomization.yaml b/test/e2e/testdata/ssh-kustomize-base/remote/kustomization.yaml index 84537aac69f81..c144aa21a1cee 100644 --- a/test/e2e/testdata/ssh-kustomize-base/remote/kustomization.yaml +++ b/test/e2e/testdata/ssh-kustomize-base/remote/kustomization.yaml @@ -1,5 +1,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -bases: +resources: - ssh://root@argocd-e2e-server:2222/tmp/argo-e2e/testdata.git//config-map diff --git a/test/e2e/testdata/syncwaves-prune-order/README.md b/test/e2e/testdata/syncwaves-prune-order/README.md new file mode 100644 index 0000000000000..92a62fdfe109d --- /dev/null +++ b/test/e2e/testdata/syncwaves-prune-order/README.md @@ -0,0 +1,15 @@ +## Test Scenario + +This test example is for testing the reverse pruning of resources with syncwaves during sync operation. + +Resource creation happens in below order +- wave 0: sa & role +- wave 1: rolebinding +- wave 2: pod + +They are setup in such a way that the resources will be cleaned up properly only if they are deleted in the reverse order of creation i.e +- wave 0: pod +- wave 1: rolebinding +- wave 2: sa & role + +If above delete order is not followed the pod gets stuck in terminating state due to a finalizer which is supposed to be removed by k8s container lifecycle hook on delete if delete order is correct. \ No newline at end of file diff --git a/test/e2e/testdata/syncwaves-prune-order/pod.yaml b/test/e2e/testdata/syncwaves-prune-order/pod.yaml new file mode 100644 index 0000000000000..f801a3992aa37 --- /dev/null +++ b/test/e2e/testdata/syncwaves-prune-order/pod.yaml @@ -0,0 +1,41 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod-with-finalizers + annotations: + argocd.argoproj.io/sync-wave: "2" + # remove this finalizers using container preStop lifecycle hook on delete + finalizers: + - example.com/block-delete +spec: + serviceAccountName: modify-pods-sa # sa with permissions to modify pods + terminationGracePeriodSeconds: 15 + containers: + - name: container + image: nginx:alpine + command: ["/bin/sh", "-c"] + args: ["sleep 10h"] + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + lifecycle: + # remove finalizers for successful delete of pod + preStop: + exec: + command: + - /bin/sh + - -c + - | + set -e + + SERVICE_ACCOUNT_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + POD_URL="https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/pods/$POD_NAME" + PATCH_PAYLOAD='[{"op": "remove", "path": "/metadata/finalizers"}]' + + curl -k -v -H "Authorization: Bearer $SERVICE_ACCOUNT_TOKEN" -H "Content-Type: application/json-patch+json" -X PATCH --data "$PATCH_PAYLOAD" $POD_URL diff --git a/test/e2e/testdata/syncwaves-prune-order/rbac.yaml b/test/e2e/testdata/syncwaves-prune-order/rbac.yaml new file mode 100644 index 0000000000000..9512644b731db --- /dev/null +++ b/test/e2e/testdata/syncwaves-prune-order/rbac.yaml @@ -0,0 +1,37 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: modify-pods-sa + annotations: + argocd.argoproj.io/sync-wave: "0" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: modify-pods-role + annotations: + argocd.argoproj.io/sync-wave: "0" +rules: + - apiGroups: [""] + resources: + - pods + verbs: + - get + - list + - delete + - update + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: modify-pods-rolebinding + annotations: + argocd.argoproj.io/sync-wave: "1" +subjects: + - kind: ServiceAccount + name: modify-pods-sa +roleRef: + kind: Role + name: modify-pods-role + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/test/e2e/testdata2/cmp-symlink/plugin.yaml b/test/e2e/testdata2/cmp-symlink/plugin.yaml new file mode 100644 index 0000000000000..848deaeab3d28 --- /dev/null +++ b/test/e2e/testdata2/cmp-symlink/plugin.yaml @@ -0,0 +1,13 @@ +apiVersion: argoproj.io/v1alpha1 +kind: ConfigManagementPlugin +metadata: + name: cmp-symlink +spec: + version: v1.0 + init: + command: [kustomize, version] + generate: + command: [sh, -c, 'kustomize edit set image test=quay.io/argoprojlabs/argocd-e2e-container:0.2 && kustomize build --load-restrictor LoadRestrictionsNone'] + discover: + find: + glob: "**/kustomization.yaml" diff --git a/test/e2e/testdata2/guestbook-partial-symlink-files/guestbook-ui-deployment.yaml b/test/e2e/testdata2/guestbook-partial-symlink-files/guestbook-ui-deployment.yaml new file mode 120000 index 0000000000000..ee9a72011e097 --- /dev/null +++ b/test/e2e/testdata2/guestbook-partial-symlink-files/guestbook-ui-deployment.yaml @@ -0,0 +1 @@ +../guestbook/guestbook-ui-deployment.yaml \ No newline at end of file diff --git a/test/e2e/testdata2/guestbook-partial-symlink-files/guestbook-ui-svc.yaml b/test/e2e/testdata2/guestbook-partial-symlink-files/guestbook-ui-svc.yaml new file mode 100644 index 0000000000000..e8a4a27fbae40 --- /dev/null +++ b/test/e2e/testdata2/guestbook-partial-symlink-files/guestbook-ui-svc.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: guestbook-ui +spec: + ports: + - port: 80 + targetPort: 80 + selector: + app: guestbook-ui diff --git a/test/e2e/testdata2/guestbook-partial-symlink-files/kustomization.yaml b/test/e2e/testdata2/guestbook-partial-symlink-files/kustomization.yaml new file mode 100644 index 0000000000000..ff46a361352b4 --- /dev/null +++ b/test/e2e/testdata2/guestbook-partial-symlink-files/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ./guestbook-ui-deployment.yaml + - ./guestbook-ui-svc.yaml diff --git a/test/e2e/testdata2/guestbook-symlink-files/guestbook-ui-deployment.yaml b/test/e2e/testdata2/guestbook-symlink-files/guestbook-ui-deployment.yaml new file mode 120000 index 0000000000000..ee9a72011e097 --- /dev/null +++ b/test/e2e/testdata2/guestbook-symlink-files/guestbook-ui-deployment.yaml @@ -0,0 +1 @@ +../guestbook/guestbook-ui-deployment.yaml \ No newline at end of file diff --git a/test/e2e/testdata2/guestbook-symlink-files/guestbook-ui-svc.yaml b/test/e2e/testdata2/guestbook-symlink-files/guestbook-ui-svc.yaml new file mode 120000 index 0000000000000..9dc306132bcb3 --- /dev/null +++ b/test/e2e/testdata2/guestbook-symlink-files/guestbook-ui-svc.yaml @@ -0,0 +1 @@ +../guestbook/guestbook-ui-svc.yaml \ No newline at end of file diff --git a/test/e2e/testdata2/guestbook-symlink-files/kustomization.yaml b/test/e2e/testdata2/guestbook-symlink-files/kustomization.yaml new file mode 120000 index 0000000000000..339635a9669f0 --- /dev/null +++ b/test/e2e/testdata2/guestbook-symlink-files/kustomization.yaml @@ -0,0 +1 @@ +../guestbook/kustomization.yaml \ No newline at end of file diff --git a/test/e2e/testdata2/guestbook-symlink-folder b/test/e2e/testdata2/guestbook-symlink-folder new file mode 120000 index 0000000000000..3132c00309c3a --- /dev/null +++ b/test/e2e/testdata2/guestbook-symlink-folder @@ -0,0 +1 @@ +guestbook \ No newline at end of file diff --git a/test/e2e/testdata2/guestbook/guestbook-ui-deployment.yaml b/test/e2e/testdata2/guestbook/guestbook-ui-deployment.yaml new file mode 100644 index 0000000000000..2e748234df4a4 --- /dev/null +++ b/test/e2e/testdata2/guestbook/guestbook-ui-deployment.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: guestbook-ui + labels: + test: "true" +spec: + replicas: 0 + revisionHistoryLimit: 3 + selector: + matchLabels: + app: guestbook-ui + template: + metadata: + labels: + app: guestbook-ui + spec: + containers: + - image: test + imagePullPolicy: IfNotPresent + name: guestbook-ui + ports: + - containerPort: 80 diff --git a/test/e2e/testdata2/guestbook/guestbook-ui-svc.yaml b/test/e2e/testdata2/guestbook/guestbook-ui-svc.yaml new file mode 100644 index 0000000000000..e8a4a27fbae40 --- /dev/null +++ b/test/e2e/testdata2/guestbook/guestbook-ui-svc.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: guestbook-ui +spec: + ports: + - port: 80 + targetPort: 80 + selector: + app: guestbook-ui diff --git a/test/e2e/testdata2/guestbook/kustomization.yaml b/test/e2e/testdata2/guestbook/kustomization.yaml new file mode 100644 index 0000000000000..ff46a361352b4 --- /dev/null +++ b/test/e2e/testdata2/guestbook/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ./guestbook-ui-deployment.yaml + - ./guestbook-ui-svc.yaml diff --git a/test/fixture/testrepos/start-git.sh b/test/fixture/testrepos/start-git.sh index b36da58a28388..4a660c29a7575 100755 --- a/test/fixture/testrepos/start-git.sh +++ b/test/fixture/testrepos/start-git.sh @@ -2,5 +2,5 @@ docker run --name e2e-git --rm -i \ -p 2222:2222 -p 9080:9080 -p 9443:9443 -p 9444:9444 -p 9445:9445 \ - -w /go/src/github.com/argoproj/argo-cd -v $(pwd):/go/src/github.com/argoproj/argo-cd -v /tmp:/tmp argoproj/argo-cd-ci-builder:v1.0.0 \ + -w /go/src/github.com/argoproj/argo-cd -v $(pwd):/go/src/github.com/argoproj/argo-cd -v /tmp:/tmp docker.io/argoproj/argo-cd-ci-builder:v1.0.0 \ bash -c "goreman -f ./test/fixture/testrepos/Procfile start" diff --git a/test/manifests/base/kustomization.yaml b/test/manifests/base/kustomization.yaml index 66aa10c182934..149554b317eba 100644 --- a/test/manifests/base/kustomization.yaml +++ b/test/manifests/base/kustomization.yaml @@ -1,11 +1,11 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -bases: +resources: - ../../../manifests/crds - ../../../manifests/base/config - ../../../manifests/cluster-rbac - ../../../manifests/base/notification -patchesStrategicMerge: - - patches.yaml +patches: + - path: patches.yaml diff --git a/test/manifests/cmp/kustomization.yaml b/test/manifests/cmp/kustomization.yaml index f0f55a990250e..a2387e42f453b 100644 --- a/test/manifests/cmp/kustomization.yaml +++ b/test/manifests/cmp/kustomization.yaml @@ -5,9 +5,9 @@ resources: - plugin.yaml - app.yaml -patchesStrategicMerge: - - repo-patch.yaml - - secret-patch.yaml +patches: + - path: repo-patch.yaml + - path: secret-patch.yaml images: - name: quay.io/argoproj/argocd diff --git a/test/manifests_test.go b/test/manifests_test.go index f5959ca40d29c..ce62b175d79b3 100644 --- a/test/manifests_test.go +++ b/test/manifests_test.go @@ -16,7 +16,7 @@ func TestKustomizeVersion(t *testing.T) { test.CIOnly(t) out, err := argoexec.RunCommand("kustomize", argoexec.CmdOpts{}, "version") assert.NoError(t, err) - assert.Contains(t, out, "Version:kustomize/v4", "kustomize should be version 4") + assert.Contains(t, out, "v5.", "kustomize should be version 5") } // TestBuildManifests makes sure we are consistent in naming, and all kustomization.yamls are buildable diff --git a/test/remote/Dockerfile b/test/remote/Dockerfile index 182dc47e9e69c..cf43ee355567d 100644 --- a/test/remote/Dockerfile +++ b/test/remote/Dockerfile @@ -1,6 +1,6 @@ ARG BASE_IMAGE=docker.io/library/ubuntu:22.04 -FROM golang:1.18 AS go +FROM docker.io/library/golang:1.22.0@sha256:7b297d9abee021bab9046e492506b3c2da8a3722cbf301653186545ecc1e00bb AS go RUN go install github.com/mattn/goreman@latest && \ go install github.com/kisielk/godepgraph@latest @@ -9,21 +9,21 @@ FROM $BASE_IMAGE ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install --no-install-recommends -y \ - ca-certificates \ - curl \ - openssh-server \ - nginx \ - fcgiwrap \ - git \ - git-lfs \ - gpg \ - make \ - wget \ - gcc \ - sudo \ - zip && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + ca-certificates \ + curl \ + openssh-server \ + nginx \ + fcgiwrap \ + git \ + git-lfs \ + gpg \ + make \ + wget \ + gcc \ + sudo \ + zip && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # These are required for running end-to-end tests COPY ./test/fixture/testrepos/id_rsa.pub /root/.ssh/authorized_keys diff --git a/test/remote/Makefile b/test/remote/Makefile index 5925d3745d968..4bc9b90811b65 100644 --- a/test/remote/Makefile +++ b/test/remote/Makefile @@ -2,8 +2,8 @@ PWD=$(shell pwd) TEST_ROOT=$(shell realpath $(PWD)/../..) IMAGE_NAMESPACE?= -IMAGE_NAME=argocd-e2e-cluster -IMAGE_TAG=latest +IMAGE_NAME?=argocd-e2e-cluster +IMAGE_TAG?=latest ifneq (${IMAGE_NAMESPACE},) IMAGE_PREFIX=$(IMAGE_NAMESPACE)/ else diff --git a/test/remote/README.md b/test/remote/README.md index 25364ab61c26b..0c05dce914d32 100644 --- a/test/remote/README.md +++ b/test/remote/README.md @@ -128,7 +128,7 @@ Run the tests In another shell, do a port-forward to the API server's service: ```shell -kubectl -n argocd-e2e port-forward svc/argocd-server 443:4443 +kubectl -n argocd-e2e port-forward svc/argocd-server 4443:443 ``` Set Argo CD Server port: @@ -140,7 +140,7 @@ export ARGOCD_SERVER=127.0.0.1:4443 Set the admin password to use: ```shell -export ARGOCD_E2E_ADMIN_PASSWORD=$(kubectl get secrets argocd-initial-admin-secret -o jsonpath='{.data.password}'|base64 -d) +export ARGOCD_E2E_ADMIN_PASSWORD=$(kubectl -n argocd-e2e get secrets argocd-initial-admin-secret -o jsonpath='{.data.password}'|base64 -d) ``` Run the tests diff --git a/test/testdata.go b/test/testdata.go index 4531a11c6eb69..2c5c6178317e3 100644 --- a/test/testdata.go +++ b/test/testdata.go @@ -4,9 +4,8 @@ import ( "context" "github.com/alicebob/miniredis/v2" - "github.com/go-redis/redis/v8" - "github.com/argoproj/gitops-engine/pkg/utils/testing" + "github.com/redis/go-redis/v9" apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -86,6 +85,22 @@ func NewDeployment() *unstructured.Unstructured { return testing.Unstructured(DeploymentManifest) } +var ConfigMapManifest = ` +{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": { + "name": "my-configmap", + }, + "data": { + "config.yaml": "auth: token\nconfig:field" + } +}` + +func NewConfigMap() *unstructured.Unstructured { + return testing.Unstructured(ConfigMapManifest) +} + func NewFakeConfigMap() *apiv1.ConfigMap { cm := apiv1.ConfigMap{ TypeMeta: metav1.TypeMeta{ diff --git a/test/testutil.go b/test/testutil.go index bccf0cef23a3f..e97de4a762bcf 100644 --- a/test/testutil.go +++ b/test/testutil.go @@ -1,18 +1,20 @@ package test import ( + "bytes" "context" "encoding/json" "fmt" - "log" "net" "os" "testing" "time" - "github.com/ghodss/yaml" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/client-go/tools/cache" + "sigs.k8s.io/yaml" ) // StartInformer is a helper to start an informer, wait for its cache to sync and return a cancel func @@ -83,6 +85,15 @@ func YamlToUnstructured(yamlStr string) *unstructured.Unstructured { return &unstructured.Unstructured{Object: obj} } +func YamlToApplication(yamlStr string) *v1alpha1.Application { + app := v1alpha1.Application{} + err := yaml.Unmarshal([]byte(yamlStr), &app) + if err != nil { + panic(err) + } + return &app +} + // ToMap converts any object to a map[string]interface{} func ToMap(obj interface{}) map[string]interface{} { data, err := json.Marshal(obj) @@ -107,3 +118,18 @@ func GetTestDir(t *testing.T) string { } return cwd } + +// CaptureLogEntries captures log entries generated by the logger and return it as string +func CaptureLogEntries(run func()) string { + f := log.StandardLogger().Formatter + log.SetFormatter(&log.TextFormatter{DisableColors: true}) + defer log.SetFormatter(f) + output := bytes.NewBuffer(nil) + log.SetOutput(output) + log.SetLevel(log.DebugLevel) + defer log.SetOutput(os.Stdout) + + run() + + return output.String() +} diff --git a/tools/cmd-docs/main.go b/tools/cmd-docs/main.go index 6b49678a4b73a..aace315302d4b 100644 --- a/tools/cmd-docs/main.go +++ b/tools/cmd-docs/main.go @@ -1,8 +1,11 @@ package main import ( + "fmt" "log" "os" + "path/filepath" + "strings" "github.com/spf13/cobra/doc" @@ -18,27 +21,36 @@ func main() { os.Setenv("HOME", "/home/user") os.Setenv("XDG_CONFIG_HOME", "/home/user/.config") - err := doc.GenMarkdownTree(argocdcli.NewCommand(), "./docs/user-guide/commands") + identity := func(s string) string { return s } + headerPrepender := func(filename string) string { + // The default header looks like `Argocd app get`. The leading capital letter is off-putting. + // This header overrides the default. It's better visually and for search results. + filename = filepath.Base(filename) + filename = filename[:len(filename)-3] // Drop the '.md' + return fmt.Sprintf("# `%s` Command Reference\n\n", strings.ReplaceAll(filename, "_", " ")) + } + + err := doc.GenMarkdownTreeCustom(argocdcli.NewCommand(), "./docs/user-guide/commands", headerPrepender, identity) if err != nil { log.Fatal(err) } - err = doc.GenMarkdownTree(argocdserver.NewCommand(), "./docs/operator-manual/server-commands") + err = doc.GenMarkdownTreeCustom(argocdserver.NewCommand(), "./docs/operator-manual/server-commands", headerPrepender, identity) if err != nil { log.Fatal(err) } - err = doc.GenMarkdownTree(controller.NewCommand(), "./docs/operator-manual/server-commands") + err = doc.GenMarkdownTreeCustom(controller.NewCommand(), "./docs/operator-manual/server-commands", headerPrepender, identity) if err != nil { log.Fatal(err) } - err = doc.GenMarkdownTree(reposerver.NewCommand(), "./docs/operator-manual/server-commands") + err = doc.GenMarkdownTreeCustom(reposerver.NewCommand(), "./docs/operator-manual/server-commands", headerPrepender, identity) if err != nil { log.Fatal(err) } - err = doc.GenMarkdownTree(argocddex.NewCommand(), "./docs/operator-manual/server-commands") + err = doc.GenMarkdownTreeCustom(argocddex.NewCommand(), "./docs/operator-manual/server-commands", headerPrepender, identity) if err != nil { log.Fatal(err) } diff --git a/ui-test/Dockerfile b/ui-test/Dockerfile index a58d05f23fda4..46231bad8d142 100644 --- a/ui-test/Dockerfile +++ b/ui-test/Dockerfile @@ -1,4 +1,4 @@ -FROM node:12.18.4 AS node +FROM docker.io/library/node:21.7.1@sha256:b9ccc4aca32eebf124e0ca0fd573dacffba2b9236987a1d4d2625ce3c162ecc8 as node RUN apt-get update && apt-get install --no-install-recommends -y \ software-properties-common diff --git a/ui-test/osv-scanner.toml b/ui-test/osv-scanner.toml new file mode 100644 index 0000000000000..fe1660cf6d497 --- /dev/null +++ b/ui-test/osv-scanner.toml @@ -0,0 +1,15 @@ +[[IgnoredVulns]] +id = "GHSA-93q8-gq69-wqmw" +reason = "CVE-2021-3807 Code is only run client-side in the swagger-ui endpoint. No risk of server-side DoS." + +[[IgnoredVulns]] +id = "GHSA-36fh-84j7-cv5h" +reason = "Used in testing, does not affect a release" + +[[IgnoredVulns]] +id = "GHSA-f8q6-p94x-37v3" +reason = "Used in testing, does not affect a release" + +[[IgnoredVulns]] +id = "GHSA-qrpm-p2h7-hrv2" +reason = "Used in testing, does not affect a release" \ No newline at end of file diff --git a/ui-test/package.json b/ui-test/package.json index 1875e31b6fd62..fd34ca2edab4a 100644 --- a/ui-test/package.json +++ b/ui-test/package.json @@ -27,6 +27,6 @@ "tslint-config-prettier": "^1.18.0", "tslint-plugin-prettier": "^2.0.1", "typescript": "^4.0.3", - "yarn": "^1.22.10" + "yarn": "^1.22.13" } } diff --git a/ui-test/yarn.lock b/ui-test/yarn.lock index dbda283031734..9d7f089c6f4d9 100644 --- a/ui-test/yarn.lock +++ b/ui-test/yarn.lock @@ -540,9 +540,9 @@ flat@^5.0.2: integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== follow-redirects@^1.14.0: - version "1.14.9" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" - integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== foreach@^2.0.5: version "2.0.5" @@ -1222,9 +1222,9 @@ selenium-webdriver@^4.0.0-alpha.7: ws "^7.3.1" semver@^5.3.0: - version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== serialize-javascript@5.0.1: version "5.0.1" @@ -1510,10 +1510,10 @@ yargs@13.3.2: y18n "^4.0.0" yargs-parser "^13.1.2" -yarn@^1.22.10: - version "1.22.10" - resolved "https://registry.npmjs.org/yarn/-/yarn-1.22.10.tgz" - integrity sha512-IanQGI9RRPAN87VGTF7zs2uxkSyQSrSPsju0COgbsKQOOXr5LtcVPeyXWgwVa0ywG3d8dg6kSYKGBuYK021qeA== +yarn@^1.22.13: + version "1.22.13" + resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.13.tgz#8789ef23b630fe99b819b044f4b7b93ab1bc1b8f" + integrity sha512-G8qG4t7Ef5cLVpzbM3HWWsow4hpfeSCfKtMnjfERmp9V5qSCOKz0uGAIQCM/x3gWfCzH8Bvb4hl3ZfhG/XD1Jg== yauzl@^2.10.0: version "2.10.0" diff --git a/ui/.nvmrc b/ui/.nvmrc index 82f87fa0a5788..a8d3ff91fa10d 100644 --- a/ui/.nvmrc +++ b/ui/.nvmrc @@ -1 +1 @@ -v12.18.4 +v21.6.1 diff --git a/ui/embed.go b/ui/embed.go index 665621a9c12fd..8f9c2774fbbca 100644 --- a/ui/embed.go +++ b/ui/embed.go @@ -3,5 +3,6 @@ package ui import "embed" // Embedded contains embedded UI resources +// //go:embed dist/app var Embedded embed.FS diff --git a/ui/jest.config.js b/ui/jest.config.js index abd8a45bcecd6..524b493f546fc 100644 --- a/ui/jest.config.js +++ b/ui/jest.config.js @@ -1,12 +1,11 @@ module.exports = { preset: 'ts-jest', - testEnvironment: 'node', + testEnvironment: 'jsdom', reporters: ['default', 'jest-junit'], collectCoverage: true, transformIgnorePatterns: ['node_modules/(?!(argo-ui)/)'], globals: { 'self': {}, - 'window': {localStorage: { getItem: () => '{}', setItem: () => null }}, 'ts-jest': { isolatedModules: true, }, @@ -17,20 +16,3 @@ module.exports = { '.+\\.(css|styl|less|sass|scss)$': 'jest-transform-css', }, }; - -const localStorageMock = (() => { - let store = {}; - return { - getItem: (key) => store[key], - setItem: (key, value) => { - store[key] = value.toString(); - }, - clear: () => { - store = {}; - }, - removeItem: (key) => { - delete store[key]; - } - }; -})(); -global.localStorage = localStorageMock; \ No newline at end of file diff --git a/ui/osv-scanner.toml b/ui/osv-scanner.toml new file mode 100644 index 0000000000000..683f8c5c4b866 --- /dev/null +++ b/ui/osv-scanner.toml @@ -0,0 +1,3 @@ +[[IgnoredVulns]] +id = "GHSA-93q8-gq69-wqmw" +reason = "CVE-2021-3807 Code is only run client-side in the swagger-ui endpoint. No risk of server-side DoS." diff --git a/ui/package.json b/ui/package.json index b5fcdc9808a2b..8eaaaa26dfcfe 100644 --- a/ui/package.json +++ b/ui/package.json @@ -6,26 +6,32 @@ "start": "webpack-dev-server --config ./src/app/webpack.config.js --mode development", "docker": "./scripts/build_docker.sh", "build": "find ./dist -type f -not -name gitkeep -delete && webpack --config ./src/app/webpack.config.js --mode production", - "lint": "tslint -p ./src/app", + "lint": "tsc --noEmit --project ./src/app && tslint -p ./src/app", + "lint:fix": "tslint -p ./src/app --fix", "test": "jest" }, "dependencies": { - "@types/superagent": "^4.1.15", + "@fortawesome/fontawesome-free": "^6.4.0", + "@types/react-virtualized": "^9.21.21", + "@types/superagent": "^4.1.21", "ansi-to-react": "^6.1.6", "argo-ui": "git+https://github.com/argoproj/argo-ui.git", "buffer": "^6.0.3", "classnames": "^2.2.5", "color": "^3.2.1", "dagre": "^0.8.5", + "date-fns": "^2.30.0", "deepmerge": "^3.2.0", - "foundation-sites": "^6.7.4", + "foundation-sites": "^6.7.5", "git-url-parse": "^13.1.0", + "history": "^4.7.2", "js-yaml": "^3.14.1", "json-merge-patch": "^0.2.3", "lodash-es": "^4.17.21", "minimatch": "^3.1.2", "moment": "^2.29.4", "monaco-editor": "^0.33.0", + "oauth4webapi": "^2.3.0", "path": "^0.12.7", "prop-types": "^15.8.1", "react": "^16.9.3", @@ -34,27 +40,29 @@ "react-dom": "^16.9.3", "react-form": "2.16.3", "react-ga": "^2.7.0", - "react-helmet": "^5.2.0", + "react-helmet": "^6.1.0", "react-hot-loader": "^3.1.3", "react-moment": "^0.9.7", "react-paginate": "^8.1.4", "react-router": "^4.3.1", "react-router-dom": "^4.2.2", "react-svg-piechart": "^2.4.2", + "react-virtualized": "^9.22.3", "redoc": "^2.0.0-rc.64", "rxjs": "^6.6.6", - "superagent": "^7.1.6", + "superagent": "^8.1.2", "timezones-list": "3.0.1", "tsx": "^3.4.0", "unidiff": "^1.0.2", "url": "^0.11.0", - "xterm": "^4.18.0", + "xterm": "^4.19.0", "xterm-addon-fit": "^0.5.0" }, "resolutions": { "@types/react": "^16.9.3", "@types/react-dom": "^16.8.2", - "normalize-url": "4.3.0" + "normalize-url": "4.3.0", + "rxjs": "6.6.7" }, "devDependencies": { "@babel/core": "^7.7.2", @@ -70,20 +78,22 @@ "@types/js-yaml": "^3.11.2", "@types/lodash-es": "^4.17.6", "@types/minimatch": "^3.0.3", + "@types/node": "20.6.3", "@types/prop-types": "^15.7.5", "@types/react": "^16.8.5", "@types/react-autocomplete": "^1.8.4", "@types/react-dom": "^16.9.14", "@types/react-form": "^2.16.0", - "@types/react-helmet": "^5.0.17", + "@types/react-helmet": "^6.1.6", "@types/react-paginate": "^6.2.0", "@types/react-router": "^4.0.27", "@types/react-router-dom": "^4.2.3", "@types/react-test-renderer": "^16.8.3", + "@types/uuid": "^9.0.1", "add": "^2.0.6", "babel-jest": "^26.6.3", "babel-loader": "^8.0.6", - "codecov": "^3.7.2", + "codecov": "^3.8.3", "copy-webpack-plugin": "^6.1.1", "esbuild-loader": "^2.18.0", "html-webpack-plugin": "^5.5.0", @@ -92,7 +102,7 @@ "jest-junit": "^6.4.0", "jest-transform-css": "^2.0.0", "monaco-editor-webpack-plugin": "^7.0.0", - "postcss": "^8.2.13", + "postcss": "^8.4.38", "prettier": "1.19", "raw-loader": "^0.5.1", "react-test-renderer": "16.8.3", @@ -101,15 +111,15 @@ "source-map-loader": "^0.2.3", "style-loader": "^0.20.1", "ts-jest": "^27.1.3", - "ts-node": "^10.7.0", + "ts-node": "10.9.1", "tslint": "^6.1.3", "tslint-config-prettier": "^1.18.0", "tslint-plugin-prettier": "^2.0.1", "tslint-react": "^5.0.0", - "typescript": "^4.0.3", - "webpack": "^5.70.0", + "typescript": "^4.9.5", + "webpack": "^5.84.1", "webpack-cli": "^4.9.2", "webpack-dev-server": "^4.7.4", - "yarn": "^1.22.10" + "yarn": "^1.22.21" } } diff --git a/ui/src/app/app.tsx b/ui/src/app/app.tsx index b474c86b3ca84..d0a58d3fbdc7f 100644 --- a/ui/src/app/app.tsx +++ b/ui/src/app/app.tsx @@ -9,6 +9,7 @@ import help from './help'; import login from './login'; import settings from './settings'; import {Layout} from './shared/components/layout/layout'; +import {Page} from './shared/components/page/page'; import {VersionPanel} from './shared/components/version-info/version-info-panel'; import {AuthSettingsCtx, Provider} from './shared/context'; import {services} from './shared/services'; @@ -17,6 +18,7 @@ import {hashCode} from './shared/utils'; import {Banner} from './ui-banner/ui-banner'; import userInfo from './user-info'; import {AuthSettings} from './shared/models'; +import {PKCEVerification} from './login/components/pkce-verify'; services.viewPreferences.init(); const bases = document.getElementsByTagName('base'); @@ -31,7 +33,8 @@ const routes: Routes = { '/applications': {component: applications.component}, '/settings': {component: settings.component}, '/user-info': {component: userInfo.component}, - '/help': {component: help.component} + '/help': {component: help.component}, + '/pkce/verify': {component: PKCEVerification, noLayout: true} }; interface NavItem { @@ -176,7 +179,9 @@ export class App extends React.Component< {extension.title} - Argo CD - + + + ); extendedRoutes[extension.path] = { @@ -214,7 +219,9 @@ export class App extends React.Component< - {this.state.popupProps && } + services.viewPreferences.getPreferences()}> + {pref =>
    {this.state.popupProps && }
    } +
    diff --git a/ui/src/app/applications/components/application-conditions/application-conditions.scss b/ui/src/app/applications/components/application-conditions/application-conditions.scss index 13291efa9421f..d2b6003cddd5f 100644 --- a/ui/src/app/applications/components/application-conditions/application-conditions.scss +++ b/ui/src/app/applications/components/application-conditions/application-conditions.scss @@ -20,5 +20,11 @@ .columns { white-space: normal; } + + .row { + line-height: 2; + padding-top: 15px; + padding-bottom: 15px; + } } } diff --git a/ui/src/app/applications/components/application-conditions/application-conditions.tsx b/ui/src/app/applications/components/application-conditions/application-conditions.tsx index 5c174caaa1425..317b135eeffe4 100644 --- a/ui/src/app/applications/components/application-conditions/application-conditions.tsx +++ b/ui/src/app/applications/components/application-conditions/application-conditions.tsx @@ -16,7 +16,9 @@ export const ApplicationConditions = ({conditions}: {conditions: models.Applicat
    {condition.type}
    -
    {condition.message}
    +
    + {condition.message} +
    diff --git a/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx b/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx index db87258b6bea3..eef8c8ec32103 100644 --- a/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx +++ b/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx @@ -108,15 +108,24 @@ export const ApplicationCreatePanel = (props: { const [explicitPathType, setExplicitPathType] = React.useState<{path: string; type: models.AppSourceType}>(null); const [destFormat, setDestFormat] = React.useState('URL'); const [retry, setRetry] = React.useState(false); + const app = deepMerge(DEFAULT_APP, props.app || {}); + + React.useEffect(() => { + if (app?.spec?.destination?.name && app.spec.destination.name !== '') { + setDestFormat('NAME'); + } else { + setDestFormat('URL'); + } + }, []); function normalizeTypeFields(formApi: FormApi, type: models.AppSourceType) { - const app = formApi.getFormState().values; + const appToNormalize = formApi.getFormState().values; for (const item of appTypes) { if (item.type !== type) { - delete app.spec.source[item.field]; + delete appToNormalize.spec.source[item.field]; } } - formApi.setAllValues(app); + formApi.setAllValues(appToNormalize); } return ( @@ -132,7 +141,6 @@ export const ApplicationCreatePanel = (props: { }> {({projects, clusters, reposInfo}) => { const repos = reposInfo.map(info => info.repo).sort(); - const app = deepMerge(DEFAULT_APP, props.app || {}); const repoInfo = reposInfo.find(info => info.repo === app.spec.source.repoURL); if (repoInfo) { normalizeAppSource(app, repoInfo.type || 'git'); @@ -211,7 +219,10 @@ export const ApplicationCreatePanel = (props: { qeId='application-create-field-project' field='spec.project' component={AutocompleteField} - componentProps={{items: projects}} + componentProps={{ + items: projects, + filterSuggestions: true + }} />
    diff --git a/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx b/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx index b612c9ed9056c..37908fb1a35b8 100644 --- a/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx +++ b/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx @@ -1,4 +1,5 @@ import {DataLoader, DropDownMenu, Duration} from 'argo-ui'; +import {InitiatedBy} from './initiated-by'; import * as moment from 'moment'; import * as React from 'react'; import {Revision, Timestamp} from '../../../shared/components'; @@ -32,16 +33,22 @@ export const ApplicationDeploymentHistory = ({
    selectDeployment(index)}>
    - Deployed At: + Deployed At:

    - Time to deploy: + Time to deploy:
    {(info.deployStartedAt && ) || 'Unknown'}
    +
    +
    + Initiated by: +
    + +

    Active for: diff --git a/ui/src/app/applications/components/application-deployment-history/initiated-by.tsx b/ui/src/app/applications/components/application-deployment-history/initiated-by.tsx new file mode 100644 index 0000000000000..f691389b5daca --- /dev/null +++ b/ui/src/app/applications/components/application-deployment-history/initiated-by.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; + +export const InitiatedBy = (props: {username: string; automated: boolean}) => { + const initiator = props.automated ? 'automated sync policy' : props.username || 'Unknown'; + return {initiator}; +}; diff --git a/ui/src/app/applications/components/application-deployment-history/revision-metadata-rows.tsx b/ui/src/app/applications/components/application-deployment-history/revision-metadata-rows.tsx index 21d7d65133e57..3fa7c62ed1caa 100644 --- a/ui/src/app/applications/components/application-deployment-history/revision-metadata-rows.tsx +++ b/ui/src/app/applications/components/application-deployment-history/revision-metadata-rows.tsx @@ -1,22 +1,46 @@ import {DataLoader} from 'argo-ui'; import * as React from 'react'; import {Timestamp} from '../../../shared/components/timestamp'; -import {ApplicationSource, RevisionMetadata} from '../../../shared/models'; +import {ApplicationSource, RevisionMetadata, ChartDetails} from '../../../shared/models'; import {services} from '../../../shared/services'; export const RevisionMetadataRows = (props: {applicationName: string; applicationNamespace: string; source: ApplicationSource}) => { if (props.source.chart) { return ( -
    -
    -
    Helm Chart
    -
    {props.source.chart}
    -
    -
    -
    Version
    -
    v{props.source.targetRevision}
    -
    -
    + services.applications.revisionChartDetails(input.applicationName, input.applicationNamespace, input.source.targetRevision)}> + {(m: ChartDetails) => ( +
    +
    +
    Helm Chart:
    +
    + {props.source.chart}  + {m.home && ( + { + e.stopPropagation(); + window.open(m.home); + }}> + + + )} +
    +
    + {m.description && ( +
    +
    Description:
    +
    {m.description}
    +
    + )} + {m.maintainers && m.maintainers.length > 0 && ( +
    +
    Maintainers:
    +
    {m.maintainers.join(', ')}
    +
    + )} +
    + )} +
    ); } return ( diff --git a/ui/src/app/applications/components/application-details/application-details.scss b/ui/src/app/applications/components/application-details/application-details.scss index 9f7433d2f7e14..82402ffd0d8b4 100644 --- a/ui/src/app/applications/components/application-details/application-details.scss +++ b/ui/src/app/applications/components/application-details/application-details.scss @@ -1,5 +1,6 @@ @import 'node_modules/argo-ui/src/styles/config'; @import 'node_modules/foundation-sites/scss/util/util'; +@import 'node_modules/argo-ui/src/styles/theme'; @import '../../../shared/config.scss'; $header: 120px; @@ -7,16 +8,16 @@ $header: 120px; .application-details { height: 100vh; width: 100%; - &__status-panel { - position: fixed; - left: $sidebar-width; - right: 0; - z-index: 3; - @media screen and (max-width: map-get($breakpoints, xlarge)) { - top: 150px; - } - @media screen and (max-width: map-get($breakpoints, large)) { - top: 146px; + + &__wrapper { + display: flex; + flex-direction: column; + height: calc(100vh - 2 * $top-bar-height); + overflow: hidden; + + @media screen and (max-width: map-get($breakpoints, xxlarge)) { + height: calc(100vh - 3 * $top-bar-height); + margin-top: $top-bar-height; } } @@ -26,13 +27,11 @@ $header: 120px; &__tree { padding: 1em; + + flex: 1; overflow-x: auto; overflow-y: auto; - margin-top: 115px; - height: calc(100vh - 2 * 50px - 115px); - @media screen and (max-width: map-get($breakpoints, xlarge)) { - margin-top: 165px; - } + overscroll-behavior-x: none; } &__sliding-panel-pagination-wrap { @@ -48,7 +47,7 @@ $header: 120px; &__refreshing-label { color: $white-color; position: fixed; - top: $header + 100px; + margin-top: -20px; left: 50%; background-color: $argo-color-gray-7; border: 1px solid $argo-color-gray-5; @@ -154,7 +153,7 @@ $header: 120px; } } - @media screen and (max-width: map-get($breakpoints, xlarge)) { + @media screen and (max-width: map-get($breakpoints, xxlarge)) { .page__content-wrapper { min-height: calc(100vh - 3 * 50px); } @@ -211,9 +210,13 @@ $header: 120px; z-index: 1; padding: 5px; display: inline-block; - background-color: $argo-color-gray-1; box-shadow: 1px 1px 3px $argo-color-gray-5; - position: fixed; + position: absolute; + + @include themify($themes) { + background: themed('background-2'); + } + a { padding: 5px; @@ -227,7 +230,6 @@ $header: 120px; position: relative; display: inline-block; vertical-align: middle; - font-size: 13px; font-weight: 500; line-height: 1.4; text-align: center; @@ -246,21 +248,27 @@ $header: 120px; &.group-nodes-button-on { color: $argo-color-gray-1; background-color: $argo-color-gray-6; + border: 3px solid $argo-color-teal-4; + font-size: 14px; + outline-style: solid; &:hover { background-color: $argo-color-gray-5; } + } } .separator { - border-right: 1px solid $argo-color-gray-4; + @include themify($themes) { + border-right: 1px solid themed('border'); + } padding-top: 6px; padding-bottom: 6px; } .zoom-value { user-select: none; - margin-top: 3px; + margin-top: 5px; margin-right: 6px; margin-left: 4px; font-size: 14px; @@ -273,75 +281,97 @@ $header: 120px; color: $argo-color-gray-7; } } -} -@media screen and (max-width: map-get($breakpoints, large)) { -.sliding-panel__body { - padding: 4px !important; -} -.sliding-panel--is-middle .sliding-panel__wrapper { - width: 90% !important; -} -.sliding-panel--is-middle .sliding-panel__body { - padding: 18px !important; -} -.sliding-panel__close { - z-index: 2 !important; -} -.top-bar__title { - display: none; -} + @media screen and (max-width: map-get($breakpoints, large)) { + .sliding-panel__body { + padding: 4px !important; + } + .sliding-panel--is-middle .sliding-panel__wrapper { + width: 90% !important; + } + .sliding-panel--is-middle .sliding-panel__body { + padding: 18px !important; + } + .sliding-panel__close { + z-index: 2 !important; + } + .top-bar__title { + display: none; + } -.top-bar__left-side { - white-space: normal !important; -} -.top-bar__left-side > div { - display: block !important; -} -.top-bar__right-side { - justify-content: right !important; -} -.application-status-panel.row { - flex-flow: unset; -} -.application-status-panel__item label { - margin-right: 0; -} -.application-status-panel__item { - padding: 5px 10px; -} + .top-bar__left-side { + white-space: normal !important; + } + .top-bar__left-side > div { + display: block !important; + } + .top-bar__right-side { + justify-content: right !important; + } + .application-status-panel.row { + flex-flow: unset; + } + .application-status-panel__item label { + margin-right: 0; + } + .application-status-panel__item { + padding: 5px 10px; + } -.white-box, .tabs__content { - padding: 4px !important; -} -.white-box__details-row .columns.small-3 { - overflow-wrap: unset !important; - overflow: scroll; -} -.white-box__details-row .columns.small-9{ - padding-left: 4px; -} + .white-box, .tabs__content { + padding: 4px !important; + } + .white-box__details-row .columns.small-3 { + overflow-wrap: unset !important; + overflow: scroll; + } + .white-box__details-row .columns.small-9{ + padding-left: 4px; + } -.resource-details__header h1 { - font-size: 16px; -} -.resource-details__header { - margin-top: 30px; - padding-right: 4px; -} + .resource-details__header h1 { + font-size: 16px; + } + .resource-details__header { + margin-top: 30px; + padding-right: 4px; + } -.tabs__nav a:first-child, .tabs__nav a { - margin-left: 0 !important; -} + .tabs__nav a:first-child, .tabs__nav a { + margin-left: 0 !important; + } -.editable-panel__buttons { - top: unset; -} + .editable-panel__buttons { + top: unset; + } + } } -@media screen and (max-width: map-get($breakpoints, medium)) { -.sb-page-wrapper .top-bar.row { - display: none !important; +.resource-parent-node-info-title { + flex-direction: column; + color: $argo-color-gray-6; + + &__label { + display: flex; + margin-bottom: 0.25em; + flex-shrink: 1; + div:first-child { + margin-right: 10px; + width: 60px; + text-align: right; + } + div:last-child { + font-weight: 500; + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: left; + + } + } } -} \ No newline at end of file + + + diff --git a/ui/src/app/applications/components/application-details/application-details.tsx b/ui/src/app/applications/components/application-details/application-details.tsx index c1c0da6736510..a3e8175591dde 100644 --- a/ui/src/app/applications/components/application-details/application-details.tsx +++ b/ui/src/app/applications/components/application-details/application-details.tsx @@ -25,12 +25,12 @@ import * as AppUtils from '../utils'; import {ApplicationResourceList} from './application-resource-list'; import {Filters, FiltersProps} from './application-resource-filter'; import {getAppDefaultSource, urlPattern, helpTip} from '../utils'; -import {ResourceStatus} from '../../../shared/models'; +import {ChartDetails, ResourceStatus} from '../../../shared/models'; import {ApplicationsDetailsAppDropdown} from './application-details-app-dropdown'; import {useSidebarTarget} from '../../../sidebar/sidebar'; import './application-details.scss'; -import {AppViewExtension, ExtensionComponentProps} from '../../../shared/services/extensions-service'; +import {AppViewExtension, StatusPanelExtension} from '../../../shared/services/extensions-service'; interface ApplicationDetailsState { page: number; @@ -42,6 +42,8 @@ interface ApplicationDetailsState { collapsedNodes?: string[]; extensions?: AppViewExtension[]; extensionsMap?: {[key: string]: AppViewExtension}; + statusExtensions?: StatusPanelExtension[]; + statusExtensionsMap?: {[key: string]: StatusPanelExtension}; } interface FilterInput { @@ -87,8 +89,23 @@ export class ApplicationDetails extends React.Component { extensionsMap[ext.title] = ext; }); - - this.state = {page: 0, groupedResources: [], slidingPanelPage: 0, filteredGraph: [], truncateNameOnRight: false, collapsedNodes: [], extensions, extensionsMap}; + const statusExtensions = services.extensions.getStatusPanelExtensions(); + const statusExtensionsMap: {[key: string]: StatusPanelExtension} = {}; + statusExtensions.forEach(ext => { + statusExtensionsMap[ext.id] = ext; + }); + this.state = { + page: 0, + groupedResources: [], + slidingPanelPage: 0, + filteredGraph: [], + truncateNameOnRight: false, + collapsedNodes: [], + extensions, + extensionsMap, + statusExtensions, + statusExtensionsMap + }; if (typeof this.props.match.params.appnamespace === 'undefined') { this.appNamespace = ''; } else { @@ -134,12 +151,17 @@ export class ApplicationDetails extends React.Component (usrMsg.appName === appName && usrMsg.msgKey === 'groupNodes' ? {...usrMsg, display: true} : usrMsg)); services.viewPreferences.updatePreferences({appDetails: {...pref, groupNodes: !pref.groupNodes}}); } @@ -223,6 +245,7 @@ export class ApplicationDetails extends React.Component usrMsg.appName === application.metadata.name); const resourceNodes = (): any[] => { const statusByKey = new Map(); application.status.resources.forEach(res => statusByKey.set(AppUtils.nodeKey(res), res)); @@ -285,6 +308,17 @@ export class ApplicationDetails extends React.Component { this.setState({filteredGraph: filterGraph}); }; + const setShowCompactNodes = (showCompactView: boolean) => { + services.viewPreferences.updatePreferences({appDetails: {...pref, groupNodes: showCompactView}}); + }; + const updateHelpTipState = (usrHelpTip: models.UserMessages) => { + const existingIndex = pref.userHelpTipMsgs.findIndex(msg => msg.appName === usrHelpTip.appName && msg.msgKey === usrHelpTip.msgKey); + if (existingIndex !== -1) { + pref.userHelpTipMsgs[existingIndex] = usrHelpTip; + } else { + (pref.userHelpTipMsgs || []).push(usrHelpTip); + } + }; const toggleNameDirection = () => { this.setState({truncateNameOnRight: !this.state.truncateNameOnRight}); }; @@ -326,8 +360,17 @@ export class ApplicationDetails extends React.Component +
    ) }}> -
    - this.setOperationStatusVisible(true)} - showConditions={() => this.setConditionsStatusVisible(true)} - showMetadataInfo={revision => this.setState({...this.state, revision})} - /> -
    -
    - {refreshing &&

    Refreshing

    } - {((pref.view === 'tree' || pref.view === 'network') && ( - <> - services.viewPreferences.getPreferences()}> - {viewPref => ( - - )} - - - this.filterTreeNode(node, treeFilter)} - selectedNodeFullName={this.selectedNodeKey} - onNodeClick={fullName => this.selectNode(fullName)} - nodeMenu={node => - AppUtils.renderResourceMenu(node, application, tree, this.appContext, this.appChanged, () => - this.getApplicationActionMenu(application, false) - ) - } - showCompactNodes={pref.groupNodes} - tree={tree} - app={application} - showOrphanedResources={pref.orphanedResources} - useNetworkingHierarchy={pref.view === 'network'} - onClearFilter={clearFilter} - onGroupdNodeClick={groupdedNodeIds => openGroupNodeDetails(groupdedNodeIds)} - zoom={pref.zoom} - appContext={this.appContext} - nameDirection={this.state.truncateNameOnRight} - filters={pref.resourceFilter} - setTreeFilterGraph={setFilterGraph} - setNodeExpansion={(node, isExpanded) => this.setNodeExpansion(node, isExpanded)} - getNodeExpansion={node => this.getNodeExpansion(node)} - /> - - )) || - (pref.view === 'pods' && ( - this.selectNode(fullName)} - nodeMenu={node => - AppUtils.renderResourceMenu(node, application, tree, this.appContext, this.appChanged, () => - this.getApplicationActionMenu(application, false) - ) - } - quickStarts={node => AppUtils.renderResourceButtons(node, application, tree, this.appContext, this.appChanged)} - /> - )) || - (this.state.extensionsMap[pref.view] != null && ( - - )) || ( -
    +
    +
    + this.selectNode(appFullName, 0, 'diff')} + showOperation={() => this.setOperationStatusVisible(true)} + showConditions={() => this.setConditionsStatusVisible(true)} + showExtension={id => this.setExtensionPanelVisible(id)} + showMetadataInfo={revision => this.setState({...this.state, revision})} + /> +
    +
    + {refreshing &&

    Refreshing

    } + {((pref.view === 'tree' || pref.view === 'network') && ( + <> services.viewPreferences.getPreferences()}> {viewPref => ( )} - {(filteredRes.length > 0 && ( - this.setState({page})} - preferencesKey='application-details'> - {data => ( - this.selectNode(fullName)} - resources={data} - nodeMenu={node => - AppUtils.renderResourceMenu( - {...node, root: node}, - application, - tree, - this.appContext, - this.appChanged, - () => this.getApplicationActionMenu(application, false) - ) - } + + this.filterTreeNode(node, treeFilter)} + selectedNodeFullName={this.selectedNodeKey} + onNodeClick={fullName => this.selectNode(fullName)} + nodeMenu={node => + AppUtils.renderResourceMenu(node, application, tree, this.appContext.apis, this.appChanged, () => + this.getApplicationActionMenu(application, false) + ) + } + showCompactNodes={pref.groupNodes} + userMsgs={pref.userHelpTipMsgs} + tree={tree} + app={application} + showOrphanedResources={pref.orphanedResources} + useNetworkingHierarchy={pref.view === 'network'} + onClearFilter={clearFilter} + onGroupdNodeClick={groupdedNodeIds => openGroupNodeDetails(groupdedNodeIds)} + zoom={pref.zoom} + podGroupCount={pref.podGroupCount} + appContext={this.appContext} + nameDirection={this.state.truncateNameOnRight} + filters={pref.resourceFilter} + setTreeFilterGraph={setFilterGraph} + updateUsrHelpTipMsgs={updateHelpTipState} + setShowCompactNodes={setShowCompactNodes} + setNodeExpansion={(node, isExpanded) => this.setNodeExpansion(node, isExpanded)} + getNodeExpansion={node => this.getNodeExpansion(node)} + /> + + )) || + (pref.view === 'pods' && ( + this.selectNode(fullName)} + nodeMenu={node => + AppUtils.renderResourceMenu(node, application, tree, this.appContext.apis, this.appChanged, () => + this.getApplicationActionMenu(application, false) + ) + } + quickStarts={node => AppUtils.renderResourceButtons(node, application, tree, this.appContext.apis, this.appChanged)} + /> + )) || + (this.state.extensionsMap[pref.view] != null && ( + + )) || ( +
    + services.viewPreferences.getPreferences()}> + {viewPref => ( + )} - - )) || ( - -

    No resources found

    -
    Try to change filter criteria
    -
    - )} -
    - )} + + {(filteredRes.length > 0 && ( + this.setState({page})} + preferencesKey='application-details'> + {data => ( + this.selectNode(fullName)} + resources={data} + nodeMenu={node => + AppUtils.renderResourceMenu( + {...node, root: node}, + application, + tree, + this.appContext.apis, + this.appChanged, + () => this.getApplicationActionMenu(application, false) + ) + } + tree={tree} + /> + )} + + )) || ( + +

    No resources found

    +
    Try to change filter criteria
    +
    + )} +
    + )} +
    0} onClose={() => this.closeGroupedNodesPanel()}>
    @@ -550,10 +611,11 @@ export class ApplicationDetails extends React.Component this.selectNode(fullName)} resources={data} nodeMenu={node => - AppUtils.renderResourceMenu({...node, root: node}, application, tree, this.appContext, this.appChanged, () => + AppUtils.renderResourceMenu({...node, root: node}, application, tree, this.appContext.apis, this.appChanged, () => this.getApplicationActionMenu(application, false) ) } + tree={tree} /> )} @@ -571,7 +633,7 @@ export class ApplicationDetails extends React.Component AppUtils.showDeploy(null, this.appContext)} + hide={() => AppUtils.showDeploy(null, null, this.appContext.apis)} selectedResource={syncResourceKey} /> -1} onClose={() => this.setRollbackPanelVisible(-1)}> @@ -591,54 +653,107 @@ export class ApplicationDetails extends React.Component} this.setState({revision: null})}> - {this.state.revision && ( - - services.applications.revisionMetadata(application.metadata.name, application.metadata.namespace, this.state.revision) - }> - {metadata => ( -
    -
    -
    -
    SHA:
    -
    - + {this.state.revision && + (source.chart ? ( + + services.applications.revisionChartDetails(input.metadata.name, input.metadata.namespace, this.state.revision) + }> + {(m: ChartDetails) => ( +
    +
    +
    +
    Revision:
    +
    {this.state.revision}
    +
    +
    +
    Helm Chart:
    +
    + {source.chart}  + {m.home && ( + { + e.stopPropagation(); + window.open(m.home); + }}> + + + )} +
    + {m.description && ( +
    +
    Description:
    +
    {m.description}
    +
    + )} + {m.maintainers && m.maintainers.length > 0 && ( +
    +
    Maintainers:
    +
    {m.maintainers.join(', ')}
    +
    + )}
    -
    -
    -
    Date:
    -
    - + )} + + ) : ( + + services.applications.revisionMetadata(application.metadata.name, application.metadata.namespace, this.state.revision) + }> + {metadata => ( +
    +
    +
    +
    SHA:
    +
    + +
    -
    -
    -
    -
    Tags:
    -
    - {((metadata.tags || []).length > 0 && metadata.tags.join(', ')) || 'No tags'} +
    +
    +
    Date:
    +
    + +
    -
    -
    -
    -
    Author:
    -
    {metadata.author}
    +
    +
    +
    Tags:
    +
    + {((metadata.tags || []).length > 0 && metadata.tags.join(', ')) || 'No tags'} +
    +
    -
    -
    -
    -
    Message:
    -
    -
    {renderCommitMessage(metadata.message)}
    +
    +
    +
    Author:
    +
    {metadata.author}
    +
    +
    +
    +
    +
    Message:
    +
    +
    {renderCommitMessage(metadata.message)}
    +
    -
    - )} - + )} + + ))} + + this.setExtensionPanelVisible('')}> + {this.selectedExtension !== '' && activeExtension && activeExtension.flyout && ( + )} @@ -659,19 +774,19 @@ export class ApplicationDetails extends React.Component, + title: , action: () => this.selectNode(fullName) }, { iconClassName: 'fa fa-file-medical', - title: , + title: , action: () => this.selectNode(fullName, 0, 'diff'), disabled: app.status.sync.status === appModels.SyncStatuses.Synced }, { iconClassName: 'fa fa-sync', title: , - action: () => AppUtils.showDeploy('all', this.appContext) + action: () => AppUtils.showDeploy('all', null, this.appContext.apis) }, { iconClassName: 'fa fa-info-circle', @@ -733,7 +848,7 @@ export class ApplicationDetails extends React.Component -1) && + (filterInput.name.length === 0 || this.nodeNameMatchesWildcardFilters(node.name, filterInput.name)) && (filterInput.kind.length === 0 || filterInput.kind.indexOf(node.kind) > -1) && // include if node's root sync matches filter (syncStatuses.length === 0 || hook || (root.status && syncStatuses.indexOf(root.status) > -1)) && @@ -750,6 +865,24 @@ export class ApplicationDetails extends React.Component '^' + this.escapeRegex(pattern) + '$') + // Replace any escaped * with proper regex + .map(pattern => pattern.replace(/\\\*/g, '.*')) + // Join all filterInputs to a single regular expression + .join('|'), + 'gi' + ); + return regularExpression.test(nodeName); + } + + private escapeRegex(input: string): string { + return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + private loadAppInfo(name: string, appNamespace: string): Observable<{application: appModels.Application; tree: appModels.ApplicationTree}> { return from(services.applications.get(name, appNamespace)) .pipe( @@ -857,6 +990,10 @@ export class ApplicationDetails extends React.Component any; nodeMenu?: (node: models.ResourceNode) => React.ReactNode; -}) => ( -
    -
    -
    -
    -
    NAME
    -
    GROUP/KIND
    -
    SYNC ORDER
    -
    NAMESPACE
    -
    CREATED AT
    -
    STATUS
    + tree?: models.ApplicationTree; +}) => { + function getResNode(nodes: ResourceNode[], nodeId: string): models.ResourceNode { + for (const node of nodes) { + if (nodeKey(node) === nodeId) { + return node; + } + } + return null; + } + const parentNode = ((resources || []).length > 0 && (getResNode(tree.nodes, nodeKey(resources[0])) as ResourceNode)?.parentRefs?.[0]) || ({} as ResourceRef); + const searchParams = new URLSearchParams(window.location.search); + const view = searchParams.get('view'); + + const ParentRefDetails = () => { + return Object.keys(parentNode).length > 0 ? ( +
    +
    Parent Node Info
    +
    +
    Name:
    +
    {parentNode?.name}
    +
    +
    +
    Kind:
    +
    {parentNode?.kind}
    +
    -
    - {resources - .sort((first, second) => -createdOrNodeKey(first).localeCompare(createdOrNodeKey(second))) - .map(res => ( -
    onNodeClick(nodeKey(res))}> + ) : ( +
    + ); + }; + return ( +
    + {/* Display only when the view is set to or network */} + {(view === 'tree' || view === 'network') && ( +
    + +
    + )} +
    +
    -
    -
    - -
    -
    {ResourceLabel({kind: res.kind})}
    -
    -
    -
    - {res.name} - {res.kind === 'Application' && ( - - {ctx => ( - - - - +
    +
    NAME
    +
    GROUP/KIND
    +
    SYNC ORDER
    +
    NAMESPACE
    + {(parentNode.kind === 'Rollout' || parentNode.kind === 'Deployment') &&
    REVISION
    } +
    CREATED AT
    +
    STATUS
    +
    +
    + {resources + .sort((first, second) => -createdOrNodeKey(first).localeCompare(createdOrNodeKey(second))) + .map(res => ( +
    onNodeClick(nodeKey(res))}> +
    +
    +
    + +
    +
    {ResourceLabel({kind: res.kind})}
    +
    +
    +
    + {res.name} + {res.kind === 'Application' && ( + + {ctx => ( + + e.stopPropagation()} + title='Open application'> + + + + )} + + )} +
    +
    {[res.group, res.kind].filter(item => !!item).join('/')}
    +
    {res.syncWave || '-'}
    +
    {res.namespace}
    + {res.kind === 'ReplicaSet' && + ((getResNode(tree.nodes, nodeKey(res)) as ResourceNode).info || []) + .filter(tag => !tag.name.includes('Node')) + .slice(0, 4) + .map((tag, i) => { + return ( +
    + {tag?.value?.split(':')[1] || '-'} +
    + ); + })} + +
    + {res.createdAt && ( + + + {res.createdAt} + +  ago   {format(new Date(res.createdAt), 'MM/dd/yy')} )} - - )} -
    -
    {[res.group, res.kind].filter(item => !!item).join('/')}
    -
    {res.syncWave || '-'}
    -
    {res.namespace}
    -
    {res.createdAt}
    -
    - {res.health && ( - - {res.health.status}   - - )} - {res.status && } - {res.hook && } -
    - ( - - )}> - {() => - nodeMenu({ - name: res.name, - version: res.version, - kind: res.kind, - namespace: res.namespace, - group: res.group, - info: null, - uid: '', - resourceVersion: null, - parentRefs: [] - }) - } - +
    +
    + {res.health && ( + + {res.health.status}   + + )} + {res.status && } + {res.hook && } +
    + ( + + )}> + {nodeMenu({ + name: res.name, + version: res.version, + kind: res.kind, + namespace: res.namespace, + group: res.group, + info: null, + uid: '', + resourceVersion: null, + parentRefs: [] + })} + +
    +
    -
    -
    - ))} -
    -); + ))} +
    +
    + ); +}; diff --git a/ui/src/app/applications/components/application-fullscreen-logs/application-fullscreen-logs.tsx b/ui/src/app/applications/components/application-fullscreen-logs/application-fullscreen-logs.tsx index 4314d1e1c6a47..c7e669f46dded 100644 --- a/ui/src/app/applications/components/application-fullscreen-logs/application-fullscreen-logs.tsx +++ b/ui/src/app/applications/components/application-fullscreen-logs/application-fullscreen-logs.tsx @@ -3,12 +3,10 @@ import * as React from 'react'; import Helmet from 'react-helmet'; import {RouteComponentProps} from 'react-router-dom'; import {Query} from '../../../shared/components'; -import {Context} from '../../../shared/context'; import {PodsLogsViewer} from '../pod-logs-viewer/pod-logs-viewer'; import './application-fullscreen-logs.scss'; export const ApplicationFullscreenLogs = (props: RouteComponentProps<{name: string; appnamespace: string; container: string; namespace: string}>) => { - const appContext = React.useContext(Context); return ( {q => { @@ -16,8 +14,6 @@ export const ApplicationFullscreenLogs = (props: RouteComponentProps<{name: stri const name = q.get('name'); const group = q.get('group'); const kind = q.get('kind'); - const page = q.get('page'); - const untilTimes = (q.get('untilTimes') || '').split(',') || []; const title = `${podName || `${group}/${kind}/${name}`}:${props.match.params.container}`; return (
    @@ -32,9 +28,6 @@ export const ApplicationFullscreenLogs = (props: RouteComponentProps<{name: stri kind={kind} name={name} podName={podName} - fullscreen={true} - page={{number: parseInt(page, 10) || 0, untilTimes}} - setPage={pageData => appContext.navigation.goto('.', {page: pageData.number, untilTimes: pageData.untilTimes.join(',')}, {replace: true})} />
    ); diff --git a/ui/src/app/applications/components/application-node-info/application-node-info.scss b/ui/src/app/applications/components/application-node-info/application-node-info.scss index 4edf2235eea22..27ab11d776c17 100644 --- a/ui/src/app/applications/components/application-node-info/application-node-info.scss +++ b/ui/src/app/applications/components/application-node-info/application-node-info.scss @@ -1,4 +1,5 @@ @import 'node_modules/argo-ui/src/styles/config'; +@import 'node_modules/argo-ui/src/styles/theme'; .application-node-info { &__manifest { @@ -6,6 +7,9 @@ .tabs__content { background-color: white; + @include themify($themes){ + background-color: themed('background-2'); + } } &--raw { @@ -37,6 +41,39 @@ label { padding-right: 2em; color: $argo-color-gray-8; + @include themify($themes){ + color: themed('text-2'); + } } } -} + &__err_msg { + padding-right: 2em; + color: $argo-failed-color; + } + &__container { + display: flex; + align-items: center; + flex-direction: row; + line-height: 1.8; + border-bottom: 1px solid rgba(222, 230, 235, 0.7); + + &--name { + width: 15%; + } + &--highlight { + font-style: italic; + } + + &--hint { + text-decoration: underline; + text-decoration-style: dashed; + cursor: pointer; + &:hover { + text-decoration: none; + } + } + &:last-child { + border-bottom: none; + } + } +} \ No newline at end of file diff --git a/ui/src/app/applications/components/application-node-info/application-node-info.tsx b/ui/src/app/applications/components/application-node-info/application-node-info.tsx index 17b7a5d853bff..edd787e0240c1 100644 --- a/ui/src/app/applications/components/application-node-info/application-node-info.tsx +++ b/ui/src/app/applications/components/application-node-info/application-node-info.tsx @@ -1,6 +1,6 @@ import {Checkbox, DataLoader, Tab, Tabs} from 'argo-ui'; +import classNames from 'classnames'; import * as deepMerge from 'deepmerge'; -import * as moment from 'moment'; import * as React from 'react'; import {YamlEditor, ClipboardText} from '../../../shared/components'; @@ -9,9 +9,85 @@ import * as models from '../../../shared/models'; import {services} from '../../../shared/services'; import {ResourceTreeNode} from '../application-resource-tree/application-resource-tree'; import {ApplicationResourcesDiff} from '../application-resources-diff/application-resources-diff'; -import {ComparisonStatusIcon, formatCreationTimestamp, getPodStateReason, HealthStatusIcon} from '../utils'; - +import {ComparisonStatusIcon, formatCreationTimestamp, getPodReadinessGatesState, getPodStateReason, HealthStatusIcon} from '../utils'; import './application-node-info.scss'; +import {ReadinessGatesNotPassedWarning} from './readiness-gates-not-passed-warning'; + +const RenderContainerState = (props: {container: any}) => { + const state = (props.container.state?.waiting && 'waiting') || (props.container.state?.terminated && 'terminated') || (props.container.state?.running && 'running'); + const status = props.container.state.waiting?.reason || props.container.state.terminated?.reason || props.container.state.running?.reason; + const lastState = props.container.lastState?.terminated; + const msg = props.container.state.waiting?.message || props.container.state.terminated?.message || props.container.state.running?.message; + + return ( +
    +
    + {props.container.state?.running && ( + + + + )} + {(props.container.state.terminated && props.container.state.terminated?.exitCode !== 0) || + (lastState && lastState?.exitCode !== 0 && ( + + + + ))} + {props.container.name} +
    +
    + {state && ( + <> + Container is {state} + {status && ' because of '} + + )} + + {status && ( + + {status} + + )} + + {'.'} + {(props.container.state.terminated?.exitCode === 0 || props.container.state.terminated?.exitCode) && ( + <> + {' '} + It exited with exit code {props.container.state.terminated.exitCode}. + + )} + <> + {' '} + It is {props.container?.started ? 'started' : 'not started'} + {status === 'Completed' ? '.' : props.container?.ready ? ' and ready.' : ' and not ready.'} + +
    + {lastState && ( + <> + <> + The container last terminated with exit code {lastState?.exitCode} + + {lastState?.reason && ' because of '} + + {lastState?.reason && ( + + {lastState?.reason} + + )} + + {'.'} + + )} +
    +
    + ); +}; export const ApplicationNodeInfo = (props: { application: models.Application; @@ -45,13 +121,26 @@ export const ApplicationNodeInfo = (props: { ) }); } + if (props.live) { if (props.node.kind === 'Pod') { - const {reason, message} = getPodStateReason(props.live); + const {reason, message, netContainerStatuses} = getPodStateReason(props.live); attributes.push({title: 'STATE', value: reason}); if (message) { attributes.push({title: 'STATE DETAILS', value: message}); } + if (netContainerStatuses.length > 0) { + attributes.push({ + title: 'CONTAINER STATE', + value: ( +
    + {netContainerStatuses.map((container, i) => { + return ; + })} +
    + ) + }); + } } else if (props.node.kind === 'Service') { attributes.push({title: 'TYPE', value: props.live.spec.type}); let hostNames = ''; @@ -61,7 +150,7 @@ export const ApplicationNodeInfo = (props: { } attributes.push({title: 'HOSTNAMES', value: hostNames}); } else if (props.node.kind === 'ReplicaSet') { - attributes.push({title: 'REPLICAS', value: `${props.live.spec?.replicas || 0}/${props.live.status?.readyReplicas || 0}/${props.live.spec?.replicas || 0}`}); + attributes.push({title: 'REPLICAS', value: `${props.live.spec?.replicas || 0}/${props.live.status?.readyReplicas || 0}/${props.live.status?.replicas || 0}`}); } } @@ -102,7 +191,7 @@ export const ApplicationNodeInfo = (props: { } as any); } } - + let showLiveState = true; if (props.links) { attributes.push({ title: 'LINKS', @@ -118,35 +207,62 @@ export const ApplicationNodeInfo = (props: { services.viewPreferences.getPreferences()}> {pref => { const live = deepMerge(props.live, {}) as any; + if (Object.keys(live).length === 0) { + showLiveState = false; + } + if (live?.metadata?.managedFields && pref.appDetails.hideManagedFields) { delete live.metadata.managedFields; } return ( - <> -
    - - services.viewPreferences.updatePreferences({ - appDetails: { - ...pref.appDetails, - hideManagedFields: !pref.appDetails.hideManagedFields + + {showLiveState ? ( + +
    + + services.viewPreferences.updatePreferences({ + appDetails: { + ...pref.appDetails, + hideManagedFields: !pref.appDetails.hideManagedFields + } + }) } - }) - } - /> - -
    - - services.applications.patchResource(props.application.metadata.name, props.application.metadata.namespace, props.node, patch, patchType) - } - /> - + /> + +
    + + services.applications.patchResource( + props.application.metadata.name, + props.application.metadata.namespace, + props.node, + patch, + patchType + ) + } + /> + + ) : ( +
    + Resource not found in cluster:{' '} + {`${props?.controlled?.state?.targetState?.apiVersion}/${props?.controlled?.state?.targetState?.kind}:${props.node.name}`} +
    + {props?.controlled?.state?.normalizedLiveState?.apiVersion && ( + + Please update your resource specification to use the latest Kubernetes API resources supported by the target cluster. The + recommended syntax is{' '} + {`${props.controlled.state.normalizedLiveState.apiVersion}/${props?.controlled.state.normalizedLiveState?.kind}:${props.node.name}`} + + )} +
    + )} + ); }}
    @@ -167,8 +283,25 @@ export const ApplicationNodeInfo = (props: { }); } + const readinessGatesState = React.useMemo(() => { + // If containers are not ready then readiness gate status is not important. + if (!props.live?.status?.containerStatuses?.length) { + return null; + } + if (props.live?.status?.containerStatuses?.some((containerStatus: {ready: boolean}) => !containerStatus.ready)) { + return null; + } + + if (props.live && props.node?.kind === 'Pod') { + return getPodReadinessGatesState(props.live); + } + + return null; + }, [props.live, props.node]); + return (
    + {Boolean(readinessGatesState) && }
    {attributes.map(attr => ( diff --git a/ui/src/app/applications/components/application-node-info/readiness-gates-not-passed-warning.scss b/ui/src/app/applications/components/application-node-info/readiness-gates-not-passed-warning.scss new file mode 100644 index 0000000000000..7887918671396 --- /dev/null +++ b/ui/src/app/applications/components/application-node-info/readiness-gates-not-passed-warning.scss @@ -0,0 +1,12 @@ +@import 'node_modules/argo-ui/src/styles/config'; + +.white-box { + &__readiness-gates-alert { + padding: 20px; + border-left: 6px solid $argo-status-warning-color !important; + + ul { + margin-bottom: 0; + } + } +} diff --git a/ui/src/app/applications/components/application-node-info/readiness-gates-not-passed-warning.tsx b/ui/src/app/applications/components/application-node-info/readiness-gates-not-passed-warning.tsx new file mode 100644 index 0000000000000..31af11b1d0349 --- /dev/null +++ b/ui/src/app/applications/components/application-node-info/readiness-gates-not-passed-warning.tsx @@ -0,0 +1,44 @@ +import * as React from 'react'; +import {selectPostfix} from '../utils'; + +import './readiness-gates-not-passed-warning.scss'; + +export interface ReadinessGatesNotPassedWarningProps { + readinessGatesState: { + nonExistingConditions: string[]; + notPassedConditions: string[]; + }; +} + +export const ReadinessGatesNotPassedWarning = ({readinessGatesState}: ReadinessGatesNotPassedWarningProps) => { + if (readinessGatesState.notPassedConditions.length > 0 || readinessGatesState.nonExistingConditions.length > 0) { + return ( +
    +
    Readiness Gates Not Passing:
    +
      + {readinessGatesState.notPassedConditions.length > 0 && ( +
    • + The status of pod readiness gate{selectPostfix(readinessGatesState.notPassedConditions, '', 's')}{' '} + {readinessGatesState.notPassedConditions + .map(t => `"${t}"`) + .join(', ') + .trim()}{' '} + {selectPostfix(readinessGatesState.notPassedConditions, 'is', 'are')} False. +
    • + )} + {readinessGatesState.nonExistingConditions.length > 0 && ( +
    • + Corresponding condition{selectPostfix(readinessGatesState.nonExistingConditions, '', 's')} of pod readiness gate{' '} + {readinessGatesState.nonExistingConditions + .map(t => `"${t}"`) + .join(', ') + .trim()}{' '} + do{selectPostfix(readinessGatesState.nonExistingConditions, 'es', '')} not exist. +
    • + )} +
    +
    + ); + } + return null; +}; diff --git a/ui/src/app/applications/components/application-parameters/application-parameters.tsx b/ui/src/app/applications/components/application-parameters/application-parameters.tsx index 330e6f0362c59..38a6d151a90c2 100644 --- a/ui/src/app/applications/components/application-parameters/application-parameters.tsx +++ b/ui/src/app/applications/components/application-parameters/application-parameters.tsx @@ -1,16 +1,30 @@ import {AutocompleteField, DataLoader, FormField, FormSelect, getNestedField} from 'argo-ui'; import * as React from 'react'; import {FieldApi, FormApi, FormField as ReactFormField, Text, TextArea} from 'react-form'; - -import {ArrayInputField, CheckboxField, EditablePanel, EditablePanelItem, Expandable, TagsInputField} from '../../../shared/components'; +import {cloneDeep} from 'lodash-es'; +import { + ArrayInputField, + ArrayValueField, + CheckboxField, + EditablePanel, + EditablePanelItem, + Expandable, + MapValueField, + NameValueEditor, + StringValueField, + NameValue, + TagsInputField, + ValueEditor +} from '../../../shared/components'; import * as models from '../../../shared/models'; -import {ApplicationSourceDirectory, AuthSettings} from '../../../shared/models'; +import {ApplicationSourceDirectory, Plugin} from '../../../shared/models'; import {services} from '../../../shared/services'; import {ImageTagFieldEditor} from './kustomize'; import * as kustomize from './kustomize-image'; import {VarsInputField} from './vars-input-field'; import {concatMaps} from '../../../shared/utils'; import {getAppDefaultSource} from '../utils'; +import * as jsYaml from 'js-yaml'; const TextWithMetadataField = ReactFormField((props: {metadata: {value: string}; fieldApi: FieldApi; className: string}) => { const { @@ -112,11 +126,14 @@ export const ApplicationParameters = (props: { save?: (application: models.Application, query: {validate?: boolean}) => Promise; noReadonlyMode?: boolean; }) => { - const app = props.application; + const app = cloneDeep(props.application); const source = getAppDefaultSource(app); const [removedOverrides, setRemovedOverrides] = React.useState(new Array()); let attributes: EditablePanelItem[] = []; + const isValuesObject = source?.helm?.valuesObject; + const helmValues = isValuesObject ? jsYaml.safeDump(source.helm.valuesObject) : source?.helm?.values; + const [appParamsDeletedState, setAppParamsDeletedState] = React.useState([]); if (props.details.type === 'Kustomize' && props.details.kustomize) { attributes.push({ @@ -145,6 +162,12 @@ export const ApplicationParameters = (props: { edit: (formApi: FormApi) => }); + attributes.push({ + title: 'NAMESPACE', + view: app.spec.source.kustomize && app.spec.source.kustomize.namespace, + edit: (formApi: FormApi) => + }); + const srcImages = ((props.details && props.details.kustomize && props.details.kustomize.images) || []).map(val => kustomize.parse(val)); const images = ((source.kustomize && source.kustomize.images) || []).map(val => kustomize.parse(val)); @@ -196,16 +219,23 @@ export const ApplicationParameters = (props: { title: 'VALUES', view: source.helm && ( -
    {source.helm.values}
    +
    {helmValues}
    ), - edit: (formApi: FormApi) => ( -
    -
    -                        
    -                    
    -
    - ) + edit: (formApi: FormApi) => { + // In case source.helm.valuesObject is set, set source.helm.values to its value + if (source.helm) { + source.helm.values = helmValues; + } + + return ( +
    +
    +                            
    +                        
    +
    + ); + } }); const paramsByName = new Map(); (props.details.helm.parameters || []).forEach(param => paramsByName.set(param.name, param)); @@ -256,50 +286,170 @@ export const ApplicationParameters = (props: { } else if (props.details.type === 'Plugin') { attributes.push({ title: 'NAME', - view: source.plugin && source.plugin.name, + view:
    {ValueEditor(app.spec.source?.plugin?.name, null)}
    , edit: (formApi: FormApi) => ( - services.authService.settings()}> - {(settings: AuthSettings) => ( - p.name)}} /> + services.authService.plugins()}> + {(plugins: Plugin[]) => ( + p.name)}} /> )} ) }); attributes.push({ title: 'ENV', - view: source.plugin && (source.plugin.env || []).map(i => `${i.name}='${i.value}'`).join(' '), + view: ( +
    + {(app.spec.source?.plugin?.env || []).map(val => ( + + {NameValueEditor(val, null)} + + ))} +
    + ), edit: (formApi: FormApi) => }); - if (props.details.plugin.parametersAnnouncement) { + const parametersSet = new Set(); + if (props.details?.plugin?.parametersAnnouncement) { for (const announcement of props.details.plugin.parametersAnnouncement) { - const liveParam = app.spec.source.plugin.parameters?.find(param => param.name === announcement.name); - if (announcement.collectionType === undefined || announcement.collectionType === '' || announcement.collectionType === 'string') { - attributes.push({ - title: announcement.title ?? announcement.name, - view: liveParam?.string || announcement.string, - edit: () => liveParam?.string || announcement.string - }); - } else if (announcement.collectionType === 'array') { - attributes.push({ - title: announcement.title ?? announcement.name, - view: (liveParam?.array || announcement.array || []).join(' '), - edit: () => (liveParam?.array || announcement.array || []).join(' ') - }); - } else if (announcement.collectionType === 'map') { - const entries = concatMaps(announcement.map, liveParam?.map).entries(); - attributes.push({ - title: announcement.title ?? announcement.name, - view: Array.from(entries) - .map(([key, value]) => `${key}='${value}'`) - .join(' '), - edit: () => - Array.from(entries) - .map(([key, value]) => `${key}='${value}'`) - .join(' ') - }); - } + parametersSet.add(announcement.name); } } + if (app.spec.source?.plugin?.parameters) { + for (const appParameter of app.spec.source.plugin.parameters) { + parametersSet.add(appParameter.name); + } + } + + for (const key of appParamsDeletedState) { + parametersSet.delete(key); + } + parametersSet.forEach(name => { + const announcement = props.details.plugin.parametersAnnouncement?.find(param => param.name === name); + const liveParam = app.spec.source?.plugin?.parameters?.find(param => param.name === name); + const pluginIcon = + announcement && liveParam ? 'This parameter has been provided by plugin, but is overridden in application manifest.' : 'This parameter is provided by the plugin.'; + const isPluginPar = !!announcement; + if ((announcement?.collectionType === undefined && liveParam?.map) || announcement?.collectionType === 'map') { + let liveParamMap; + if (liveParam) { + liveParamMap = liveParam.map ?? new Map(); + } + const map = concatMaps(liveParamMap ?? announcement?.map, new Map()); + const entries = map.entries(); + const items = new Array(); + Array.from(entries).forEach(([key, value]) => items.push({name: key, value: `${value}`})); + attributes.push({ + title: announcement?.title ?? announcement?.name ?? name, + customTitle: ( + + {isPluginPar && } + {announcement?.title ?? announcement?.name ?? name} + + ), + view: ( +
    + {items.length === 0 && -- NO ITEMS --} + {items.map(val => ( + + {NameValueEditor(val)} + + ))} +
    + ), + edit: (formApi: FormApi) => ( + + ) + }); + } else if ((announcement?.collectionType === undefined && liveParam?.array) || announcement?.collectionType === 'array') { + let liveParamArray; + if (liveParam) { + liveParamArray = liveParam?.array ?? []; + } + attributes.push({ + title: announcement?.title ?? announcement?.name ?? name, + customTitle: ( + + {isPluginPar && } + {announcement?.title ?? announcement?.name ?? name} + + ), + view: ( +
    + {(liveParamArray ?? announcement?.array ?? []).length === 0 && -- NO ITEMS --} + {(liveParamArray ?? announcement?.array ?? []).map((val, index) => ( + + {ValueEditor(val, null)} + + ))} +
    + ), + edit: (formApi: FormApi) => ( + + ) + }); + } else if ( + (announcement?.collectionType === undefined && liveParam?.string) || + announcement?.collectionType === '' || + announcement?.collectionType === 'string' || + announcement?.collectionType === undefined + ) { + let liveParamString; + if (liveParam) { + liveParamString = liveParam?.string ?? ''; + } + attributes.push({ + title: announcement?.title ?? announcement?.name ?? name, + customTitle: ( + + {isPluginPar && } + {announcement?.title ?? announcement?.name ?? name} + + ), + view: ( +
    + {ValueEditor(liveParamString ?? announcement?.string, null)} +
    + ), + edit: (formApi: FormApi) => ( + + ) + }); + } + }); } else if (props.details.type === 'Directory') { const directory = source.directory || ({} as ApplicationSourceDirectory); attributes.push({ @@ -345,6 +495,7 @@ export const ApplicationParameters = (props: { props.save && (async (input: models.Application) => { const src = getAppDefaultSource(input); + function isDefined(item: any) { return item !== null && item !== undefined; } @@ -358,11 +509,33 @@ export const ApplicationParameters = (props: { if (src.kustomize && src.kustomize.images) { src.kustomize.images = src.kustomize.images.filter(isDefinedWithVersion); } + + let params = input.spec?.source?.plugin?.parameters; + if (params) { + for (const param of params) { + if (param.map && param.array) { + // @ts-ignore + param.map = param.array.reduce((acc, {name, value}) => { + // @ts-ignore + acc[name] = value; + return acc; + }, {}); + delete param.array; + } + } + + params = params.filter(param => !appParamsDeletedState.includes(param.name)); + input.spec.source.plugin.parameters = params; + } + if (input.spec.source.helm && input.spec.source.helm.valuesObject) { + input.spec.source.helm.valuesObject = jsYaml.safeLoad(input.spec.source.helm.values); // Deserialize json + input.spec.source.helm.values = ''; + } await props.save(input, {}); setRemovedOverrides(new Array()); }) } - values={app} + values={((props.details.plugin || app?.spec?.source?.plugin) && cloneDeep(app)) || app} validate={updatedApp => { const errors = {} as any; @@ -371,11 +544,23 @@ export const ApplicationParameters = (props: { errors[fieldPath] = invalid.length > 0 ? 'All fields must have name' : null; } + if (updatedApp.spec.source.helm && updatedApp.spec.source.helm.values) { + const parsedValues = jsYaml.safeLoad(updatedApp.spec.source.helm.values); + errors['spec.source.helm.values'] = typeof parsedValues === 'object' ? null : 'Values must be a map'; + } + return errors; }} + onModeSwitch={ + props.details.plugin && + (() => { + setAppParamsDeletedState([]); + }) + } title={props.details.type.toLocaleUpperCase()} items={attributes} noReadonlyMode={props.noReadonlyMode} + hasMultipleSources={app.spec.sources && app.spec.sources.length > 0} /> ); }; diff --git a/ui/src/app/applications/components/application-pod-view/pod-tooltip.tsx b/ui/src/app/applications/components/application-pod-view/pod-tooltip.tsx new file mode 100644 index 0000000000000..51303a1334b56 --- /dev/null +++ b/ui/src/app/applications/components/application-pod-view/pod-tooltip.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; +import Moment from 'react-moment'; +import {Pod} from '../../../shared/models'; +import {isYoungerThanXMinutes} from '../utils'; + +export const PodTooltip = (props: {pod: Pod}) => { + const pod = props.pod; + + return ( +
    +
    +
    {pod.metadata.name}
    +
    +
    +
    Health:
    +
    {pod.health}
    +
    + {(pod.info || []) + .filter(i => i.name !== 'Node') + .map(i => ( +
    +
    + {i.name}: +
    +
    {i.value}
    +
    + ))} + {pod.createdAt && ( +
    +
    + Created: +
    +
    + + {pod.createdAt} + + ago +
    + {isYoungerThanXMinutes(pod, 30) && ( +
    + +   + pod age less than 30min + +
    + )} +
    + )} +
    + ); +}; diff --git a/ui/src/app/applications/components/application-pod-view/pod-view.scss b/ui/src/app/applications/components/application-pod-view/pod-view.scss index 24b2b79a9f33f..372bf1b0fa6ca 100644 --- a/ui/src/app/applications/components/application-pod-view/pod-view.scss +++ b/ui/src/app/applications/components/application-pod-view/pod-view.scss @@ -246,3 +246,7 @@ $pod-age-icon-clr: #ffce25; } } } + +.tippy-content { + text-align: left; +} diff --git a/ui/src/app/applications/components/application-pod-view/pod-view.tsx b/ui/src/app/applications/components/application-pod-view/pod-view.tsx index 5d9493872c4a6..2c1bb54770abf 100644 --- a/ui/src/app/applications/components/application-pod-view/pod-view.tsx +++ b/ui/src/app/applications/components/application-pod-view/pod-view.tsx @@ -14,6 +14,7 @@ import {ResourceLabel} from '../resource-label'; import {ComparisonStatusIcon, isYoungerThanXMinutes, HealthStatusIcon, nodeKey, PodHealthIcon, deletePodAction} from '../utils'; import './pod-view.scss'; +import {PodTooltip} from './pod-tooltip'; interface PodViewProps { tree: ApplicationTree; @@ -163,29 +164,7 @@ export class PodView extends React.Component { key={pod.uid} anchor={() => ( - {pod.metadata.name} -
    Health: {pod.health}
    - {pod.createdAt && ( - - Created: - - {pod.createdAt} - - ago ({{pod.createdAt}}) -
    - {isYoungerThanXMinutes(pod, 30) && ( - -   - pod age less than 30min - - )} -
    -
    - )} -
    - } + content={} popperOptions={{ modifiers: { preventOverflow: { @@ -246,7 +225,12 @@ export class PodView extends React.Component { ), action: () => { - deletePodAction(pod, this.appContext, this.props.app.metadata.name); + deletePodAction( + pod, + this.appContext, + this.props.app.metadata.name, + this.props.app.metadata.namespace + ); } } ]} diff --git a/ui/src/app/applications/components/application-resource-tree/application-resource-tree.scss b/ui/src/app/applications/components/application-resource-tree/application-resource-tree.scss index 74ad1e53a79fc..9f3879d617732 100644 --- a/ui/src/app/applications/components/application-resource-tree/application-resource-tree.scss +++ b/ui/src/app/applications/components/application-resource-tree/application-resource-tree.scss @@ -71,7 +71,7 @@ padding-right: 1em; margin: 10px; box-shadow: 1px 1px 1px $argo-color-gray-4; - @include themify($themes) { + @include themify($themes) { background-color: themed('background-2'); color: themed('text-2'); } @@ -79,10 +79,19 @@ border: 1px solid transparent; cursor: pointer; + .theme-dark & { + box-shadow: 1px 1px 1px $argo-color-gray-7; + } + .icon { font-size: 2em; } + .icon-background + { + color: $argo-color-gray-4; + } + .fa-filter { margin-left: 8px; padding: 2px; @@ -94,7 +103,7 @@ &--orphaned { @include themify($themes) { - background-color: themed('light-argo-gray-2'); + background-color: themed('light-argo-gray-2') !important; } } @@ -110,13 +119,17 @@ font-size: 0.5em; padding: 2px; box-shadow: 1px 1px 1px $argo-color-gray-4; - @include themify($themes) { + @include themify($themes) { background-color: themed('background-2'); } margin-top: 9px; margin-left: 215px; + + .theme-dark & { + box-shadow: 1px 1px 1px $argo-color-gray-7; + } } - + &--podgroup--expansion { position: absolute; flex-shrink: 0px; @@ -126,10 +139,14 @@ box-shadow: 1px 1px 1px $argo-color-gray-4; background-color: white; margin-left: 215px; + + .theme-dark & { + box-shadow: 1px 1px 1px $argo-color-gray-7; + } } &--pod { - @include themify($themes) { + @include themify($themes) { background-color: themed('pod-cyan') !important; } } @@ -146,7 +163,7 @@ padding: $padding; transition: all 1s linear; position: absolute; - + &__pod-group { $pod-container-width: $pods-per-row * ($pod-size + (2 * $gutter)) + 4 * $gutter; $pod-container-height: $pods-per-column * ($pod-size + (2 * $gutter)) + 4 * $gutter; @@ -168,6 +185,7 @@ width: 100%; background-color: $argo-color-gray-3; border-radius: 3px; + align-items: center; padding: $gutter * 2; margin-right: -1 * $gutter; margin-bottom: -1 * $gutter; @@ -235,7 +253,7 @@ } &__stat-tooltip { text-align: left; - + i { display: inline-block; height: 1em; @@ -243,15 +261,15 @@ border-radius: 5px; } } - + &__stat-icon-app { background-color: $argo-color-teal-7; } - + &__stat-icon-neighbors { background-color: $argo-color-gray-6; } - + &__stat { &__bar { background-color: $argo-color-gray-4; @@ -262,22 +280,22 @@ margin: 0 $gutter * 2; overflow: hidden; cursor: pointer; - + &--fill { position: absolute; background-color: $argo-color-teal-7; width: 100%; bottom: 0; } - + &--neighbors { background-color: $argo-color-gray-6; } - + &:hover > &--fill { background-color: $argo-color-teal-8; } - + &:hover &--neighbors { background-color: $argo-color-gray-7; } @@ -338,8 +356,12 @@ border-radius: 33px; left: -20px; top: -8px; - border: 4px solid white; text-align: center; + + @include themify($themes) { + border: 4px solid themed('background-2'); + } + i { color: $white-color; line-height: 56px; @@ -365,6 +387,7 @@ margin-right: 1px; } + &__node-kind { font-size: 0.7em; color: $argo-color-gray-6; @@ -398,5 +421,11 @@ &__direction-right { direction: rtl; } - + &__direction-center-left { + overflow: hidden; + padding-top: 15px; + padding-left: 15px; + + } + } diff --git a/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx b/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx index 1bd6b42df814a..06ba5e331e041 100644 --- a/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx +++ b/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx @@ -3,6 +3,7 @@ import * as classNames from 'classnames'; import * as dagre from 'dagre'; import * as React from 'react'; import Moment from 'react-moment'; +import * as moment from 'moment'; import * as models from '../../../shared/models'; @@ -21,12 +22,13 @@ import { isYoungerThanXMinutes, NodeId, nodeKey, - PodHealthIcon + PodHealthIcon, + getUsrMsgKeyToDisplay } from '../utils'; import {NodeUpdateAnimation} from './node-update-animation'; import {PodGroup} from '../application-pod-view/pod-view'; -import {ArrowConnector} from './arrow-connector'; import './application-resource-tree.scss'; +import {ArrowConnector} from './arrow-connector'; function treeNodeKey(node: NodeId & {uid?: string}) { return node.uid || nodeKey(node); @@ -58,7 +60,11 @@ export interface ApplicationResourceTreeProps { appContext?: AppContext; showOrphanedResources: boolean; showCompactNodes: boolean; + userMsgs: models.UserMessages[]; + updateUsrHelpTipMsgs: (userMsgs: models.UserMessages) => void; + setShowCompactNodes: (showCompactNodes: boolean) => void; zoom: number; + podGroupCount: number; filters?: string[]; setTreeFilterGraph?: (filterGraph: any[]) => void; nameDirection: boolean; @@ -87,7 +93,6 @@ const NODE_TYPES = { groupedNodes: 'grouped_nodes', podGroup: 'pod_group' }; - // generate lots of colors with different darkness const TRAFFIC_COLORS = [0, 0.25, 0.4, 0.6] .map(darken => @@ -109,7 +114,7 @@ function getGraphSize(nodes: dagre.Node[]): {width: number; height: number} { return {width, height}; } -function groupNodes(nodes: any[], graph: dagre.graphlib.Graph) { +function groupNodes(nodes: ResourceTreeNode[], graph: dagre.graphlib.Graph) { function getNodeGroupingInfo(nodeId: string) { const node = graph.node(nodeId); return { @@ -173,22 +178,34 @@ function groupNodes(nodes: any[], graph: dagre.graphlib.Graph) { groupedNodesArr.forEach((obj: {kind: string; nodeIds: string[]; parentIds: dagre.Node[]}) => { const {nodeIds, kind, parentIds} = obj; const groupedNodeIds: string[] = []; + const podGroupIds: string[] = []; nodeIds.forEach((nodeId: string) => { const index = nodes.findIndex(node => nodeId === node.uid || nodeId === nodeKey(node)); - if (index > -1) { + const graphNode = graph.node(nodeId); + if (!graphNode?.podGroup && index > -1) { groupedNodeIds.push(nodeId); + } else { + podGroupIds.push(nodeId); } - graph.removeNode(nodeId); - }); - graph.setNode(`${parentIds[0].toString()}/child/${kind}`, { - kind, - groupedNodeIds, - height: NODE_HEIGHT, - width: NODE_WIDTH, - count: nodeIds.length, - type: NODE_TYPES.groupedNodes }); - graph.setEdge(parentIds[0].toString(), `${parentIds[0].toString()}/child/${kind}`); + const reducedNodeIds = nodeIds.reduce((acc, aNodeId) => { + if (podGroupIds.findIndex(i => i === aNodeId) < 0) { + acc.push(aNodeId); + } + return acc; + }, []); + if (groupedNodeIds.length > 1) { + groupedNodeIds.forEach(n => graph.removeNode(n)); + graph.setNode(`${parentIds[0].toString()}/child/${kind}`, { + kind, + groupedNodeIds, + height: NODE_HEIGHT, + width: NODE_WIDTH, + count: reducedNodeIds.length, + type: NODE_TYPES.groupedNodes + }); + graph.setEdge(parentIds[0].toString(), `${parentIds[0].toString()}/child/${kind}`); + } }); } } @@ -216,6 +233,14 @@ export function compareNodes(first: ResourceTreeNode, second: ResourceTreeNode) } return value.replace(/^Rev:/, ''); } + if (first.kind === 'ReplicaSet') { + return ( + orphanedToInt(first.orphaned) - orphanedToInt(second.orphaned) || + compareRevision(getRevision(second), getRevision(first)) || + nodeKey(first).localeCompare(nodeKey(second)) || + 0 + ); + } return ( orphanedToInt(first.orphaned) - orphanedToInt(second.orphaned) || nodeKey(first).localeCompare(nodeKey(second)) || @@ -271,10 +296,22 @@ function renderGroupedNodes(props: ApplicationResourceTreeProps, node: {count: n
    {ResourceLabel({kind: node.kind})}
    -
    - props.onGroupdNodeClick && props.onGroupdNodeClick(node.groupedNodeIds)}> - click to show details of {node.count} collapsed {node.kind} - +
    props.onGroupdNodeClick && props.onGroupdNodeClick(node.groupedNodeIds)} + title={`Click to see details of ${node.count} collapsed ${node.kind} and doesn't contains any active pods`}> + {node.count} {node.kind}s + + {node.kind === 'ReplicaSet' ? ( + + ) : ( + + )} +
    {indicators.map(i => ( @@ -387,21 +424,47 @@ function renderPodGroup(props: ApplicationResourceTreeProps, id: string, node: R const margin = 8; let topExtra = 0; const podGroup = node.podGroup; + const podGroupHealthy = []; + const podGroupDegraded = []; + const podGroupInProgress = []; + + for (const pod of podGroup?.pods || []) { + switch (pod.health) { + case 'Healthy': + podGroupHealthy.push(pod); + break; + case 'Degraded': + podGroupDegraded.push(pod); + break; + case 'Progressing': + podGroupInProgress.push(pod); + } + } + + const showPodGroupByStatus = props.tree.nodes.filter((rNode: ResourceTreeNode) => rNode.kind === 'Pod').length >= props.podGroupCount; + const numberOfRows = showPodGroupByStatus + ? [podGroupHealthy, podGroupDegraded, podGroupInProgress].reduce((total, podGroupByStatus) => total + (podGroupByStatus.filter(pod => pod).length > 0 ? 1 : 0), 0) + : Math.ceil(podGroup?.pods.length / 8); + if (podGroup) { - const numberOfRows = Math.ceil(podGroup.pods.length / 8); topExtra = margin + (POD_NODE_HEIGHT / 2 + 30 * numberOfRows) / 2; } + return (
    props.onNodeClick && props.onNodeClick(fullName)} className={classNames('application-resource-tree__node', { 'active': fullName === props.selectedNodeFullName, 'application-resource-tree__node--orphaned': node.orphaned })} title={describeNode(node)} - style={{left: node.x, top: node.y - topExtra, width: node.width, height: node.height}}> + style={{ + left: node.x, + top: node.y - topExtra, + width: node.width, + height: showPodGroupByStatus ? POD_NODE_HEIGHT + 20 * numberOfRows : node.height + }}> -
    +
    props.onNodeClick && props.onNodeClick(fullName)} className={`application-resource-tree__node__top-part`}>
    + })} + onClick={() => props.onGroupdNodeClick && props.onGroupdNodeClick(node.groupedNodeIds)}> {node.name} 4 && ( ( -
    - {i.name}: {i.value} -
    - ))} + content={ + <> + {(node.info || []).map(i => ( +
    + {i.name}: {i.value} +
    + ))} + + } key={node.uid}> More @@ -482,6 +550,7 @@ function renderPodGroup(props: ApplicationResourceTreeProps, id: string, node: R {props.nodeMenu && (
    (
    - {podGroup && ( -
    -
    -
    - {podGroup.pods.map(pod => ( - ( - - {pod.metadata.name} -
    Health: {pod.health}
    - {pod.createdAt && ( - - Created: - - {pod.createdAt} - - ago ({{pod.createdAt}}) - - )} -
    - } - popperOptions={{ - modifiers: { - preventOverflow: { - enabled: true - }, - hide: { - enabled: false - }, - flip: { - enabled: false - } - } - }} - key={pod.metadata.name}> -
    - {isYoungerThanXMinutes(pod, 30) && ( - - )} -
    - -
    -
    - - )} - items={[ - { - title: ( - - Info - - ), - action: () => props.onNodeClick(pod.fullName) - }, - { - title: ( - - Logs - - ), - action: () => { - props.appContext.apis.navigation.goto('.', {node: pod.fullName, tab: 'logs'}, {replace: true}); - } - }, - { - title: ( - - Delete - - ), - action: () => { - deletePodAction(pod, props.appContext, props.app.metadata.name, props.app.metadata.namespace); - } - } - ]} - /> - ))} + {[podGroupHealthy, podGroupDegraded, podGroupInProgress].map((pods, index) => { + if (pods.length > 0) { + return ( +
    + {renderPodGroupByStatus(props, node, pods, showPodGroupByStatus)}
    -
    PODS
    -
    -
    - )} + ); + } + })}
    ); } +function renderPodGroupByStatus(props: ApplicationResourceTreeProps, node: any, pods: models.Pod[], showPodGroupByStatus: boolean) { + return ( +
    + {pods.length !== 0 && showPodGroupByStatus ? ( + +
    + +
    + + +
    + ) : ( + pods.map(pod => ( + ( + + {pod.metadata.name} +
    Health: {pod.health}
    + {pod.createdAt && ( + + Created: + + {pod.createdAt} + + ago ({{pod.createdAt}}) + + )} +
    + } + popperOptions={{ + modifiers: { + preventOverflow: { + enabled: true + }, + hide: { + enabled: false + }, + flip: { + enabled: false + } + } + }} + key={pod.metadata.name}> +
    + {isYoungerThanXMinutes(pod, 30) && ( + + )} +
    + +
    +
    + + )} + items={[ + { + title: ( + + Info + + ), + action: () => props.onNodeClick(pod.fullName) + }, + { + title: ( + + Logs + + ), + action: () => { + props.appContext.apis.navigation.goto('.', {node: pod.fullName, tab: 'logs'}, {replace: true}); + } + }, + { + title: ( + + Delete + + ), + action: () => { + deletePodAction(pod, props.appContext, props.app.metadata.name, props.app.metadata.namespace); + } + } + ]} + /> + )) + )} +
    + ); +} + function expandCollapse(node: ResourceTreeNode, props: ApplicationResourceTreeProps) { const isExpanded = !props.getNodeExpansion(node.uid); node.isExpanded = isExpanded; props.setNodeExpansion(node.uid, isExpanded); } +function NodeInfoDetails({tag: tag, kind: kind}: {tag: models.InfoItem; kind: string}) { + if (kind === 'Pod') { + const val = `${tag.name}`; + if (val === 'Status Reason') { + if (`${tag.value}` !== 'ImagePullBackOff') + return ( + + {tag.value} + + ); + else { + return ( + + {tag.value} + + ); + } + } else if (val === 'Containers') { + const arr = `${tag.value}`.split('/'); + const title = `Number of containers in total: ${arr[1]} \nNumber of ready containers: ${arr[0]}`; + return ( + + {tag.value} + + ); + } else if (val === 'Restart Count') { + return ( + + {tag.value} + + ); + } else if (val === 'Revision') { + return ( + + {tag.value} + + ); + } else { + return ( + + {tag.value} + + ); + } + } else { + return ( + + {tag.value} + + ); + } +} + function renderResourceNode(props: ApplicationResourceTreeProps, id: string, node: ResourceTreeNode & dagre.Node, nodesHavingChildren: Map) { const fullName = nodeKey(node); let comparisonStatus: models.SyncStatusCode = null; @@ -665,25 +817,29 @@ function renderResourceNode(props: ApplicationResourceTreeProps, id: string, nod
    {node.createdAt || rootNode ? ( - - {node.createdAt || props.app.metadata.creationTimestamp} - + + + {node.createdAt || props.app.metadata.creationTimestamp} + + ) : null} {(node.info || []) .filter(tag => !tag.name.includes('Node')) .slice(0, 4) - .map((tag, i) => ( - - {tag.value} - - ))} + .map((tag, i) => { + return ; + })} {(node.info || []).length > 4 && ( ( -
    - {i.name}: {i.value} -
    - ))} + content={ + <> + {(node.info || []).map(i => ( +
    + {i.name}: {i.value} +
    + ))} + + } key={node.uid}> More @@ -776,6 +932,7 @@ export const ApplicationResourceTree = (props: ApplicationResourceTreeProps) => const [filters, setFilters] = React.useState(props.filters); const [filteredGraph, setFilteredGraph] = React.useState([]); const filteredNodes: any[] = []; + React.useEffect(() => { if (props.filters !== filters) { setFilters(props.filters); @@ -783,6 +940,18 @@ export const ApplicationResourceTree = (props: ApplicationResourceTreeProps) => props.setTreeFilterGraph(filteredGraph); } }, [props.filters]); + const {podGroupCount, userMsgs, updateUsrHelpTipMsgs, setShowCompactNodes} = props; + const podCount = nodes.filter(node => node.kind === 'Pod').length; + + React.useEffect(() => { + if (podCount > podGroupCount) { + const userMsg = getUsrMsgKeyToDisplay(appNode.name, 'groupNodes', userMsgs); + updateUsrHelpTipMsgs(userMsg); + if (!userMsg.display) { + setShowCompactNodes(true); + } + } + }, [podCount]); function filterGraph(app: models.Application, filteredIndicatorParent: string, graphNodesFilter: dagre.graphlib.Graph, predicate: (node: ResourceTreeNode) => boolean) { const appKey = appNodeKey(app); diff --git a/ui/src/app/applications/components/application-resources-diff/application-resources-diff.scss b/ui/src/app/applications/components/application-resources-diff/application-resources-diff.scss index f8208a36653a3..fb139f273a24c 100644 --- a/ui/src/app/applications/components/application-resources-diff/application-resources-diff.scss +++ b/ui/src/app/applications/components/application-resources-diff/application-resources-diff.scss @@ -6,7 +6,8 @@ text-align: right; label { padding-right: 2em; - @include themify($themes) { + color: $argo-color-gray-8; + @include themify($themes){ color: themed('text-2'); } } @@ -30,4 +31,8 @@ .custom-diff-hunk { color: $argo-color-gray-6; + border-bottom: 1px dashed; + @include themify($themes){ + border-bottom: 1px dashed themed('text-2'); + } } \ No newline at end of file diff --git a/ui/src/app/applications/components/application-status-panel/application-status-panel.scss b/ui/src/app/applications/components/application-status-panel/application-status-panel.scss index 5842209f6e959..e96c29624d5d1 100644 --- a/ui/src/app/applications/components/application-status-panel/application-status-panel.scss +++ b/ui/src/app/applications/components/application-status-panel/application-status-panel.scss @@ -26,14 +26,15 @@ .application-status-panel { font-size: 0.875em; - @include themify($themes) { + @include themify($themes) { background-color: themed('background-2'); color: themed('text-1'); } - box-shadow: 1px 2px 3px rgba(0, 0, 0, 0.1); + box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1); flex-wrap: nowrap; flex-shrink: 1; overflow-x: auto; + padding-bottom: 0.5em; $row-width: 250px; @@ -52,38 +53,44 @@ &__more-button { margin-left: auto; - font-size: 12px; + font-size: 14px; line-height: 20px; - padding: 0; + padding: 0 5px; + cursor: pointer; + color: $argo-color-gray-6; } &__item { - height: 105px; margin-top: 5px; padding: 5px 20px; font-size: 0.8em; line-height: 1.2; - @include themify($themes) { - color: themed('text-1'); + @include themify($themes) { + color: themed('text-1'); } display: flex; flex-direction: column; + justify-content: flex-start; flex-shrink: 0; flex-grow: 0; &__row { display: flex; max-width: $row-width; + margin-bottom: 0.25em; flex-shrink: 1; div:first-child { + width: 80px; margin-right: 10px; + text-align: right; } div:last-child { font-weight: 500; - margin-left: auto; + width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + text-align: left; } @include responsive-widths(); @@ -94,7 +101,9 @@ } &:not(:first-child) { - border-left: 1px solid $argo-color-gray-3; + @include themify($themes) { + border-left: 1px solid themed('border'); + } } & { @@ -115,7 +124,7 @@ label { display: block; - @include themify($themes) { + @include themify($themes) { color: themed('text-1'); } font-size: 13px; @@ -129,12 +138,14 @@ &__item-value { display: flex; align-items: center; + margin-bottom: 0.5em; + font-weight: 500; .fa { font-size: 1em; } font-size: 2em; - @include themify($themes) { + @include themify($themes) { color: themed('text-1'); } &--highlight { @@ -161,15 +172,12 @@ &__revision { font-size: 14px; - @include themify($themes) { + @include themify($themes) { color: themed('text-1'); } font-weight: 500; - margin-left: auto; - padding-left: 5px; - a { - color: $argo-color-teal-6; - } + padding-left: 8px; + margin-bottom: 2px; } } diff --git a/ui/src/app/applications/components/application-status-panel/application-status-panel.tsx b/ui/src/app/applications/components/application-status-panel/application-status-panel.tsx index 443a841f5785f..7c2b65cd3ce27 100644 --- a/ui/src/app/applications/components/application-status-panel/application-status-panel.tsx +++ b/ui/src/app/applications/components/application-status-panel/application-status-panel.tsx @@ -1,20 +1,22 @@ import {HelpIcon} from 'argo-ui'; import * as React from 'react'; -import {DataLoader} from '../../../shared/components'; +import {ARGO_GRAY6_COLOR, DataLoader} from '../../../shared/components'; import {Revision} from '../../../shared/components/revision'; import {Timestamp} from '../../../shared/components/timestamp'; import * as models from '../../../shared/models'; import {services} from '../../../shared/services'; import {ApplicationSyncWindowStatusIcon, ComparisonStatusIcon, getAppDefaultSource, getAppOperationState} from '../utils'; -import {getConditionCategory, HealthStatusIcon, OperationState, syncStatusMessage} from '../utils'; +import {getConditionCategory, HealthStatusIcon, OperationState, syncStatusMessage, helpTip} from '../utils'; import {RevisionMetadataPanel} from './revision-metadata-panel'; import './application-status-panel.scss'; interface Props { application: models.Application; + showDiff?: () => any; showOperation?: () => any; showConditions?: () => any; + showExtension?: (id: string) => any; showMetadataInfo?: (revision: string) => any; } @@ -24,26 +26,27 @@ interface SectionInfo { } const sectionLabel = (info: SectionInfo) => ( -

    -_OMk^EK@dWp7%n|ewU0@&>cg9Qw^Te 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%0Aai7Em(;>A2XFTkGi7W@a_E+QQ%C?v7{%T$0s?F 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⋘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 literal 0 HcmV?d00001 diff --git a/docs/assets/zitadel-application-3.png b/docs/assets/zitadel-application-3.png new file mode 100644 index 0000000000000000000000000000000000000000..7650217264cbcb710cf421a036eab18f2f3af62e GIT binary patch literal 50472 zcmeFZc{E%5`!BA8_LQRMqpGc!6BT^Kn1KKKznrljg z8j>I)MN3P~A|zs{8Y5ziA(Gs!=X3Apd)NK@l`OdH!Vi2t;&;a;kdIHeb??2e-M92HAKz!f-M?;HhB+?M_#2N;bK6&>-xxhR zE1OsPBvC-d+1Y+0Q|6Bu)e3);(VoE(rtX^dG??UD)du|dua^z=&n@wPZ@;<_1N!&o-_M-I z{=NC=kB56h1zu0y{bsfApUtxu|KB{c%pSd?Fm$kI-`Pu+Bel;#74{=?mgSye9o8QM zreoCA9zj;S2T@j))gXCm%Ib6{qf(D2F!s+y(+@PuJ$Y}Au?3-QF-Wv!*cLg!uIf<{ z+L6#PBm)}#nksMI-H~4DoGJ&Z?EuNyeR(bh9jSK1de2uoAN^&w`W9d1yGGB~XxnLQd~*(c^BNKl%K+rH`Cm zYGH@yKj#t#ZXG?*hv!rsR;A!%YIyH<{;GurQv26c-kATo?Y!|uum{^GErb!(l$Jbt z*JR4m9$s9IvO*&r5GJ8n%Ofzg&2;7>&PEj4{GAEi`RUKHC6+9HN@5>$Es$xDLcEk8 zU|x)T%x2W!BtkmwW-}6aiB;dmld7jQB`4!k(@+ggp2{U~uPFl|+z@Q(#8KLnvO+mb znUM+UM}B~1IMV)~!F=nScMh&pgAHf!H2Z8-#=CtM!*5gkKuoVQiLjyhOCw{@&7)(o zFkxLkJs1xi9WwuA_940pLisz{(3Tjj@4Za-9T^Q5^d`coBQ<90Rcqb43R{28gK^dn zHtWUjEL&cQgW0r1rUHEs+ogtnBqnG1(PWLQa3r@;?=$DOW1y1m6BW44j#T*>h1TNc zUZIo-p0y!JMpw=(RsNSbWx_Qi%cIm-f_QYObe1Zlf>i0G?pC*;4@y*qvF!01No?r% z*{Rsj&J+Bt>h@F`mKp+KKmO@4)HG!t(jq%c*%o(#^0;Q9kb%kj`1v82_naM*@}lyn zfG()vv?|hM%0X*9IU%C&ic_=c}V|7a?E^}dgbqpa&t(CFE)i6+$4$U z*I=!bxW?p5|$;!qN{r42BY zL%BX)4GZ1?!=}0>)%^S4PGqky5$Wz(oHcb8g6!Z^*|horR&lIkTrl~N>pKU(u?*Jq zER)*kL=1#txLX{L5LjhSg@UzNv{x?77b82K7&0}N|Ix@qK8#6J{S?8f`;N3M`*G`X zV&S$!An8K$Nq2RcU#iLO!oX)0uKJ`aJ)}>g8UIc8;pvs7vstPf2yU?^YNx|7oclZO zUQWz40%Ai=0)6Hj*|#&8&smvmm6e$vk2sGmk#|v;IVFyP0?w$JBzCCv>$ev z46(k=pa@G-dK9u|P+nllB+Ex>wR13&o_U zCP~?y$jk_L8W(=kzi?ltIP}!MuHEd#FaZ(Xd+?$W!sQXm~ z=!fRD6lCaV|G-7F46vPw=e(2%3r-0c&5wgsMiCTf#4pVW`BA#yH~L*ckB!^cUuM*d zdL-+((BNiN#tuZOCKYAHum9X&8oBzQM=g))kwD(e$uej{)*jnNSsVAjyG$RddxKN7=F ztpBu*&8jdNT!1Z)*)4vaO0L>o6R-0hi&|T8@M5Y~K^2zYxbU`bL;m9|wOzvREXpf&MzS8Sb}C6@G-5mge#NRu zGZ;K8gl~q=kec&Zk4dr@?|zY?`U=%5^-KvgC7yWI%G=UY+(Kl7iOnkb~l8 zXq(Ym-kY?LyWiOG9v++>+_!AUgC#0Buo8qKp`~y(@h&!ape>vZ&&POI z!LJPMfbt{x?Xq#n$J1KA0^x(IMEw3+{JRbK;;GG(sy~EqIRfb=W>UVFLNqS^{!sp{ zFz;C24ZCyL%GakkuWyXOd9Dx~?{%|$O^r0>vnj1LVNUmk z2%bO=4sY*QVb4&RvFC0V27tEr@5PrVZ$R!bAag$?A1L~*=E;6n{Ue|vo0?*)dLuVG zR(me1Q>i8snrQ0+a&lWBM7O$4CtpCOh9{Lip6lXo` z70%->PpN4AoJdlvOM&>*btP$VgT(*vdT-Iw*lSYgp16SZO(2Y3-yG$Y!gYOO1dTnq zGhM~)`&$*87Hwf`j7z9IZ&dcx&hG0aS$4uYii97B*qqJVL-hl{W1%%@T2RYi z(!9;AgH@i+h?EX&o$w1~=yR7sXN}Z;s(QA6NM@Ttv*I_@c<=$?e2-_jTrdEroE+ zozheHiRB%7r{FzooRi0SjnknrX>DE1`B}%R{MNBxjDWa;<6?1Pf=F}9_U5TeHnBS! zix~3wz3+DP)#bvThKu~i9~*XVR9$lVctBXAI}tYMUs9i$op^evlRCye>{{+5x1$Of zI#~I`!a|6NSbip`Aoeicw`$YGvvIlq280sX)42VP{rgQ5&(a}J+>#CuG&C>y%v#TB zgp8(J;a!aBWW+~UkGkwx5xq=3Xk0;N`d^S7IZNn$qR*I0h~L`%iOMSu&Db=36D`lJ z3+bCjbzhqe3`Y6elWU$^O1Wg3zI2fIwcZ?mkRf38dTg$zZ3I{!{?*tKI>BYvsp$|o zJ_SOjwJ)CpE6!yslW2 zW<1L2oplt1Y}Z-%Dk5cd8Vx3O#pKh|tqMz_&*VN>J}g@f>!F33*itRmW8=UOr|=Pe z#gib06XV5c#hO)?KYyO>MFxHc7exS0c|}j}nHnjt`0(-W8FU7WQBnv^d7c)WD-5eY zTUU1rWL|$_>gO*L-w<$*!~+`8daM)a1X8y!^XbTazfwxg$iak$;K-FX7p$rt#r=gZ$xs1Xjw3;6 zFoLs@4kV{8S5!1GxE&xbv^V_AHo5sg&RvNLw}SgNlL6J?yz3j6$lm&2jKs)Cas#t7 zpUXJi!=X@KPcKrLUIWD@I4n9;B>sd-;EXqYaQdkD%LQ}dH^qwLK$p@sY-Yx?s-vL( zIXt*+;hj0uj=_}NgfPJ!NZHter`>YasY<`>B4gur@gc}>2SG+NN*8t{n0_KUs9jb7U$l|V5R$OFAhU$3+ z&UTBmJ;BNO<^oyBvgBpicaifWP0grX^1>0Pe2>Ae=DD*3>u*VVAyBQdvf6PxMt0!6 zjzJm@^_v8 zr-3>6j1xa)YseOIhie zfBZJS#FR``XCi^}DK{afR!vekkWoBFoxN&@k;D7lVZ2ZS4}Zd3MSYT&3|0dXB7)g# zJ=5Wx_@(MUm0jDsL$!z$WwaeG7bv9#(^1XB!?PkEqtj*>6M z#(QoGQRP6$;TepYNc_w|1Zw7IM`Jj+vHGv<&@!FL7K&GU8n$aFQB<`6q`WooI(LK- z%JR=IqPzvMQ8mQ~S4j#EljT$2t|`gs`MkAY-$A|(N_+%&Y)#gg@xyNq!R$ZDW~N~a zH9J;wp-pgQ{7yqm_WfY|kBF?)P^nc6niI6X-Cz8lRWWs~8H~J7qV~3s(%B**lT#I1 zOlLFQfMog=*#(xP4ue5<>RzzgHg|1n@T+xu!LUE9>$20xJPAe_2sxZBoDn--`@E$^ z($>dUtRp?NQhD2Z@gP~atax7Q^Xg!Z7gEiHbR?st*-H&R^f!NBZT-Q%6Y(ebZ~jRcbE>cg{o&H)BWKLH2*6?dEIpLp(?ZXw8+K z*>i`*>P%p{EGUh^!!Opxl1k0HFmz4TS}E9K6N-Kr^|575cenR|;CJ@ViuMsJJ5@i- zAMKH9x+iVbc2liQ#pJDO1;hlbAs_+|m4fk0G{ZzM$VVWRWrWsnyYj=EaF6{*4z^~qjj>95@@C~p*d>ayF(fl zS$tn(>YuGNA2M}s@Yx`$QYa92OFn3c_rul4R{xMLgegp)Ee^lY&SHH@A0IA=djYM) z!s;&WlY-4DvbPA$L-`JN(UM{JEGXF8J|sGeiK^803LW<^Os#yS-OHKHKRj}Y zDdImcR42YW8)Ub(InDx)?Y6pjrOKu5<=ufm0Up=afU(=RD5xazQp{OrW!-ow5P5gE zS*!BmjLt++JDQxRV}HGb$|IQ%qj?0itHe%4U9#$zW5qM4tSW4$J8n^@*38BOT*Bqd zbl0l0xK)PIj?QD^??W3PDVrIyY!AtQ&AxF1);i3>2qy5wVkuzsUnbA{IUV$IXJlAu z5jd*;^xLvqpq>QO>CL@#0?Mlcd1{zPFPTc{nbT2}8KhBp>~gI|C2Rxn6a2a^)&%;3abSHa1Cdz_kaVtxarcaAl@NGoyfKqvLucoH;|`Ob$lCX zQ5{+CgS1V(ACyw>!H-SBx~$)*`CnKeWH@{+4f42uc@5ZURGR?|fyH7oUo6g0muj?@ zBCeb~pSyoTeLQO1mVvE7?W0Drp^VN-VjwPrZAYjnTAdHs-e7g%#;K-SwSM%X<*3E2 z%0-~)4xks!2u5xBXJR~?&~{h~1~L*HPT!?%ro@!}lvr(}vUYky7nwwI-Lk!^AHwNf zgT>`8yH4#%lyAA)Sgk4(uC>UZny0E{cCBzd`Wv)@y_zP7!|Ib>hqr%U3*25BQMM7Gk z!SdD>L6U*!hgJPnm5!hfIjic&r#n(>R;T4378!}515!IGHW9&=x3m8Fk#gySD87Is zO#_sDeKn}`VV~UFO6TbgeqZm;Zxt$S2_06lL5@wM+?^CPuV1*(!`%M@L;}dzuQB|0 zfG7W(qOSiJE-UbR6nham9(A=PDoge1lH3RuUn<))PB!J z?4)rvcjVD7u{+Hr0Vy4zX_T8)IE>;{Y$k>4<~}$R^nMdIX|B%US0;wKsX=zgYLhja z9e-uSC7xc1x8ss`^q|1h5R6X~PPG_6%=Dx7q)&}HHPtYQyPtPPX`PbPJ?DPXa_(W5 z%bNJE`#8t;4(<+J3AU1I4gK*!wpq)q-6i3SbMS)qWDdE)p_3av7rhi-q4^{Cu+17H zYvdwO-~vni3&{Y#_KEB_p`cAchrHrrG8wI0|0kfb2VcNY$AMIc*J0}nPrdBx^9lRg zmy#_I)g%-T3(bN;#hsRY*?wE7kmhiYL%A{y_uNjT?^!3nDm*>tRBpfnk6)0M^kIf? zmQO>O32)mP03HwTAp=@0B~9LC*kne6cNRM9VD1$yZryoC$z4~6uxF7w&F;%jJjx-n zuH^ofw%qJuNn^oYu{qNu+ znir?1oID)PytD~pu)xqPU&r%d7;fX%DvGtlmL3^L@SuB36Hvlm2rD_uuPC|6oXAP$ zdN!I*Nzg_ zEt@h_V~E?2x%r~tW^9C4=VirIREPC+`Ksc#YeW8d(q@(ydF-fa$+7vzR97bAA)b?5 zD{0GsBxr(NnNo|QP&Iz?sAgTG`4 zJem#mF$XCOS(q&>g1h$s5~=Xrk@tdq)&9=y!b2Z#9rb-5#$$q z^;0v3lxR;=fY?nN)th%3@9U61aEJ=fKu~(I-NH#|o^Ly<0!@?f?d?j(c&UURucX`cmm1 z2?cKT-xmhUpa2)-harnAIi5b+b&p<78UJcsV<1GWH4ogz+kE^ASo?qF6BK#TCZW(6}ta!j(f>S<%B62a}$gO#(C zi-TJce^K2nPhOAo)XTcMVZ7O-dDGlOCRuwJmTmfYH)znHKzEZ%J>cLWd%9TILgjL5 zf>by~S3;|~BVEZiVYjeQa=QmnH@Wb2=4rPfo|+X*UvM>Rnuv#G&SdF3-U|vUOvr&e ztloB8xMv_4d%LqaeI=pwuuawwek9L2r`kpjJ{@1yS~6g!bMB|4_995u%y~#@0Px7sr8Xdn9Yee+xPpc z@k{GACi(*%R(0PC-UO?Bm?r6pDjL^f?Z zkt-Yb*~xO^<|TI*wC_?UJ}b7@x#WN~Hg|kQ*x<#(&sO`15pd(NWly`NCgoyju6kP% zPz?EG)Ottd_FoNTW~+ zH0wNz^9*Tlpt7x<~JJ*I2Dg!0kwwldZsQ=ZD*-L8zAH6s;vc*i{IaAP!lg%J4L2Xa!x8Y3aTfs+fHoiMm`bg3P&6KEB)2#3*-j8Cfs1`D7yY5KyNqRrJ z$xM5rpeu{`2~)MjR5lA~?a`oHhT?aBd@*JoqWf zsje4p#aq}wtj3Eh@q+pt`{b>Wk7-KmWJqE4gJ~nR@Q&p0_JM!l5(m%RX4+MXv|ZM< zRZF!=%p{3U;y-I}s}*XD6}7dP+ADQ}tLoI+=UR@nPL^d4MuP9Lwpv5Q%DS8wqGs9w zX3=gr%Aq-=3PPOxrj}2=t{Zx^YO1OBwxBqkQqnFB`;d70>R`7PyY{V1ImL$KzYs(| zRQ^ z^!xkbLadp*W$CNyrYgDQ1mRG6;SI~?fa)9Tf42&UL^rU$sgKKsy&$SDy{PnNy{})Y zr}zztoTsE0=sgV|aTK*oqRd`Pbq!*twf*CvMiP4-Iyv=ROrEixSNN~sH-QuzalF!U zgQ}&DhcQ6xF6JRZs-i1}Fd&pQet9yJ?t3?=BCwq%(y(Ibx?ZkqT5Y0^estgvVUqH5 zK!ud?-Ml?}20SuvT;(&69QAt4b<@G&K4to6;HZZR4PX+kxU%0Pzi9kS#L*OZP?onF zkH9su9P+l{H(+;SeO5^sLj#1>(cjJ;wND}HKOQQo$l+iZ$9h#?|5B$42r#Sir*;R zF{nbYSyhKPzMT{=6}5N@m+a+$p{dI{jm>oBGBvx+4{I*m@z^8?WAlBR=tEBVIlljz z92Kneb3^&v*FAIfS`|Wyx^X#cd0O2kOwKx(_=yoKaLSiXYfnwpMIkn9TS02Tbaz>P zhN2A}1HkYQDEtY0_nRl~{o&wI^{V$@+?r#?q0>l^j}~f8_8AO_f&&{lf(5h*wa@ns~us&ebx`ElG2o-8p4e0P&_Q z)i8<~o6OiwKhyxXr4Lqr2??Qgkw-pM8d9j0L~ovEk#pB_j{4ZrklAGJoD;D-;mn9? zUti{}F9+7JSrMO!tav*IEi{E^S)AEct(P`3nIKu$y>1~H3YRiMj9w@_8}Rv3p*IsQ z5;|a>G0K||_0bg2Hq)wqA(&5kJ>HRuT4gn(Y03X`(X&t^A!gz6fgGoqaH=tekB|0Rfcz15U9+c{N2Wjgf z?ihhil)z!VYY~lWOTXbRQ&pgk45iKMl4^qJgrU1G@Ar=_N0n?WY$I^gVD-@xN3Uve z#>J_ocqZVKD=K5ln|c8QrH%w7qqjPI*>qBNeX&=OHw2Dbkt@7HV5!EdI;#9hQj1#u zHp@t(G6my*D$RasZXFFkTFpzLhlb=7gpqL#kG2JH$!R^*vDRMLncHH4ttv({1;$`)W7`!TcI#Iv30)gwIxGIhfqxeELHmo%Fex z5XvQwNe(HQOQ=c6W$6o#jd!Gve?BA>H2|f4G|UYf(`D}piZ3yU%mz$biezZ$crLfj z*u5ovoDxT?s5+H@jog521Nh&pA+-J0^+m>}J=B7Kmv>!;XL!NPL_)?7gbwALeVv4z z9pTlFrs7#%=Jy-dOcflI4@fsDe-+kSJgcrPu{}NP57n4SwR;$)krX;z=j5ni(1X(y zcX);4PQoA20?k);`Svtrd_~dDstIwvUP5}7#wPo~*A_~DGD-BIgLVXPD%;vANG$WQ zF{Wz$6sb4@^3|C2jjb=BZMxY1O>N{uqu^)kc!l%FLc5mS2-frjNEd5f;cBmj2tNDEP@*hyxxrsTtAJwe)%g^Q5vBM@MVNp;bC&{gQCo5d%Wl6+6>*D z$F=xWHH%ADFHR_Qvk}b<)JF!rk~f=!S}XLNsD1QBz&ghqOa)Eb0qif;JqwV}goV)D z3~_p0-Bjsu_C$)aXC%t1A|SdRTq}1^PA)D-RlU*$!QD8AT|fSB^B`V znc~?&5qSX{kQ8jGDhpzImP$#)<0vBwJ~r(-%sM9+`zIl0y0qO?3+u;3xqDc;B;g;Y zaaIhY{YD4E9i}S+Y1ppJhPC%C9zrj$^{81*1Itba%su5kk>8N@cwTcm1umjJ-;|%J(GZ6cf+xFg@7=nOy2r|AuE0lFIG1?o!%~#` z-e|Wo$T-aQjOu70Dbr&47~Go(C@0FxvOgK6qU!u#ya4NQR_RAKBX`!F7RL@t1^4J+ z3?HJbOg8PKZ_6RL8|iFo`oBuBKK>7=NGcSc2;&l;rI3%D1EIoNthaGqsJ3F?>T`Br zh`GzN{pk^QKBtbFm_e73-j=aw$75-qF}Hi!jTF~GboLn?c<7A%*Wf8 zcvDW4Q6^sMtHwCg9GO=0$M}%S(lqy39KJJxHC!m$-cK5Wl2~W)i%4D|1DHX_WqsAs^m;p7D90y9R)kgIh5Kk~>5VKu>djD7DAT7Hq&T z?^Ss>u)kTo?jH?bV^k8Y>SmET*xRvbveIA=->-DvdVk}X{U3wQwKyRZL~^Dt^8X>w z{+EgNmg8`%2YvyDTK3yDlbP zEVYxIr1)}WpNqdmSC5$dn(u|4U;s@C9AnA&z7SXNKL@)jg%D5|11C#vW9j^kH@QCe z#X0P4|62dDV`k6>De}fW^5b|-U8Dm(jlRw4I;V7^A?1(MR#7wJ1m#ck!?FSrI*TT< zf8nDyXe=r(@GF11eRl0p&eRkABthL&8^D#lx25flmm-fGB+2bD3=Mf8OsLbTSM^x1 z9P&%VEJ1|Ya{C3%-&$)c`cKi+sRhvXbQt$bM(5R0Z)43b{)&6Hda@DiEGAoasKEDd zt=B-?(zyL9ATL|o9AAT+FBUwchJNFe;yc;$Mb=kP=`J|EH`KQ1?D^4h{q_7!u@9vN z9`DtZG2Be5MB%H8rVRYO<3GI!wNb(JpNA>tfPN;>Z3gnUd&a)h--6$LFfIMdB@?WY zKDbhXy&=66h!?~JLpXNK$O-UM3A8vfDR(jE$Yww=ilT*Z_>4E#icgaMP6TI#E{hNg z27_=KC(sZK?ICh4gfS25H~9N5*kjV0HT_x{m>^}(I+yAKrZ1|PcslCuurT?}+? zhc5#9P}7l9gFg7Bw>X-b$>3sF8(TW+N2XXGdWItMqR`66B^fV+E}5!F#F8A8dAqs|Uu=W*jEN8^jd723Tu7ds5L z9)1tW`ZZ@2&pwPRR2tSgM5sa~`19N%ze@PxmDmg6#97t!2BiTPp6=9R_MZNYg2MCb z8fh8u6{;9yDwr;Q+10vCtq`{hrDgln`-|^7Zm_J1({L5Mk9adYZH{ zr&YDX*o<2mmmx(oRn3a|AmFaOYtNuRvx9f;4s8#gx365^W-}Tji{}lGb{W&BsFqgH zIa~r_F7uctq1}fyLBKT#6SY&#fNC@p`rREi>Hyog;FXMjZ0k)#X9ql(od88;jKkX# z6@OZGd2J*~qwCREEIQGQ0^u*K5EYLcCqRCOm9lnxZ=KXrfAM5X(0eZXyT#~L_3ebR zyIRdgAtt@m^Jk_zR{*9okUrriXPEt>7T!9fBQDk?s=YUdWS|F88uu8I3xZ%|J3#0j z1JK*?$Tfn3P4ytWxha2knnh0*^&SS;)(R@GhfqmnFzb2d)+u|5ASEx#jEZx5`EgIr zJfG5Duhh3t-`JIdetzNAGh+f*kFzL`^&XMNhEjDHiPpJ+41X5Bp$dI&0Mu zWD9(12dB@6mVs(#p(4y1k5YFr}yY-R(lGJn2;GmR;k(3PaAQ+O!;7imBIG z`z!ae+SX!BlR~SBtzC6 zV8$8J0=5|;vrzRqX`@=&;O`b;r)_;yp?Ryj6O3pz4c}dBr|_0No#ZB0GE~R2QaDPP zsqAhj>3_$u{$&syyrV+H3lhO=;|OvWYicvM ze#(iy>^;_?LM?vAxgOkr!kiHfRCWLmiP-qYR2f6GF?Rahy!~-n@tz;PqVJV($ed3c zSN5Y)P}#?oKA4sETa1zGS)Z(kS|CuljfwIMerMV#!KM@S~z*hN{EIe}hQu;X!eV z1Rr9LV%-PBD^@r66jg>&h#{-XPLn>e4G`E@jy0D!B2;3b$A&=cx zUACNBeXFeGr}_iXA!Ki^V@7Ib8!|&rd(?EP)!fvNAL+~Hxmm2GDQDO*=UfZhewx5Y za^ftRNEi?ny}x@*#a?~0CMmPgWfzK}K7_1w(fkVmK*6nF)|aTzt#&)boLwWgxm<X0g;b_81@g;fXt{TH0$7eGlwM<`9X>?XvQtI!y zF9WC*A9W?zagxEK#8GaM+9S>?GOavj3z18vp)IcMR86fToV-1jTom5R ziO7s@r-fu)FLUOLgfQ7_-FDJ-NsG1J0v>v&wlmQZe5}rDkUt4Sx10$z7l0oI3dBQAtay5uF`HOiI5i0*=X>gWv;L5H z3?Xuu!}Be$`LqaChx;||!E|_!eu!`GBDj6#%}q{D zbrKsBI$IFJ7B7zz+4E|*Q>s8$&sXzHLAn&|Xf+lnIqtz+hbc4n_V)78rJLpW_%!ZF z;xW(~JlYmRi6^SJ0HO!mW#PU3XT^_|@8^3JyH{mm5Gbri!cq98lLC9YzKYdW{n`Mw zubR95w{9q^$^n>CL#^3zi*onG)6K6IFWWW_OCc6+OK`{jcK)Y12areql}I`hz{3~Sw^$5&2r`g$iM1Kn2F-mVO7Y`Mmqw4nQGZk3ej@#j|l&l5~F@qXrGgHKP{A+8b;7%55xA z_C?hSj*Xvnxk2}?9N#-3&;W-TmI?M8AKGKS_LRx+-FZ*-^6^jSh90wZ9C|Yory6pm z_m3DWGEW_q)hSYCQX@92c1%L5LfF!GmH%mA;yZT`C80sOWeNSfo0$w5RhVhu(8iON ze3Pw(%xJ<=O>Vj*#z+I)eqecOA#f+sa?8qBXLoCo%H-7M%)vq*aUtB++h1A zbd3EGT4Q1%K6pD&DK0#8EF@Maild(TvlyJ(bj|tiES^_MP!#}*yxSbXtVK-DCk!1CD>O*j8J+2InwZf6BnkX2ZI5d& zi)_}@_~FRD%!=Z>GI43C!(0V^&wS6d)Ii&619J|&}rvFe^X=(9h|FL3YOd1ExQzq1q9KThOF~T_djwnC|n(K z=tO(%yt1YbZLWCGu&g9K)*W0Irkh(M5Ce2RFmew3vd7QUF`&mPu~2-czbF_2Q>jL;0(W$^}O-|Y$JHP z>-R8$UZBZjHtlwF(8dBOCiHmEprNMuLvfh`e;0P5CISHMdBveJ zDe&n$rt5Z>w#-Xb7hj~V&n-$fa?k)dQ+T>%lFw?*^4$)g=KM1=7+^Ev0S0naO%|pF#D*m*w2LuX?Jt_M6obXZJL5CT0+56QZ6w92$hN9w40o z!D_+kAQQExxT;&h{0jp8@B!hGepY`-p$X{Mjk<4jti0giHgybCXnDK^L&alYIYxCH zb7_j~Q%m-TvZua?&1_HEzbs3vSB-ehOPGiZkUBJ7RPt-o?BLI|I}n^IV0aG;U@1v;C&2UP$V;Sg{YS zwo34t;UPw;+#Lr%$j`ubCV6G;>#gn4n++oSc-M8=t#n;^3ds$@&n=c`?(b#2@k_8n zo*Vsm6TWF*8x@uHPj76w@7^6+I99`Q=NLW^Sr_pyvB&!$A3XHxlkE~ybjeV{`s5eQP% z+vu+BIl6$uN)DAycUnK>rAzfV@f!UtNE1gBT2JHJ#l-6;t|`LZCkwv#IA$jKe6db& zUAQA>gEkMADY_`El2qxBbTiLGCKXx2RK-Yltnf7gFI1Iy_dlY%O4npbiw;-TVCs=T zgNZ$3mSAOlZ{_J!_`D5@e4@ai{ufYBjr%#51ZPss=chP%7^GU zZi!l()WzY|u9Kcw+SsxlANLyzrwUbsAa9xcy2pIpb){9A`Z-}c7sOw+74>Ly2Vc19 zzWet|&>;XWe?-V{1JbO$SBKnxX|4WMckKQ5|J8=;{|nN;e?08}x4{1g7Wkj-@zLep z4+uFRZwf_j5b8Cp?)!KeX&Kr|-o<>jQhM)#nlY_S9cUjJH=e1R$7{b^ZvS+P~~C z@$pFkiZdC>IIZy}(@(bd0DL~xh`QvFwG?bSCM8<4@wY01nwzWRD<8Fc9a!#%kNIcw z@rBuo^5q2`pO0j33u1pHB**NNDPPU9LIt`mpSgov?KT`PMw@kL-S6brfAp1o?+e;? zTCx@e-58Z^zQOk?{we(+->0cefKn@*o2AmOkDoXY1Ntx>#p#W<{Yz|t=?vjM^h6Ql#frl;t1b;&F34D?Abr;I1;nemghFsCnZEN1Cz+N;-V(T0|)uMPa4f= zn(Kw}^T`<3UF5qlFT+}cQY|#bPwZ=!d+=bY-Jd#tJ*20B#ZZ7z1!oz@j~@A!m6mmp z&pGz~HS)8!wDsi-U&oskd(rA<;BE-WRS=sC(bWM>3feKCKS5Pq^a-1g zb$WYZyIPIb_Ps*_kzY>iiF z@EvUfr|~xUq*(%>lq(0>BVt2;_%1)v|M>g^;3229JMSG{FlkqO#yts0k{r0JDJm+mXtL3SXTHhZ!!6>VQf!kw)SBAYANirEJut z?aCC-Mo{a-DL!ZT)-*Yzx#lO}Q=i%jOUX|kTfP8w@m=1Aqzc+w{K({UKL3V}h;?;1 zh`NJp^WS_}Z;~?prtpBOeUW{&?)|m$?ZDRxQbF}O(?I?HRwJWAG>#jqC<4qu8M}T5 z(SjE8->)rBP|A{{#2Zu205j|13u1Gih{?XQWh6n5gFOOr;8MBmftefpPpU7|f(v92 zy+?k1`GJGzn?8(lI=!u~8-+#VGvKh!u_elDf&X=mX1O=Es-cgTz0p5@16>#a?Br&Q zr2l2k%M+~-<-3nPKGTQ};SP@FE@GfUo4(h8DeA1iJ=CKETnc!4-M_hJP#`eRNNwN{ z-%sotg6T_j;=A;v$Wa!KK!bnJ%r&9Pg6yuHCUejes}s?h0T*-KNG$uAGRd4amUL4K>rh};1SD6Pwttua`e{9 z6fhiT{ZD{DPWS^y_M4Z_QhRc0#PlMMH{P zQV(swI?z1;Fee!QZVz-}QOlpnN>^?mG*6211yBLt=1~oTdvShSTfG{z1eK!GG z4RrU906IN=GIWT$n#>dK+dAmjp$qqZAvpX)m8H8I$_}TzqB1LIa?x`k_rpD&(`Ht!J}%yXHaOMmyRTbqC8@SLeFbwXz|zDUz|C$N#%G*Rg~ zd99bejOj+W>u*Q#=Pmx7Q$pSxkG%kQ9Pj+emSX?JNAbz@l1-hQ2K z;uhGhAt-~e-2dtGM_04!%6pe$M;*dbpHIt|n`To&Zp{|P=8yD=6D}r7@K+k;+-gZv zzzRBwt#>LZuX2eOdAHB)b*lp6FN0+{gd<`)wR5k|9a_|A?)?D(+&c8p`M*DqdqmCV z9s^AsLXK`$6zV>{=K~+U=^fv~HvkP5if=D>B)e7nCM<_X`dq2qIzC%(O`gtnQK4vDt(Uv`28#HG0k(#nDMC& z46$@SU&kT1>}cSo19S^^-W%v`RqTDSb{o6*$bzy5MH8TJ7AI{85ts>3)Bgmb0BFh! zB9M!+o%)(Sm8>V}diYGhdqypryATG=IR)bQ4em#?+H?logrxUH)uhUrG)Gu+b~8L%h=v61yv2tW(8#=rpkpGa-$)*@qKUKvKt|GVY#6`k}y^5!tb^=qrav<QOgI z7R?F_;vi>V187V2qo@7cx0F$vOF)_vC>T9S?I~O9=&4v^1rrlVcr#4dhLZBis!Kr& zbLyNK&8Ct6NVQD!mPjTf;t8;i;O+0LqMmtrZ4QrCE%;&G&tXM(w!0B|MeglAlG9|j z4Xl8z8B3UMd5Gil`7wsfm9WX?Y3wp*pWp6yxq}iq3Vm&wYKv`cE+B=+XD$kvTMi*w z_ebV04z!?5(5Ah%;_}KT4fVG=#j}qWkkqp4cOW%-)8emVJ2*q-0$k~(e#G&R~a{os# zf1ZoDq=l3#tYple<$X}@xJ${IT>^X|KQu;}OSB%H*)n(CaqddpxBLS(a$lwEe?jS! z?sq3^$Y@HRxE9At8z6QYXIQlECIE*AaQtVi(>5+;li_`~tHAVbJpnqol$0NdS8!KY z@Lw*=B1Jy6V?}OHAmz}JlI6bM5IfQu=qRQs+C`%e3u#Ul^d%zG38+v#1&EFj##rvk zLwxM&2D)kP0<-frv%xQH_=45>XgV44opq_i&mGJ<8tf)nl#)TaIrZEXAeA45c!36} zbtr6bXQxW?9gZ&{Lj&Giu)8&*7brT!Xz5H5Y;?$osR$l~Jo1NOskOpOKt|q`N$2&P zBzo1wH&)n$%x;oBxNuuMZC|uJ%Smt7jCLE(vqk`jxO8(0*~pr#LW{O75k=Np4*oG(C-N??!T-u#g7-+5-% zMxq3&{>UNoo$WQc$Y&Qyo>Wc5(l;!kczxUuVWCkDagnW2 zevVr^#nd3sOgv6$1UJsENI5mE{sK%Qqh<8+9#8O9R@0^ubEjt_jif@`)qJ$I)Ic2< zx7vIL5xH()Rf7%Ky1;cqv{#c#4%r!8PP~hvf5NZ3mqz1nr4Uz^99#R|Erh=~&MJfb zdGr+v^8nJfkoW+yqD_>*DkBdIH|K1x4+L4-qFj7zc=nwZ-u2w4!vAOzc*0`+P%Vcf z?X?4ThyBlN2U6Lr61F!iSa#a|)wxWKEibR^RuSfo7QyD;#T;BwB0ap)7aTeq=Hrb&8CU+HcW^3~a$x(LONwFoA&HMfIgzL$M& zY$Km_dCUFYmX~Qn`~Vy`lHNCykr>^qvU67Lnbfv0}Zx9v_5w~ABqdL zs6(!t7!?LW^=@mOk$F2C$$U7>kTSfA*#OFBCs9Ss(k$dJL@eL1c>eY=L$U??sr(w< zYNZzVTK)X3y@eI0>^*qz&{VJbG+|knT36Q>tE8|kp7-$Oemh*G2}}~#-F+{`l>Z=P zoWxuAO+wsekK?`KH}2h3-A#G}ByhXWu`^+BsM(a}v-43COG17!{ocwF2#78=D+uuy zmU~>zY?JzCcNRWL8>ZtA8GPo5s@l6rf(I6lCMiq?kl-eN4>-hRFnkXt1!PDrVvxL2Dq+F3Ww2i3v1 z>>k3cUVF;1XpOPUX<5c$Cd@9Qa5(1qMogin+);G#-mDnM%ZIH4i#s3ubheZWWZ-dW zS1gJr`hVtA>!E)+Da`{GuQH_IAZqq-s7MDNz_2P(+u1wtw0PBQ8u$J>J}6q)I{ zs$*XW&UC;~{!q;@i-NK(=3$;qO|_Yd4MQ3B#~kj0K3KT*y!H6i@mChIma0<48GnB- zz)7F#@m_XIAtPGXhq$$X^Y9;4mAyQDE%1?joxl`yUwEIB7de&8X4{9EUxp+ z`H4Q(;wHH_Aa@7!v|apT2rZ&C-zP}f^!`p=@8OZNVP4j zP^=J?-4X7)Tx~lHDo>4#gkV-(ZTh}JnG;GuJK~bNqXQoE*OB|m?9~5>;fu;6N(kuB z^C80zaF3eeD5|@0t=;!<? z^CJmv-6`!abZ?YC&U#Wv94v_VAb`KAJB)7_nNm8(ZM-=kLkQ{TN@E*He4yov+dqif zIG1*kCo3;)k*~vJ{)8FpHOb5I zMJR01=x}$S`2zAnt$;p!8UmNlo!YuwxJ|UeRKit9cGRhZq6fpn7v@Jz5rX=msPHgW znIAg!7LMN_nggevy`Cg_8JZF=n$@x!DnoJ$wXq_H> z8>wp47yu*s_Qa!4ahx%iIuQqJUQEW`kZ2JR0oW^g z3VbG3c=W}VYJS$==>90LVC*WyrQXVX-+RURo#VWxI(E|T`^yH|{kg#)y3-aa)0igT zsl7S#sbW)&zC{@oyVj(BuaJBFGo@RQz8!|m85iTkoVxl5maMxCt~S~8BVPP*cn$^% zTMmz6V3uM8d2pb7*8yVhP+vh-2!_v#Y-T#|G z%y~}}+~&r&i0t|9TVnxV@AaVi&LM8H)vA{LKbubMR})d<+ZM`Xd)6QKr@Nckg!&Cp zb1%IRh>#|F4D0Y%7gxPr_VN<<-KOTv83CmR5pZcv^*37zVv>ba@r!o7k`@~j|cf-=IbUdIp-xk2_Y#3%w)x5%LYPuQL=OuL|gYhs&zR>N4G@dN;4b3!9O$u|Xnlt*}@g2wcCqiYHt{V?8M3b?^r<^=n zoBs}BImiNREy?fW^aUu;h5SC$03`6=uQ>iZ&Q_EBKK{8^`CsCYWB>QR{(toh+;r6k z=vRP(J)Za*ML?+zT&)=UPnVD*n;mRpKa0bie zjY__=a8dtx$+K%cs7(n$%V~qNllPT!oq94oEYxR9r_)ts=u`gHp>{;6CXt_YnUitr zD~wq>xj-{0cVkC3ij>?NKFy$rBktkLt}+9v)?cc&&z8gZZB%8NY&r~KW-4z|&?J(; z#216E(Ob9{!J_p;5nmwrvu~lzH*B#C*S&6I)6z*jvp&m&)iAl-dEc!CavrJ6bqgp> zy=OisB_uzngItiTbDCM4?xgp;_Q*1JKHb%~PspsJDqT0raX_n9CWano%rxH0TPq-C zwuecNtrrs~*-wVPCD^9FYWY?>>8Q~7<4qYX?w}OPDDC?A>MhTOh25jf`Q$-Qk>5?3 zGE)pvWpCse`T6zg$NfITYct!O0LKTULr-6?yH1+f20V!uyI%1rCz@gm z0tQ=v)rxA{MQ%MRow~-?YekjN1yg!I@-!)LAs1{6-3}<~-R@w~H>%c-x9Eo1_8{&| z*7C*ZP67$$*f)!66ob|~diX4%c5(txLEmjOZ~NE5_gYjvf+k!3B3N*rnFv2tv5KId`U0)6k z%4TkgFD3Ll<>m417>5tCwi$w4_0!L`QNpzs3lP6{{B-qB?H1-c(ph|}AA#n^GA01F zJ}1!hT5}jWQhEDl^Hr%(urr1cY8F2GS__&YSq8HwQYgFcJd{Oy5aRU(YC-8ov-Mav zZuFklO2hiyt)i;QcfRMl2Hd@spF+sig|OKJIw+$crHp`@a(GK(j)EaVI!yXVgf!0I zZSCjz<)E)~VTajQ`Fs>`a&0BuwyR_w4VySheUjheT2vtOB~f`T3viyhumKBiP4xdN z5;Bmo@>pq4o57wq63KsPA z<9_glGs}Rz3z1AkSDJ0}@kamRI#Js0Owmj(*2b_kJ+wRyEpTGPGj`+H-3_H0FX5(S zv<}&{*1pY49=h?}{{F(P3bF;8v0L8le7CStE3e_srEI<$m1a*f>yn$)w)j~ z(c(@uHUgIFiFOFxP7JNlUNfdf z2AU0;Rn}wS~ehLJQycOR@~<9_y3j~FsjZO?z9Tg zzDI9;^MPy#$QF&PTiYGaD`J(z2(y&CU3Z+=C-u@%pcF3&Z{D$d>cgfWrtV>sgHluw z>64(vXH!yKc|Pt{;r$Ix#46xI1M>w2=+c*>FaL=&Dz0TKhYD$h&E-ZfCd(~`f0#UO z-I49UDS7ClJ+GdFG#ReaXsdiSu0>bBP;a;Hx2Wyf_TM-bQ-1tI%w=t}E!0dsc}=s? zfjblaYIaZGsK&BBp9T9Wyi!jZg{)N_BfL@s*k_-g*leb<7|IJ>1)qGBy_nzT~ z@XMBb8G4#N)mef0ki=#x0qGs1e-?j4a@tt#Jx3teG%NwGClRG!LxvhGn)Yu7_pEFe zSy^Hz0w+l!aumke%XQYQ1if|d&>KmxtIdYbvy60=`*5o-!}C1#TXWpw`U1C) zQFhcRuE-0HK7+9KaNDjqQuEqeqqLghnH8RoE#Ic-pB&q6eeO(i*cb%hFTf;t8GgYt zGiJ>2P?5X%QPy7nGq1SsWeSV#6@ZG9Wooayk zsr|S=n?!iEDWu@ZuPk|=s5-u2P{kWPG}uxcV1v7HJ5jP)#$9}M+iC=HoulB(E1jjx zmZHbfatD&Mn2xgB*S_@YQB&C-bvLWFg@oIzG7gBDQ>aabii_>)Z;;nR@ir*0p>TN1 zGv0}WQWuDon%f}G36P0>HJ1_fb+#BrX4T3{r z#MY`(`n-!Q#>>jQrQ<}@4T-(_?2SvW*0Zmcvnwm!N4D(SaL zLaO2L7qoFl)Zn@6e61o~_rOX9lUt-Na$K~QuA5@0t$N;0r&03PiI1x_Gn7WunLykB-q)#1mi-C1{a=3|bq&>?~X zLZ{andhm1_tg5cTE7SSNBt-d*o zvAs?Sf0F=>fHI%os<*P^woQ9O#dvh=Q(^xiZ$Yd={puaj@#nXa@6f_dzrFHFB1p?` zG15>@C{F05(_NzFndh|_rRdnv`zA@qK6gTG7y4QWA()&D)j)F5-bkle)3+<9JsV5> z@|=vdEF8?kHBA`%lAo%n7cg3XkXshet68gE4zldpi1#eJ+uS@MA-A z94`oZtw}Gi40^rid868Er$VL!jF}$0JDe5Q&-FsIVOOCIFNbu0 zMQiW-^aDTTTX!#}N7q5EF0RjKI(2`#_7oFwDZkGpACug%G0EOdQJl8DH9O!|wNPxS z%Nm@2eKFOC(_eLdeTPD@8X+jVK%W3O({bq=UB=SFvUX(_O!FF;Wwru*9NNgR{4j~b zZ~As@hHq?{^zU-3$K7G>p4C$(wWB>;_lANm;C5#-kxBC{>5@CUK`hMYM#$oL9xZ$G zfR;mM!Yb)+o9iQ8rsLi>b{Jr^HWq6$8r`XsTiW~G<5rI(FZ6_Fwn!0HdLiggV90#h zwXmbVW&3aLfwOQyCw-AXA4&VnCVx2;5x!~T3?A0ziz2sTo}Sp7Fv_F!g~Dq{b|OFK z#KrXLERhqR@~_;rcvIdX8Fg49&DdBT8fS!2W4Z7joY!h4xb_ywr|`+uG>s%QoY|jwF>)823G*5%c+E;xc~mlg><8M^pX?&8Q>= zCbOPSlfmukV!R%|@vwUlpj_-zBRUkH{J>`^vFpol!(CERe(gdz>)v#ELY-@UM(?$G zdi7_mRQ~Vc{;EdxxxF`w6y5elO=_As9Ji$hJ6qPRnB7 z{^0ne5h?Wlt70+3k&PD>R;{sn(mBI7CE8@AxZRZdrB(N4TlCTJw&nWS5IXjps~AWj zFj+;uf5tc7&(=+118>QktQw^>hhXV95?r%fEuK%M!H0V|En7lYnz}%Vb@1NvIvt;3 zc)_g6_#28IfhM%P(2#mo`HFxJsnCUKS!NT+9bGP|gy=Hv`+U3SgEP;Hdq@s6*#C+P zq1JM4o;HXrR0{t&RKwM@5tQHb2KCI(YbtQH6>C;%dza(;jYMdY*vExQ1$sCGK6={XIwdiDe%;uy$Hy)OW_f=huM0fxK z+foiw+#ij((nzUFW_Qb(>*SU3N=1I%k+MeTRVYd{(ACzXvme4sm@3j`j^%`YYz-B) z;TjAnx5gkxXerkE1oBcMAB+lIz*v%+0Y54Dw>NbfjLqwTa`pQKo!vnpFWf-XKJ~)L z*%!LSCk~muWG6Q8DlsE?+J!vJ;wZx*In~V}0mqlZa*4 zRs2jFcQh+A&&#*c#Ys?SPuNa5^&Fej1sBlq$tEqg8FT{$N^DAHxxLH|PYD5CTWb0o z2BaY@2g7eY$K$9oStFZ@^jRyaGQa`jEl41~!m@QAy3u#zO^OS=@lxgR_AcA)1gvX+ zF_YmG-l4T2ZT7Z*($DaE-YQ+;J$j)!>~>-LUTR8~8Re;Jc*TP003- zY#Os&z2eB56xf#HYj(5)$`6iEXHMJ()?qp=xJGSE^24j;&YW5+fN&{)^!a5hqN5+8 zDB55xaF;VYcoAT8!j{B;U$jJO_Uw^VZN)=EDfrD5^}~CJanY~1L1q$;74L} z(GafZ^j@b<^PqH=>W$?%nF$w6L^S>Ls_Gp6GA;xYu2 z;^V9E?2eUUC9jaK1U^c&REAgP+E$5C@u(8fI@jQP24(Gx<+6$;-&Zk@&nHGgvh& zy9-XmTheg^4P-b}Vy!$E5*$#SpYYLhqJcJ}Hy@w3VJw)dEWU`Shcy7J?L_5#NO5D| z1!hw%COmR|_GGhI6kYQ0itrmZVYi%Vs^12cy&6Qn6e_!^Yi9~@|4}@rchVq;wo*&~ z>y3+q%yG&ZWKXsuDu{b~y(1YhktWn!Eb}NG`sZTLoBe;suef`wlk$?(s7ctE~L6{2; zYvjsb^yvo}o{r^zj++Q6DazqHb|W0ui^MXZ!2(rmRw>=~?fz%z7KXHe*iqrp>K^Hv z3bPx-Db;tLdyheHkxve)mal#mzZeq4ty|LsZlGSPZ#max3@Wypx_8N5ErDpGQX>~0 zym4d^I@pZ}-Q^akQMMGML}X*L91>L+O}D~9FQ7-MGqEFlUH zH6LSCKu&rn->F~a_i3oG$NI+%z1V}MU|{j>-8CZTubhl4NomZZzI&j19Ge_|Nk7!C z=Oe7TD0c2-jo#&@!JV31H(`e3uDl# z%rD4fvUD~qC}c}r@dU=qHSL~!x2+#0J$bU5z1K>P>;#Os_=~tyuWV6^_R8my7n^Pd zPBt0dQZ*KyG-{aFOjr$nPbp&*OHgBzi_dj~*Vbtg-V0b=^itCoPSy`3f zLiELOa|`9e(o*HcUlww^u>z>fJ8;pVuDO##ls<#>)XSn#dfegsNeu5R7Kh4bb8iKP z|GoO)#3yJlYwf3(P2ah`U_XSYIQK8O>HuXkoCRMhGksL+TroS`QqjL?oxdOuU9k^o zvD~i`;@S{)aw#*d>mRg{MEd9$SAv8eg%7z|u5TrGktN0GN;Q~?UI;@HS{$aS>ax@H zf;PVk>D}lXg@l9L(HLbIap|=W#NgDtIhC9r1Eqht$wtmcR=otR(aS&drIJ=D)!Z`t z&_z>~315d@4~Y@k@{K@u-+ey*&4 z+6S*e8EbGy`iHtAi8|K@14d=ZTCgI@dtd!`GhQ~E9dQqxQIz+V+Nx?Bm8%Y`3u#a-sn1zR7r4y%nJ| z1`-&ZiC*QiGrB68EdysZw8~Ae%u^Jajg51#a<;Rlnk(jo-e=72j?aiI<q(sQ!N4+U+Dc4Kvr;!0MTK6nKqM#=@dF9ujKNyQu`Sjg3L zaw$&|Fss=4JfUB|*V6>hQ7gTBX^oP(sMQSp^_+P2ZC|I@^ezsS3r#i@WxZ(8Ci{z| z&rRPw5Wy@QA7-{*cHfC&aHwM}+NTS06rjcs*j9ne5EN*Gnm=UVeXiRrSNS5~lyS~lyHvGIL8p!sH)qq3z7nY5pEq<8tc1#W^Z_YTb zW*x=V8)+(Kw9FMy0M6Muu*YSbn1#QXf5a@G@tsc(m9xaR7g%%2_dg$5(GPNA|hJpR4qZ>m8mo)gbZ79)uN z$O=TaqO68>@J>&P&;%5P?wMSYeQ7&Sb6+OkAgkq>7r z)iraMsxRDwEF|{}{_N*UFNt;uV!A?NNAGF-OTSzv+RX%QXF>QLhl(2BT+qx~RdN$* zVfA^8j^$p&KwMi_$!TtIMc5ht(ER2^&V@0Y{&E?6m?7hcnO-8T)TJfFX%p}7 z(pgS{BbHc&dpwW#o;V?(#f4D!bixZHKg3 zs7qOMMsk+OFMta(9KV{Ts3$i$;_#xV^pn9pBY8~2(4cIoMuChGZad@J^}Sk`F-Lm# z#QJ=58P;wdN@Q#fqmjtX_II zs_!#DbW0W&KJ4}5PqR^EXD#=tDfViY$2N%?W&v6HKHSDUVqu_`9O#-RnqN=RxM^pnV|gj zX53owE%WXdU#b>3%HVu567~i!5UG!=X6`cWi)PN^2 z*KEXI&g`GVracmS1(DFszpT#){yW0IA8r3%n1}rMe;uwI9RH`L$^LIJH!7%2G-kWV zs1Wg%6l`*+s>do14Q-@*Xp-GY1c$!y@4Vg%K?$4Mx93gL?410eUdN7H10gpGWCZ;g zZ5BVpc~{ox9U&yq^dlrKp4}i~C$Oq)d^I9#Ge5>GS4{~@R{;j?&695+yTLBGkG+nB z;#+p-8f(ZTd<0Nt2jhY&}j-zF>s|oLoZII^Yo#SMylocl(=(p_|%A zz_2LOz62P0auE4?`4${1X4m%#>7mu-bUKDN&X7dwtaDGQf;lc5{Az)h*0ch-cja`H zLOneW`t#RiPgVfYxzqlurKKt)VhUJ~ejiD|#_jj+>Ai`ey&m-S3Dmmk*A0vco6#&> z#8Cz4#Q`{sLc=se(mb1O9fjZX^k!(@p8tYCTp$>i+>|NX|ksDh&>@+4D zDTCTsQJCYenhdRF?OxPN!2=hcVdED%21h-5~^PIIfZ?#_i0 zJ*sskwx$y*?&S`yFVYZur{pA)kOw`$#eg&SD%)i0-CS*}N#7)Ei~a{=C11jD4IwOKO|At&=R4>-BgOwhIy0j>?rltHM1uq<40n%xPne9a!~ z#)vlM_GT-j=?ic~%DTp+_QHsddjVEfl{cFG+k8(N`~u}2r0h-UW&PCzeu{H!#(@D0 z8xiyA3mXkvqJsl^jX%)rMr*#iBBg*mF&5_gUmWHC^ZoqacVppXp#T{}{KpGYhqntI z1D8_phz&mWqKHdwV#rkd3SxaaGhjk4jSKxSHC2yqrHvWdIQyXPTfy-vyz+jEB z{~E1MbeY@OYhV?Xhx}-{^%5aIyp})nWmpx(Ivr+R_?ODTmwz`QZmg`dGQR0pk(HbV zw3eHbu>rhaq)%noxqah`w7`R)SRf(_wqS2Vza#e2)WM$Nmm7 zsp8-9nE3D!0m$_BFD!`ip#+j+Cl@;Mr`b639c^YZ`ja&XfbQJ!t zo}$?SWq4!V-m6LK%=1je0qUxfdYT2CSrcKGI4?!CEM6sqwJh7c1a`VE;HxsIJUd8l{Q%76Zw#z89f;rxuNeP9n!JAFA?tkC~#QXW3kyE3) z*F(fw3RNvVL!68z9YDm{6lan6M9W|moq7lzQ8oKYw{v*)6a+cq|7_&RmlbKnIMhFc$K^(8teKyObY^zhte2nHXP&wqO4FLvzTz%*fq%3g?EHb zg&Y0-YOJiIZM2qkn-h9i!>enu4Cc;UAlb2THw}1hG+-c2r`{b_q3>v+K=MogNmQYc z-aKJN$uO^t!`gsQ>*fjF#gMQ^!M4i^11WdkrCLm{m0ZgeG@sKHYf(71>bA=kP$2$9*f#z1U~sw_x9A%O}`1IkQs69V+&p2 zWD(=i$xY&eeGW5U;;n;SMWl*T6--VS^#{1tnLVs$vPnbEjqjbNFY)KnFbitSA*GXN|@GCj7iH!F_e2qTJP>8q`Iqgx9j)f3k~IF>pL ziyl8y+HE*YBVYB}4Z}OBDXQ^`AK<Aiw5$) zpGps4oU7M8z`Q1 z>kf?d;4CZrrxV?JN70fisy07ZdG8TKP+5G6@$R1;uA^cZhn{VqpI%33OuxC)xXPrD zGvD{VyV;|nH~T9YTOY9kteAB4d_(ubhvSKX`-)S|h!4(;8zn;K_y4mR#{VS>Z^7Sp z+1z8)gKYm|Y^YF+QkcdCh{T-A5pDYgIVKVP2Vxz#na0aGcl=YH$@sNqg*SA7o<>H8 zGYX-NmQxn03bx!`4ZqAiiRr(Og$>Vc>BjzPPNuI*9+?$aW6jA0J4SCT@VQmbr-~Cz%2GN9ai|Pr#R*#S^eW_ENkEe;h zlBF_9J(sXvTa{i!Se*}O%#TUvbrpc1@> z>e`M_9-7_N)A{4ls|TYwPUai6d#+Z=KZz~I_GOk{+z5Wz69DNiRQLl9bY4%*I7?GK zWyk&)<6f?@o$ln7%H&#^qhjJrI=lfW_uyUXz2bt%DA$yjSCGwmN6UuF zFBb`>_KILM?IYMWuxDeOce$4xx$pSLh50NyF5JsJp9E*>)gy2RXj7dqKUgT`>}T~( z4|(d>nq1~-Q)Zj8daDL5iOQ7t-=ul)ywwr$`EfZ|Y##TwXwGfh>iz{d#u2tfK#xOw(fc{efNW zdTxGO5G+#eMjd4X!8yf9c47XLYqj50Hk7x+*`isqgGUY*v3GXwYmSw-bG|}42=Qw0 zm*4Ypqu*G{)69YQCvzqb5_bib!8XI^=LugoeVIK|=Ia7gt>@9Z^6T&&%Fdv@bQn*A zXTK@sA6G?wPmND?p$o*6S3^t*LEO^~*{CFLim{{_Ktk*Fx9-hV-|MGZ_RlDZF0>m( zr9%JuvA!(W=mXBkbkWgsP2uXtJP#Nf_Uaj?yoQbxE$&k_84F#gjZZ~Y%2{+U-%aJV zGvB2_#d&1(iYQs}^VgD~Q>#157xfq))warJWOa==2&sf3+xms5w^Y~a0I|gl=?Pzr zQj{s?))LY2zpg2FG4LZFZ+==vODF57)~nk*vA=QOs8@m0X__X|JP+F&q?tO8X6^hK z3YB1OHGNvZ zYt3ly(ERWfrITh{hST&$yMWhn(08g^CN8%vcfHX-mkiLmIrA^N&{SZuT$(HBdtal9 z)gf{vPQRD1Zj>|mGYjQubG#J@2{SX{2~hFlq!V zXV-&X>&SlU7BLWhCLPKVYFW3}xVyNYXg1Q7Ziio0HZH5X+t@tr2T6qYoPzD%9S++q z+)3y(_GCW~93H@Lso<@8F4%EZQd-2oyO2YR;v}JVC5@h|y|wP%2r$~##JG$8DwE5U zWby4-PaV^wIYQ~TP&+}rN_A>+ zquXM2MoxRBljzytMVM{wCM+UAm3AqVOrbvwq%NFr!5uWYTIf@=%bgPN-5LFlY*{fN zq_);*inr8#Qf!&I#459F3~y!w4#T$>Ta@LKGul0~#Emlwp)d(=fSxI1{LMNLTv2JCLe+HB6 zEbWQUOYfeKDr+e>Ibj(_u!+(4iu^d&A;6h>9+&F!!01!ykLxz~>pvQuVniC9k>Pm1Wdq+G zqkT=5;Y7bIBCmCR&#s&Jkz)twaTQXg?Cag|4A^3f;5^hy{_Y7y)zfXY<|$-0Q#i_REGU~A^KMA_SsjJ8=OgZM>UPW z*YCIZUD$teaQF)UFXo#6enr=NhF!KU;>x&wSBnMiH^I+{j6VQc84Uj~SBM~vs0}m< z^iykK)iI5!>_YN8TW6WXu5u}Lu(_6Z`@pQ`c3|c_p;@zRV*Ed4Xdc@}H2O#X>jSmr zHa)a)gdo`E9w^h#3u8k-e@OrUT?Ufdge0}4@*5Lte^>K=8i5A?!wCi8{QqkY`rmp6 zpb6i-a+@hc8IA8=)fbAwzU?xE*#PqKEx;|Ajb51YogII_pA9bw&yk%39YpP`|BU+` z4Mpt@whb5N%M?=){H?dcyW4_3wdE5!!u&vGQ zy1}=y+vusQlpr>})@}&IBB6|!FNqK3D#W_x%7THqW39dC2fbjGIT$ku`Pf6C{BX;R zP?#=w%e+^$sZ*;Q;tGVv@1}}xu#AcF-CkrTqUnyTCTT_7^0CUBX2I|+|Gvi|;n#Mm z)Wu6_X;*t?LX^+V^juM9IWet`k*u3zs`KY>nYIIpzE3jDagF(W&!cuWm!kT+WvC_( z8ixOL<|cg1rq8=t-EduCuk;u;OCBWJz#o~jE;N3JH8cOny(Fe;TxwAr%_ero1}i|n zpDtUy@J+7_z&l4vdrqAyPVvZ2leRHkBA2^C2|zuOw`o)TSm){}*0kK(4H#CuAMF5r zr#*9p*vFYN7UPxv1s97Wqj6>Y3SUItDV+{#ORGN4Z-R{)YbMK&QhgJkFR~B)| zF&;h0+iQy2nz<%pS|G#q{L!^0i)zw28hLf*O;0FRGwAz^L`_sRf^{vP$W1h zRlv;awqQE={1L^i$pv(UI-TNsAshX~0K()#I~nvus`^^W@m;jr;?EbY|D5E**j|L+ z{8FUXC&Ez-;ua~AUEPq0wco8aHEY1-ZQ!L4ErE=U!gkmI8xbX%@#BicrFXh*Zt*@H z+_zBHBW$RueUWzwuuE{=jnj38FQy9;o^rO10obXo@N!{+Y|afv%&UD465JhZPg0a) zhN-_ zmt(kZHRcR->1aW`=k4^U`tzl-4M4K|G>@@dUs#MQv7O-jF!<+puB_=9n2?>r@rf5EuO zmrp%t=V#RBQSrR^yv{TgpnS>uTiM8U0NJ&NS=M!jy3Ewu?38ruIj}7`;Rg<0M0J%8 z7|Eq!?6wFWxE<@pib~^iS23HyTUg=LuGouk-|0N-6Zo~VkTbp&`@R^bC|L(~U zXU!+R#*1t=@Vte)uaz&@310qwNKbMZMsa@PnYmnV$289~6gjJz=wBy8iV0m~2o+BY zb&P=yMk!D0zpfi%nACmTy6BeWHZ9V~;HW$9>xdH5-fG%5QBsEY%U2y}!tG9T1KQoF zZfKR`mv0ts#cN+6!U1V@v<`G(-v)a%Hh*=mDmWJM)df)e-uW zTimZQksVo;t4j0J#J9>7WGs*oWoSNl*;hR=iJG7gyP#Rn@fhX@TUE}-mQ&X364i!` z()TW&peKs{9jYnr^P=oyshX8W{3(~7JNK(;MX{A@lm zkGAL3EB8-ca9KP-`*CkNnGm#Th|@3|P(iHM|0BEYILTF?t_?gyFdp{e%hCN8=DJ%IzNc!^p!luS#(xe593 zh0ml5s9tjscq0ay`lVUa&LJzr$1Kw3yJc0&7kh`yGnaG-#V-kiX#|$BMT~pfBAXTA z8N_TR{(_P0e~BEMhg(K7UX1KnClf2Lh|qi(g^DjB6eGLF7Um+31;?*Tk%GW!ZqSbN0(3u!f*dDA^lZC`qX~B zFHYsXrFG#ObY0%D*YEe)d&u3kr~wQ|#nw8_-kAl+lv1^`%c#+5w_xrT?&~OP zI50^x^fipQ4aM60j7clKWX=X{Ti?8V^@>Mk9$jU%o=FE@X+h)#5}Qb!S45y14X=?PcSh@$ z*GLh#MCE6NTC-nb#gRwYj>qm*u&ndD7MMFDS!%%5*KX`+-uYybFIVASXJfbgH+&7Q zmKEH+wZEr)m9`stBi=Wbg&$7j@2^`PeRc2Lnv0eJ_Mz^ z2qG6gDKV?MwMZP8YLOxsl4S+^9!?hu3Pg42DC{6S1z!p{-YP0@gb z!S~@4>`@OmqJC#GjNkul;sk%crvHCtu)+V^Y}@afBj1E`M+{AJHE3tL@mU~`W-fhr z3xE0IcQB6o4yL#(wrPL#&J+n(aN7#NSD!G)e(dWvr+bn(?~Y=d&GhIwUyj8emx^0u zzVZBj+WXF^rq;GiYzJ&0Dk3$a$4Wc&PEZ6?P(-9ifS{3HBE1tu6chm)U4nE7U3!?b=ki2Ho2!x)d{G`JQI3* z&{VoKcHRhU-YYt-ZB=u>+t@mV`ZQ78_@l)a-(%9QH_4I@3lg&a)T=Se^_7W*na+!A zX|EEQ_`RDWs*~cN^Ow;(x!CcFcm3;Uu871N+$4+W*`#k3s}7#ib+svDr?wH4tw8lTPQ^K4- zZgT2Q^(HVM(x%w?!NvBI?ulv3D>EQWA{0S**_rVq({@_=xrOUWt?uR{jWCwy4QQ4bpdf2bb?LA0VOvK;h*Ro#sl~V!_v=hxRGb}rC=S4YHPKqTm7O=@ z#()CtGDj|5IUr@=qh6&`c5ezOU=+Akv>htHp}>j0x{urR=$BcQuI?N(IZ3gZD2@b* zr_)~?Vtx^rP6x_3tW+D{ys5+-zC;kSQmi3gyv=KSxvwA3-NMR!GLHqKUWJzB*8q0K zta_MFb3PPq$XG>u2$vw0JpVY>{N8YEeEpqE>ORl-U%sb+zUfeVMp5`jx7S3~bZQI} zBrOI8n$`|MDlQ<<`PHcKQJg=q_1C?m0cKAH&&qZ5+0p<5=VsWS0O1s_-cUfIrYMpk8WSHN0~)U^+f4u}Cuu ztM@EzjFt#>#59l*A1BhYT@$mWD?5#$mHmM6QL!@dC7B#bo_Sg45$VT6(Z)zUW>1ntTKix8-~V%{_Egqq~Arw7R@DUsp# z#c1!{#557&YYfL+*p1x~rrMcC_yvuww%%o0h9@+Hs9nfZXq0_PlWVzCFlO3|%l15{ z0zW{Wu^(>!IS!p40w64}07ij#K!7I(?6ax?<#ET8)egZSQ3=6u z<_)!knRWU2x@-*LJi6|ZRoKu9NDFRninI+tpu@63#+iiAi|U2ByI0>5im}Z&O!h_K^Vy2huwYz+3(Rd+MCL53t+@xEl&r%2ql13Xnd z)v$u}u=Hs{+c41~h*0ZY9 z+U+@hT(^9{2llo!Fb|_`vU9vh)-ew`qBdmO9NwLxRMa#2kp58@Ea>TsJIh{lb-%a}?F4|TS89<7%J+kRn&V`#x>N|vXwqKZQ2?8*UAT5FBiCaTE$z>m zah=F1{GMEFw8=Bx_)5~+^ujG@+kHc+2isW>56Lo%M~F^BRYmoo_n=3jB^Qkhu(#~9 zxh!iUbL*!SPq;fuS2T|}V0f=^zffb1nSmSg?~h@4QJuf`U5AofT0*IJMtCG$+w3I| z>EC~DY@Y}5YOlE;98`>ChxW??0C_-upE7(Pm=Jy@8O-*l!IRn)+%-T(7a);F@?yHE zDxr9fWci`$yD?f~8tV9hY6$As#JI<(9}jn#)nFoWg;ZS%Hm963^W2>Aa0Y=`Q$ili zZQzjGtv)`@KUN5=F&`St3`ePA?n{RY$`g&$0B9X4- zCR{=yYWsM@pSSMb56=o%J$Ryvz>vVyS|PW?Z7*8CmL;s8*SfBHOzYmcqJn1LQE?O5 zW?r8TESmOt>-%8%u=)3bb9{AKaT zjv*Pj$1UL5tv7?;H!9HM1c#7;YF5bg!O>^C^e94f$5gbiLvp5-9HSZ0&~H#56f$I|MGUauU%|Fm`#sWP!YFoYqTt+z6TtHCY*@T}GWBnriOAD9F{syU@6C{#mz#7@?W;I?waUD#Jij(B2rJeC82l=6und7DuacRbd60Hm&&9209o;$E$|MsY?H}kWn=eM`-32v@$jUN7MAv^lX z8JT($=5dSbaW%DeH$Exs?dD0^L8lg~0D-xB1&2e#NN@Dbj`l()BO z<(Ag0p?{;FoTEn|(@O6cBWEu5dw2JfA3U#n*6ne9@&pg5dEclqt!qmzd>M-~Q^HZQ zvD>pCQ`!5G54sZxqvjCbcX!Lp4yxUPNCQy>B*lldict$6;E}zB?P3&L$7{&QoXXI5 zMhD=npRC*06=&s{@Ap{`i>-ch*g0M~ie~M*IvjxHfuQzyAmX3?g;N;S(Y#i$8WuKt z_I6I=ddU1)dy|1n@Q)fjXkcqp@rDuy2k$oWdS1;8aJy_uStSwMoSFRHos8l-Ak@TE7*P4zUBwSb#Ij& zlFUC|YZm#q%R1uPsEna-5$`GA@KFIXdX^3)R=`^B^g$q*;?vcC6;p7L@8;lyxpZ#- z9s7Nn@~^ClN}OiE$L09nIRr=kwEXS4%fI<|T=njUzcIg`FZ`W*@xQ_gM&$fve+_hn zCRDiFHruN&eQQT=d%7xBXDhl=VZ@K6NC1e{TiKPs(x4lr8}flSZm6%2@Xl_;4Hj)V z9f0kqWRpW^pD0*7bZDa9EzPIR5q&OB8$s^TP-!eF&+9D> z+hi=%Jz`7NU>(Y_99AdSPG*+$z+O6IXxPRb|^pf2HRS ziY8(gBMD5^m1h25QMb5r(Esr6lx5^uy4b8mS z@xUgo+pThDr>b}CL(7!9v=)=1;JmwC9wRO5A`PWD{7yb|{=B8wuj-`62SG~(d1#;( z$#5iiyV>V^PRq9@#jKiz9a8a!=WY9_(+W18^P_=`@^r9Vv9?zgr5KI?)0W=gN`qHu zAg1sJC}XL%sw-EeY3^%Ci7KHLqJv=!%c_bwGi>f`TpL8n0{a2ehdwvWn0j{HkvnpLB*DaY^BMXeZMT@5p`k<$bZ2+TNLx;mfY zTJjDK(VGxKJp)n;;Hr#r&qM_}8Phii-XfhLD5xrH#A6@E8Nq~LTzbFvBP)x&>p70R zud~OBc(0ABnV-AAVi@rvr)E*-h<^(K$3UwQY(G(Pq~iWF3PEWyhWumN)NQbctL#9z zu={8#n?Ce{=pNjp!rf&;Z4N?n-lc=!F}FCDleg4cgOkU1O=ZXl==8N(i83co4BxJq zN(nBPiCuITHs$m2@%got2yfDBPE&IA3mQkl-S6nGt@4Sc1b6B44b^$4yJ*X}HBaWM zamB!p!5&v@Hj&f|OG4dE596jqAoUB5Hs1Ymbu{Nfp7=cWT&w2!tCcs6XFd7b^mQOh zzo{V6g|Ow0?>2MI4_42fmhw*UQ+celx2v5`OEt#Yy{a*6Gac?a;4Q2>r%U;zIbZ1b z>!(7HqX*-4@v!${WbbB{KDqyx;9GUO7XMtcm&NbLb=71R{)~aOp$9cT45*SAYC18t zWpdLxCRDwrR-vq<;3~?OV(}C90Sn2FXk%HnuZ%$-p?+E!!Zw_I?X_O~V<$-!PDzp8 zI>O#6F3slXIr4fxAXJdwx7Pm6`y`G5!ew}^>{j~OC?KmbHm6VLpd zrgb!q7Y)mfif2m%^@ip+JL=o>$G;1|WXa-miz8fHsDyZjGKh zqwrEEH{d#cDenz5lG|ldHCA1U_HaqQPvSijmIw@f1<*>OIzFMi7y6ukKiC%Y2m>HM zPN4snd;RMfpRdiqxkUfzq{LdFF4PejK$48CmQJcodt!Zwd0;hJUmqXEYF6Ps8eYT3 zf0Vp27Y$~%K(k?SYG2Bg%Q8dCPdbJ$KUyPkJ(@{28e27W(?HF6nHXtZ!$xg|Rkmvw z{`WE{v@RV}nV*Zyk%Q^0jMax%NGqpLS&CTlFL+e8?hr8~4=1;q&fQ|w#^W{!80_~| zYHk#6_D5g#%@gP~{&w`VKR$K*oRf`jN0j1bMx?2uqa*HjWLxrV>(U{QDl?1QHTWR+ zATKJV@#DiKd(_kZi|~}OWJNjVcANKbv5N6+GJ^cfd3()lI1fxO*SrqS-5-^K7u;QB zIFI!;q!L~si_OYysb=YUQ4$-)y1Re}K9BP2gZD69&~~`0nN+yB5^rIf2G2vWZ1Vfw`|gDs+b+`(px}l_PE+B$r0#Yh7BzUnS9)bK$OvwQ&EIMoO1n4c zW#o$Wr(r(UN2+ge&Vi-){zA!GiWduKX0*t6PO*ENc?_+(L% zv+{ny9@W2Gdog#X#~qEql~08ZHWABoSqkY{Q9pJwB0=97bLH#mtnmj>$YY46@&2rN z-`8(LnOo!T)qx)sda$Oo!riMIOBPDK$I0cFAg)|C0`uUV4>P~KDzycv`y>B2Q5d4_xKMKq|t%ayiBkSO6DcVdBJ#2p+rF~$&`KH<+21Xv$bfr#Acsai%sT)_`HiWdBuM!nH;&1l&`x#N&-3e31sv3IU$rb zSwY`TvrXF}P_3SLhwLy(6zVv32p(pslLFVB?2wA6s^s zkMK8k2VWQq4tA$v{YxEqM7K1^yV09+rlpM+#&fE|Ob9MaF7xZ2oooiVQ9?_3GgdF5 zqB+n@SjlCpv4c6rM3d{~#A@-#mj>=W($T(`Hhhc* zgD7z|E^k16UNi=`C`7&);?>(fRvsb0%-lyN9g-+Seo|2B|3tsr4DC5(co5Y9D2OAs z(&{B4CK%w{d#-HR2%f_}{*btUMIE=KQJR-ms$PBRbTyT_!{pqk2?DKGLSG|404K_{ zj{Bhz|FNr3(x>!dt4LL$n4SS6lb09_4+ z@3=6UsrxEF9F}R8a<83~L|c7OhCWuKWTo3~H_!@xFN26%eW*G+vsc&=5NHWXtyiX9 zj-EPqEau^cT#bF7nsCNrpqpl+57`m}7Dvy|&FZJR?GB5c?h;LrGip3y47J)hWB`dA zv)DShyX80$7|}j-r`XVUDWZ1UJffK2y6YpC{nGO##Xmgzwr{5XvTJCttuoZrgi`)o zy$STRN+!ozi&(G{eg0qx$G-VvU+Wb$yEo#w_8z^*_Bw`TsQR+X6rJM!Q?HfC{o`^$aJr$+I$wh+&(|V*X8-$ zV*L9k0w(8P9ug@_8PVrIlqgB5Sps^BvrWB}iYx*;`}H-T;8btk(NHeE2WoxLoDu?y z3fdoDd=-Nv2e|TnLv8w=DsE>%2#})+pw&Y%y$90TE^1TXDFT$mg{hM1S8MVz0i#y9 zMW)bIcWb*s%?mGoB}#S~)Cy_o!o&xo5g`v&-Gy}5&;69zdfhz*i1Lqt6&*v>WO8A^a0`dc)D_LU+)-j6IP2 z(|a@6Dec9|IM(&V{ONpL9WZb%!w+`8QPEEngZ8qPhc`E;()g|Qo(gO_=s%q(UxU`t z6I!y3Mc3bHMlJv`&eS_>eTzlGN2_R_;YTtf@`DZ(Ddz0@MU_Z$cSr#}L`;k=zYK2s z>PYB!1&+d_*r83CT2<~4JWdB+uR28pj71?hGX7$!;Wh&E(g{*f!+gKBxpgs3$iQ1EKuS z9!8X(-yc)v*^{T8ioF>7>U_y(#4nR{h~)x^P_~Zu$NRpslP~cCmEJTeKK5)wFjnJo zWJR6k;kdY+&W{J>q*_WPsG+X{y+ENp=DbRCQ$r2Z_QZs=vVa zG(a71h^ewax1gTp)$x$Hdl>O)U#~z_jotoF{2|m+$EzEhvRu9%{5tHvodGh!vv74W zRp-4qdZDTDZb&n`02bRTSO-Y5KlrrFA7pggrErr|$CiOicY38iG|2Uk{6k;g@?Uka zrh`vq0M3RZ+TmvAm*{N|@N`?`zUm_iWRC##-~Ia2bkC8qy)dAS9q~cRZ|2hTRPjR2 zGp_#&f5hn3$F1Q6^x{w!pzD8qec^v7%9?9G`znEpO?iy#slfMG4Yx$Ze^;WX0yc}C z(YKWcpnbqY-8agw@Y5VceYXLC`8)A(rf$`3%JEph_u6u2hI`E&QnAM6qaxK_FiEbz zEIZfOiv_|NOIZ~lxEo+Pzf}LnJ@T96!^QPq*?;n%%>|^Bx>PB(KHRL{b?4I*PMWh# zbW)u=9!BHt>Ke>dr)s$CmZ{%}5!bpBBU)!=Wj|3iG9*`tOG-;iBi0=_Jd8F5e^}^C z#!hhT!@qvm8r?H+qc2qUywu2zAG4PAEmACU)nJa%+rlYrV9<@hX@pIR%8G@%P}w1K z?&OE<=6B}0isMgd)|}}NT_P$XSi7RvdC&8oOmN@9? zzy4A;rPx5W@HnaaVrPa*hMH|c_hZjJ65zDXb2-9>tC$e-HGr01zY_brQSwjfF*Tt( z#lZ01)!sGhE5LYU4w(1@&!6U$R!EW{wH+@|r&a4hMck*iTO@9CY)-wjm$RQxp|8{p zrvDzH1!g!|-F&Rv^^9*7+)?B@3Me@Yyn*Y8=gRmyce+a)_0(^d;$Oe2jKMwgg=v@v z|9h9BZl6cP>PL?TA|J6l*y;k85{ocYNooeM5X)Gw{~ z+L<~}%TAx#z5#RXq19+pQ+y~d5OB8#A+lmEF2ZtdL(QdR=0IiTXxpYm`iFVLGK~I0 zUs|ezHswh(JEjy__U-5KojtcHErIl^Fd43vf&|C#*VQcTiyL%xBicqJL^gUf`FxLw z`{leBx4D8nC9WdHo!Xh}8fT<34ZcWbFTh#vl!aTYc+KMK*2qBaxjbYq`Y>h0&^P{} zWRCQ>SUhWR61}0enzm(xfp=G-Wn>{)>ru$61X&~@22wJ5#DNF`d=Ua#Hyj6RwP>nZ zq|}RfB5;L;oN{EoF<(~VYljdh})4PAUi zCpxNVrI4ZCv=h!_Q-t9ln88T>w%I4|r8e(K9_MqEw6dW*$%7{WWL@&G>3a zZ5j(W`X-~%7M;gYx;t~qV}@($4t@EJT5Y4Tv9XGqGph|1d>}bZv5gEG;mKBbw2T0X zV|BXE&0Ji_Q5)1w-}eJ&bXuX&ldKn~DF?=P?H2SxKFS95;m}EcoJp`zv8lj%p%q$P zQdQY)00Ts(6el6(J;F~MIRQ=|P-k$f`NUk_gADQVz~M)Os}ycqF*?;$Q452q>3O^X zfYnH;8#I!u0fai&59Le(aXL;jt$Naqa&|MZI_TH;l4DgaZNfGg^LDi;#7A;TWcnQu zgqI8Q&}S*Q=GH>jxkWzCUq4|tZLEwGmJVh(TYqy|G8>9vg}r7Oyu7N}9dt6UsR zwvyoy5d4G*-I!3EOW#cRZpMTJck8Rcgu~DF2$ItC`wE2PUU>~8!bc@g>fJ6P5^yy+^aWgkA2vWx4b>E-8MGh*WEgOh3j-?SZXrl*c2O~i zlJCP#Q_0>(Bc(h7dIfCmXj{OY&)V_nrKr(SO#4fr)<>4wNa6!p_ly~MA`#nyIJ45k zjA$eqM7qTFvz6=tV?(Ud|6l+i|5(gthwGYn>E97fN!@6V7v|-xpPl{t6^Q;z_f$I`BG_7>4 zgfF^~Gw`lvKnS-F$+Q$!^QuzxC@y#Ih~n4v8W_zt9g$u-?Hi-caibkcin2Hhdr;d) zpA&L!m6}-^KDi>aVuh(aDnDAp@s5`WEAr?W5AcXt2q{Z(ztg>GoquKFe#{Iz==TZh zz1IB1k8t=f9C|~xaV^Bkx3?ynUv^rpSkv$MNcl!tSJq-FUE#l-Hs?Y|5zYKxDE1w zR+sT6zrQo~d_Ed)S4Dc+`J>MSD|uJ?AFp`vNbT>i-{!#kF>m}Fh!v7l_QmZxq literal 0 HcmV?d00001 diff --git a/docs/assets/zitadel-application-4.png b/docs/assets/zitadel-application-4.png new file mode 100644 index 0000000000000000000000000000000000000000..d5660fc4f5f0e737a09065ec9c20c6e52948c845 GIT binary patch literal 48643 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 u9fJ0@yj}8cpZt>eWu3F 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 D#fBI6 literal 0 HcmV?d00001 diff --git a/docs/assets/okta-create-oidc-app.png b/docs/assets/okta-create-oidc-app.png new file mode 100644 index 0000000000000000000000000000000000000000..cf0b75b0e4a21f6196799a5097b52261f876e627 GIT binary patch literal 360829 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

    ;SIyj)Bf0C{ZDq7p#eVk6)W<0&-MRmd0?gg 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 LfUa2D2ZXkltfI+4-LlzuZ*v<4*6DTb(<)u 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 literal 198765 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 z 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 diff --git a/docs/assets/keycloak-groups-mapper.png b/docs/assets/keycloak-groups-mapper.png index 3610aa5737a1acbfdf1e8990334dbcc235d67114..b1ccabb30013adca4f1a899151081f042c7477f8 100644 GIT binary patch literal 67678 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 literal 200602 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%0Ap;7yhKaJ#dxUenQ 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

    8D2z=$LLfigGlhq*lEFg7VW!E|3Zf7;m~xXR57i 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%0DN^Nxf{yX+Szu`fIkc&|g(h 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 E2Xz;%Hvj+t literal 0 HcmV?d00001 diff --git a/docs/assets/argocd-core-components.png b/docs/assets/argocd-core-components.png new file mode 100644 index 0000000000000000000000000000000000000000..f274f7cb821bbf0b68945e804bc23547e1fe36aa GIT binary patch literal 160491 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 E10C8x=>Px# literal 0 HcmV?d00001 diff --git a/docs/assets/azure-devops-webhook-config.png b/docs/assets/azure-devops-webhook-config.png new file mode 100644 index 0000000000000000000000000000000000000000..26fb6d0683d634a5ee1634a86950c765f5b696e7 GIT binary patch literal 40779 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%0D9=FoRMKhk3_HIhD& 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 literal 0 HcmV?d00001 diff --git a/docs/assets/extra_info-1.png b/docs/assets/extra_info-1.png new file mode 100644 index 0000000000000000000000000000000000000000..8ccf597c0879eab992fd49c0222da76f666eead3 GIT binary patch literal 32441 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 literal 0 HcmV?d00001 diff --git a/docs/assets/extra_info-2.png b/docs/assets/extra_info-2.png new file mode 100644 index 0000000000000000000000000000000000000000..3dde4e61fa4e0c14616aa98e3fbe926c23196e12 GIT binary patch literal 8481 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]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 z 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�ZGpj&^c$NE}cyRU*85~QJA>-U(*YCte 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 Oo3i`^NP(=W-+ure2GF(u literal 0 HcmV?d00001 diff --git a/docs/assets/groups-claim.png b/docs/assets/groups-claim.png deleted file mode 100644 index d27e03b661f825436a937ff75d90ae1f546f1fad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82650 zcmdqIbzfCm_dZNX3j#`)BCwI}4(Zr*cWy$u8$?7zS~lGv-67H~o0cx=?(Wdv;@tN+ z-d{bR=LJ0cwOMn`8gtAsu5pdALX;Gw9-Oz6jvJb zlm_WZ!?V&w9VHxiiJ=EL4>WVSbsi}*FnoAej8XX>v}5v8O>O=Gx$#Al=fMEK`;V&@ z-izD3Tjy{6b#O{U-~dX*IC#pKbjGmLi>$A-&qo{r;SdjBV-^Pp#!%x9b#w&6KXblW zSX)7alRt1D$n(9sx%v5`(k9~l1GvW#4s5!ez2cXcm6F;B#$0guV($&yJ!_1T_#ed) zJVSdn%RNv~CBQe(!ybmr&NUo8kJ^X$nwCAWT@9|ALtL@XIOmyPcN(hh(X@b2JHp#J zZ9N*2r5My?h`G7^3EtcAk3W}b&+LU_sD%>y)zH38uu*wXq)A7$>0S97z<$Q{V;<>Y zBAP|lwZHD(N{Z&+k^6@Jkwuu7!zRLWm*|FQ{R5)OdII&aLW(KZD6O;;7Zry`gsO)h zW=Oh?AOyUCT>2v2xpIqOlJn!Nc5yH zM{%1$%hEDGNSj+Gee{H7_7byN#`;X92hE+c5ubl%-#FPg+&U4GIQlp{MM|;YnM@ec zMEP9~S1t;Nsba(IQtmV>1=KTbvl^HpOu|&x!C`K6Ns36Zi18*m7}@0Y!}8?)CY!+z zyIZ1-RY>$fjcRsW;Snx`!Ci}04E>f|8WV$X;ESEzjl*X9T1!gZI@jOUs<8?sOd%)dh>p&rZd}oB( zh-|t@mGG7l6-Aly#U})-m)B8rW{A8m_oJ8}V@imkDd9rVkDjtt6VD($5tUG)Q$rgU z6&AC|lpAu*NAW^^C_**Fzljm{rl(w>7@njxt{e{>9Jpxd!J2~39O%3x;K8qlpxFjn zqPfKN!E468=qOuqd@K^J^Z{fGRFC&1$}CnUVJ{Y+6`7ShM7G(m34cm>&HwI=GAzMFj|CGfK=Y)E zG$xX+OU;y+Rg>j{RV*P=i$Md&`n_u>vJqan)s*^_?G)k^vJFWM3jcdD*pkBw9@P{Q zCQMi-WaZfL#u4U7;OOokR;%cNYk*-Nc+o+-e03RqX>!YY`{0)Bmc{QQrPvT{?W1;7 zn|9fDtacnx`Xzkh_bf6{YGT~E_rskW%g82Z6=@zpmv1lA#Gh?NrO4nsC$|zTc|=Jc>G{5I+~O*E5GNq{Z{YSZf`%ecn2Mzj|5SiM*D!_%*E zI<)FEKG<5HHq_S)@xl?pE7PK6TJxw2>XJmYF+|P?AJ~-NOaULEdFeh zV3WJcHEmis2{G(lTE$%TS(|e5bP8T$>U*f7tfpVc9V;;>E-2b0O7;=fOVZ1}I=YIn zrr7P?7fIJmH$~U^qF4!}%2PH}_FjcU$qZx;+E)_DxfD4H{rC=7KP0C9Jzc%G%Xe4UY4xe-sm9Lu zmf#5OCjXZ6*~OOowjv=RaVL=tt0k9*RlZF(OFw5ZyN`)D>oJRtxf;af8=>{CS&E%c z>tzj^jfs6bCmnko1U;EA;TVjaaGqe?pE7W1u5RA6ZsuI?8R40F^*Sg^qWr65sCg)N zoL5{(Tmu6?V~g&zE<}s0!pnxyg3PjMxPJUPH9X{DUvB?;8spcAZ*Hl!p?kXgs{9di zFB^os3w__;;orF3*keUti3WQG<46g@p2M7A2DF}e4tcf;lPRo&27@~(-+2Aq936Vx zc-M~eit4QEcUULmF)#@n@JY2nNNKE{4vHywp4eF2>HxyfwVB zM#HzPPFGI34Kxfy%OuOt>2`*^3xo?Q3l@HSeg%H6ex^Upe-zz* zM__?}3EzO=MMR8=pB=}Fze!k2Q%?1s^zjol;!}aC=URlb#0bGi?{={~aSJJWv2C#s zjl;_4j1@Z`cEkt$#0>~XjRb{y;8>B>@l~>aApXp;tu+R-EZZa-jx-H3>15rj7^w&~ zu{Nb$_F4Y0e7(ZBGTFHRR+3+&u719ZYl>Hd2P3#ybRRWj12AqePiRiz}yF7eA>u^NA0| z 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%0Dcc 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%0Ac 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½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 PWw<}wXjKOEdhw#Ea5Bm5 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 diff --git a/docs/assets/groups-scope.png b/docs/assets/groups-scope.png deleted file mode 100644 index 45557b51ead7f69512a3908424bc419ef2691201..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59680 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 E0mHsFDgXcg diff --git a/docs/assets/identity-center-1.png b/docs/assets/identity-center-1.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd49528d90f7159b7f89a9166e68c3d2416dfbc GIT binary patch literal 108361 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